Browse Source

Implement rotate animation for shared element transition (#6144)

This commit adds support for animating rotate/rotationZ animation during shared element transition.

It also...
* Removes ClipBoundsAnimator - Not sure why I reintroduced it
* Sets pivotX and pivotY only if needed
* Removes "Preview" button in Navigation tab from Android
* Delete ClipBoundsAnimator
Guy Carmeli 4 years ago
parent
commit
03dd211a54
No account linked to committer's email address

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/element/SharedElementTransition.kt View File

@@ -31,9 +31,9 @@ class SharedElementTransition(appearing: ViewController<*>, private val options:
31 31
     private fun animators(): List<PropertyAnimatorCreator<*>> {
32 32
         return listOf(
33 33
                 MatrixAnimator(from, to),
34
-                ClipBoundsAnimator(from, to),
35 34
                 XAnimator(from, to),
36 35
                 YAnimator(from, to),
36
+                RotationAnimator(from, to),
37 37
                 ScaleXAnimator(from, to),
38 38
                 ScaleYAnimator(from, to),
39 39
                 BackgroundColorAnimator(from, to),

+ 4
- 4
lib/android/app/src/main/java/com/reactnativenavigation/views/element/TransitionAnimatorCreator.kt View File

@@ -8,6 +8,7 @@ import android.view.ViewGroup
8 8
 import android.widget.FrameLayout
9 9
 import androidx.core.animation.doOnCancel
10 10
 import androidx.core.animation.doOnEnd
11
+import androidx.core.view.marginLeft
11 12
 import com.facebook.react.uimanager.ViewGroupManager
12 13
 import com.facebook.react.views.image.ReactImageView
13 14
 import com.reactnativenavigation.R
@@ -101,19 +102,18 @@ open class TransitionAnimatorCreator {
101 102
 
102 103
     private fun reparent(transition: Transition) {
103 104
         with(transition) {
105
+            val loc = ViewUtils.getLocationOnScreen(view)
104 106
             val biologicalParent = view.parent as ViewGroup
105 107
             view.setTag(R.id.original_parent, biologicalParent)
106 108
             view.setTag(R.id.original_layout_params, view.layoutParams)
107
-            view.setTag(R.id.original_top, view.top)
109
+            view.setTag(R.id.original_top, loc.y - transition.topInset)
108 110
             view.setTag(R.id.original_bottom, view.bottom)
109 111
             view.setTag(R.id.original_right, view.right)
110
-            view.setTag(R.id.original_left, view.left)
112
+            view.setTag(R.id.original_left, loc.x)
111 113
 
112
-            val loc = ViewUtils.getLocationOnScreen(view)
113 114
             biologicalParent.removeView(view)
114 115
 
115 116
             val lp = FrameLayout.LayoutParams(view.layoutParams)
116
-            lp.topMargin = loc.y + viewController.topInset
117 117
             lp.topMargin = loc.y
118 118
             lp.leftMargin = loc.x
119 119
             if (view !is ReactImageView) {

lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ClipBoundsAnimator.kt → lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/RotationAnimator.kt View File

@@ -2,31 +2,29 @@ package com.reactnativenavigation.views.element.animators
2 2
 
3 3
 import android.animation.Animator
4 4
 import android.animation.ObjectAnimator
5
-import android.graphics.Rect
6 5
 import android.view.View
7 6
 import com.facebook.react.views.image.ReactImageView
8 7
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
9
-import com.reactnativenavigation.utils.ViewUtils
10 8
 import com.reactnativenavigation.utils.withDuration
11 9
 import com.reactnativenavigation.utils.withInterpolator
12 10
 import com.reactnativenavigation.utils.withStartDelay
13 11
 
14
-class ClipBoundsAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactImageView>(from, to) {
12
+class RotationAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactImageView>(from, to) {
13
+    private val fromRotation = from.rotation
14
+    private val toRotation = to.rotation
15
+
15 16
     override fun shouldAnimateProperty(fromChild: ReactImageView, toChild: ReactImageView): Boolean {
16
-        return !ViewUtils.areDimensionsEqual(from, to)
17
+        return fromRotation != toRotation
17 18
     }
18 19
 
19 20
     override fun create(options: SharedElementTransitionOptions): Animator {
20
-        val startDrawingRect = Rect(); from.getDrawingRect(startDrawingRect)
21
-        val endDrawingRect = Rect(); to.getDrawingRect(endDrawingRect)
22
-        return ObjectAnimator.ofObject(
23
-                ClipBoundsEvaluator(),
24
-                startDrawingRect,
25
-                endDrawingRect
26
-        )
21
+        to.rotation = fromRotation
22
+        to.pivotX = 0f
23
+        to.pivotY = 0f
24
+        return ObjectAnimator
25
+                .ofFloat(to, View.ROTATION, fromRotation, toRotation)
27 26
                 .withDuration(options.getDuration())
28 27
                 .withStartDelay(options.getStartDelay())
29 28
                 .withInterpolator(options.interpolation.interpolator)
30 29
     }
31
-
32 30
 }

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/XAnimator.kt View File

@@ -7,6 +7,7 @@ import android.view.View
7 7
 import android.view.View.TRANSLATION_X
8 8
 import androidx.core.animation.addListener
9 9
 import androidx.core.animation.doOnStart
10
+import com.facebook.react.views.image.ReactImageView
10 11
 import com.facebook.react.views.text.ReactTextView
11 12
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
12 13
 import com.reactnativenavigation.utils.ViewUtils
@@ -21,7 +22,6 @@ class XAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to)
21 22
         val fromXy = ViewUtils.getLocationOnScreen(from)
22 23
         val toXy = ViewUtils.getLocationOnScreen(to)
23 24
         dx = fromXy.x - toXy.x
24
-        to.pivotX = 0f
25 25
     }
26 26
 
27 27
     override fun excludedViews() = listOf(ReactTextView::class.java)
@@ -30,6 +30,7 @@ class XAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to)
30 30
 
31 31
     override fun create(options: SharedElementTransitionOptions): Animator {
32 32
         to.translationX = dx.toFloat()
33
+        to.pivotX = 0f
33 34
         return ObjectAnimator
34 35
                 .ofFloat(to, TRANSLATION_X, dx.toFloat(), 0f)
35 36
                 .withDuration(options.getDuration())

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/YAnimator.kt View File

@@ -20,7 +20,6 @@ class YAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to)
20 20
         val fromXy = ViewUtils.getLocationOnScreen(from)
21 21
         val toY = (to.layoutParams as ViewGroup.MarginLayoutParams).topMargin
22 22
         dy = fromXy.y - toY
23
-        to.pivotY = 0f
24 23
     }
25 24
 
26 25
     override fun shouldAnimateProperty(fromChild: View, toChild: View) = dy != 0
@@ -29,6 +28,7 @@ class YAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to)
29 28
 
30 29
     override fun create(options: SharedElementTransitionOptions): Animator {
31 30
         to.translationY = dy.toFloat()
31
+        to.pivotY = 0f
32 32
         return ObjectAnimator
33 33
                 .ofFloat(to, TRANSLATION_Y, dy.toFloat(), 0f)
34 34
                 .withDuration(options.getDuration())

+ 4
- 3
playground/src/screens/NavigationScreen.js View File

@@ -1,4 +1,5 @@
1 1
 const React = require('react');
2
+const { Platform } = require('react-native');
2 3
 const Root = require('../components/Root');
3 4
 const Button = require('../components/Button')
4 5
 const Navigation = require('./../services/Navigation');
@@ -42,11 +43,11 @@ class NavigationScreen extends React.Component {
42 43
         <Button label='Static Events' testID={SHOW_STATIC_EVENTS_SCREEN} onPress={this.pushStaticEventsScreen} />
43 44
         <Button label='Orientation' testID={SHOW_ORIENTATION_SCREEN} onPress={this.orientation} />
44 45
         <Button label='React Context API' onPress={this.pushContextScreen} />
45
-        {<Button label='Shared Element' onPress={this.sharedElement} />}
46
-        <Navigation.TouchablePreview
46
+        <Button label='Shared Element' onPress={this.sharedElement} />
47
+        {Platform.OS === 'ios' && <Navigation.TouchablePreview
47 48
           touchableComponent={Button}
48 49
           onPressIn={this.preview}
49
-          label='Preview' />
50
+          label='Preview' />}
50 51
       </Root>
51 52
     );
52 53
   }

+ 3
- 0
playground/src/screens/sharedElementTransition/CocktailDetailsScreen.js View File

@@ -100,6 +100,9 @@ const styles = StyleSheet.create({
100 100
     height: SIZE,
101 101
     width: SIZE,
102 102
     zIndex: 1,
103
+    // transform: [
104
+    //   { rotate: '45deg' }
105
+    // ],
103 106
     marginLeft: 24,
104 107
     marginBottom: -24
105 108
   }