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
     private fun animators(): List<PropertyAnimatorCreator<*>> {
31
     private fun animators(): List<PropertyAnimatorCreator<*>> {
32
         return listOf(
32
         return listOf(
33
                 MatrixAnimator(from, to),
33
                 MatrixAnimator(from, to),
34
-                ClipBoundsAnimator(from, to),
35
                 XAnimator(from, to),
34
                 XAnimator(from, to),
36
                 YAnimator(from, to),
35
                 YAnimator(from, to),
36
+                RotationAnimator(from, to),
37
                 ScaleXAnimator(from, to),
37
                 ScaleXAnimator(from, to),
38
                 ScaleYAnimator(from, to),
38
                 ScaleYAnimator(from, to),
39
                 BackgroundColorAnimator(from, to),
39
                 BackgroundColorAnimator(from, to),

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

8
 import android.widget.FrameLayout
8
 import android.widget.FrameLayout
9
 import androidx.core.animation.doOnCancel
9
 import androidx.core.animation.doOnCancel
10
 import androidx.core.animation.doOnEnd
10
 import androidx.core.animation.doOnEnd
11
+import androidx.core.view.marginLeft
11
 import com.facebook.react.uimanager.ViewGroupManager
12
 import com.facebook.react.uimanager.ViewGroupManager
12
 import com.facebook.react.views.image.ReactImageView
13
 import com.facebook.react.views.image.ReactImageView
13
 import com.reactnativenavigation.R
14
 import com.reactnativenavigation.R
101
 
102
 
102
     private fun reparent(transition: Transition) {
103
     private fun reparent(transition: Transition) {
103
         with(transition) {
104
         with(transition) {
105
+            val loc = ViewUtils.getLocationOnScreen(view)
104
             val biologicalParent = view.parent as ViewGroup
106
             val biologicalParent = view.parent as ViewGroup
105
             view.setTag(R.id.original_parent, biologicalParent)
107
             view.setTag(R.id.original_parent, biologicalParent)
106
             view.setTag(R.id.original_layout_params, view.layoutParams)
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
             view.setTag(R.id.original_bottom, view.bottom)
110
             view.setTag(R.id.original_bottom, view.bottom)
109
             view.setTag(R.id.original_right, view.right)
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
             biologicalParent.removeView(view)
114
             biologicalParent.removeView(view)
114
 
115
 
115
             val lp = FrameLayout.LayoutParams(view.layoutParams)
116
             val lp = FrameLayout.LayoutParams(view.layoutParams)
116
-            lp.topMargin = loc.y + viewController.topInset
117
             lp.topMargin = loc.y
117
             lp.topMargin = loc.y
118
             lp.leftMargin = loc.x
118
             lp.leftMargin = loc.x
119
             if (view !is ReactImageView) {
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
 
2
 
3
 import android.animation.Animator
3
 import android.animation.Animator
4
 import android.animation.ObjectAnimator
4
 import android.animation.ObjectAnimator
5
-import android.graphics.Rect
6
 import android.view.View
5
 import android.view.View
7
 import com.facebook.react.views.image.ReactImageView
6
 import com.facebook.react.views.image.ReactImageView
8
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
7
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
9
-import com.reactnativenavigation.utils.ViewUtils
10
 import com.reactnativenavigation.utils.withDuration
8
 import com.reactnativenavigation.utils.withDuration
11
 import com.reactnativenavigation.utils.withInterpolator
9
 import com.reactnativenavigation.utils.withInterpolator
12
 import com.reactnativenavigation.utils.withStartDelay
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
     override fun shouldAnimateProperty(fromChild: ReactImageView, toChild: ReactImageView): Boolean {
16
     override fun shouldAnimateProperty(fromChild: ReactImageView, toChild: ReactImageView): Boolean {
16
-        return !ViewUtils.areDimensionsEqual(from, to)
17
+        return fromRotation != toRotation
17
     }
18
     }
18
 
19
 
19
     override fun create(options: SharedElementTransitionOptions): Animator {
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
                 .withDuration(options.getDuration())
26
                 .withDuration(options.getDuration())
28
                 .withStartDelay(options.getStartDelay())
27
                 .withStartDelay(options.getStartDelay())
29
                 .withInterpolator(options.interpolation.interpolator)
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
 import android.view.View.TRANSLATION_X
7
 import android.view.View.TRANSLATION_X
8
 import androidx.core.animation.addListener
8
 import androidx.core.animation.addListener
9
 import androidx.core.animation.doOnStart
9
 import androidx.core.animation.doOnStart
10
+import com.facebook.react.views.image.ReactImageView
10
 import com.facebook.react.views.text.ReactTextView
11
 import com.facebook.react.views.text.ReactTextView
11
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
12
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
12
 import com.reactnativenavigation.utils.ViewUtils
13
 import com.reactnativenavigation.utils.ViewUtils
21
         val fromXy = ViewUtils.getLocationOnScreen(from)
22
         val fromXy = ViewUtils.getLocationOnScreen(from)
22
         val toXy = ViewUtils.getLocationOnScreen(to)
23
         val toXy = ViewUtils.getLocationOnScreen(to)
23
         dx = fromXy.x - toXy.x
24
         dx = fromXy.x - toXy.x
24
-        to.pivotX = 0f
25
     }
25
     }
26
 
26
 
27
     override fun excludedViews() = listOf(ReactTextView::class.java)
27
     override fun excludedViews() = listOf(ReactTextView::class.java)
30
 
30
 
31
     override fun create(options: SharedElementTransitionOptions): Animator {
31
     override fun create(options: SharedElementTransitionOptions): Animator {
32
         to.translationX = dx.toFloat()
32
         to.translationX = dx.toFloat()
33
+        to.pivotX = 0f
33
         return ObjectAnimator
34
         return ObjectAnimator
34
                 .ofFloat(to, TRANSLATION_X, dx.toFloat(), 0f)
35
                 .ofFloat(to, TRANSLATION_X, dx.toFloat(), 0f)
35
                 .withDuration(options.getDuration())
36
                 .withDuration(options.getDuration())

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

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

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

1
 const React = require('react');
1
 const React = require('react');
2
+const { Platform } = require('react-native');
2
 const Root = require('../components/Root');
3
 const Root = require('../components/Root');
3
 const Button = require('../components/Button')
4
 const Button = require('../components/Button')
4
 const Navigation = require('./../services/Navigation');
5
 const Navigation = require('./../services/Navigation');
42
         <Button label='Static Events' testID={SHOW_STATIC_EVENTS_SCREEN} onPress={this.pushStaticEventsScreen} />
43
         <Button label='Static Events' testID={SHOW_STATIC_EVENTS_SCREEN} onPress={this.pushStaticEventsScreen} />
43
         <Button label='Orientation' testID={SHOW_ORIENTATION_SCREEN} onPress={this.orientation} />
44
         <Button label='Orientation' testID={SHOW_ORIENTATION_SCREEN} onPress={this.orientation} />
44
         <Button label='React Context API' onPress={this.pushContextScreen} />
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
           touchableComponent={Button}
48
           touchableComponent={Button}
48
           onPressIn={this.preview}
49
           onPressIn={this.preview}
49
-          label='Preview' />
50
+          label='Preview' />}
50
       </Root>
51
       </Root>
51
     );
52
     );
52
   }
53
   }

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

100
     height: SIZE,
100
     height: SIZE,
101
     width: SIZE,
101
     width: SIZE,
102
     zIndex: 1,
102
     zIndex: 1,
103
+    // transform: [
104
+    //   { rotate: '45deg' }
105
+    // ],
103
     marginLeft: 24,
106
     marginLeft: 24,
104
     marginBottom: -24
107
     marginBottom: -24
105
   }
108
   }