react-native-morph-card 0.2.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/android/src/main/java/com/melivalesca/morphcard/MorphCardSourceManager.kt +8 -0
- package/android/src/main/java/com/melivalesca/morphcard/MorphCardSourceView.kt +49 -3
- package/android/src/main/java/com/melivalesca/morphcard/MorphCardTargetView.kt +41 -1
- package/ios/Fabric/RNCMorphCardSourceComponentView.mm +111 -10
- package/ios/Fabric/RNCMorphCardTargetComponentView.h +1 -0
- package/ios/Fabric/RNCMorphCardTargetComponentView.mm +21 -0
- package/lib/commonjs/MorphCardSource.js +29 -2
- package/lib/commonjs/MorphCardSource.js.map +1 -1
- package/lib/commonjs/MorphCardTarget.js +31 -7
- package/lib/commonjs/MorphCardTarget.js.map +1 -1
- package/lib/commonjs/MorphChildrenRegistry.js +34 -0
- package/lib/commonjs/MorphChildrenRegistry.js.map +1 -0
- package/lib/commonjs/specs/NativeMorphCardSource.js.map +1 -1
- package/lib/module/MorphCardSource.js +29 -2
- package/lib/module/MorphCardSource.js.map +1 -1
- package/lib/module/MorphCardTarget.js +31 -7
- package/lib/module/MorphCardTarget.js.map +1 -1
- package/lib/module/MorphChildrenRegistry.js +27 -0
- package/lib/module/MorphChildrenRegistry.js.map +1 -0
- package/lib/module/specs/NativeMorphCardSource.js.map +1 -1
- package/lib/typescript/src/MorphCardSource.d.ts +8 -4
- package/lib/typescript/src/MorphCardSource.d.ts.map +1 -1
- package/lib/typescript/src/MorphCardTarget.d.ts.map +1 -1
- package/lib/typescript/src/MorphChildrenRegistry.d.ts +13 -0
- package/lib/typescript/src/MorphChildrenRegistry.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativeMorphCardSource.d.ts +2 -0
- package/lib/typescript/src/specs/NativeMorphCardSource.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/MorphCardSource.tsx +33 -5
- package/src/MorphCardTarget.tsx +44 -9
- package/src/MorphChildrenRegistry.ts +43 -0
- package/src/index.tsx +1 -1
- package/src/specs/NativeMorphCardSource.ts +2 -0
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ No additional steps required.
|
|
|
46
46
|
|
|
47
47
|
Wrap your card content in `MorphCardSource`. On the detail screen, use `MorphCardTarget` where the card should land. Use `useMorphTarget` for easy collapse handling.
|
|
48
48
|
|
|
49
|
-
> **Important:** `MorphCardSource` can wrap any React Native component (images, views, text, etc.)
|
|
49
|
+
> **Important:** `MorphCardSource` can wrap any React Native component (images, views, text, etc.). During the animation, the content is captured as a **bitmap snapshot** — but once the animation completes, the snapshot fades out and the source's children are automatically cloned into `MorphCardTarget` as live React components. The cloned children are rendered at the source card's original layout dimensions, so your component layout stays consistent. This means observable values (timers, animated values, live data) will update in real time after the transition finishes. If you use `resizeMode`, the bitmap is kept instead (native image scaling doesn't apply to React components).
|
|
50
50
|
|
|
51
51
|
```tsx
|
|
52
52
|
import React from 'react';
|
|
@@ -110,12 +110,14 @@ Wraps the card content on the list/grid screen. Captures a snapshot and drives t
|
|
|
110
110
|
| `backgroundColor` | `string` | — | Background color (enables "wrapper mode" where the background expands separately from the content) |
|
|
111
111
|
| `duration` | `number` | `300` | Default animation duration in ms (used for both expand and collapse if specific durations are not set) |
|
|
112
112
|
| `expandDuration` | `number` | — | Duration of the expand animation in ms. Overrides `duration` for expand. |
|
|
113
|
-
| `
|
|
113
|
+
| `resizeMode` | `'cover' \| 'contain' \| 'stretch'` | `'cover'` | How the snapshot scales during animation. When set, the bitmap is kept after expand (no live children). **Recommended when wrapping an `<Image>` — without it, the image may not scale properly during the animation.** |
|
|
114
|
+
| `rotations` | `number` | `0` | Number of full 360° rotations during the expand animation |
|
|
115
|
+
| `rotationEndAngle`| `number` | `0` | Final rotation angle in degrees after expand (e.g. `45` to end tilted). Collapse reverses it back to 0 |
|
|
114
116
|
| `onPress` | `(sourceTag: number) => void` | — | Called on tap with the native view tag. Use this to navigate to the detail screen. |
|
|
115
117
|
|
|
116
118
|
### `<MorphCardTarget>`
|
|
117
119
|
|
|
118
|
-
Placed on the detail screen where the card should land. Triggers the expand animation on mount.
|
|
120
|
+
Placed on the detail screen where the card should land. Triggers the expand animation on mount. After the animation, the source's children are automatically cloned here as live React components.
|
|
119
121
|
|
|
120
122
|
| Prop | Type | Default | Description |
|
|
121
123
|
| ------------------ | ---------------- | ------------- | -------------------------------------------------------------------------------- |
|
|
@@ -161,7 +163,7 @@ await morphCollapse(sourceTag);
|
|
|
161
163
|
const tag = getViewTag(viewRef);
|
|
162
164
|
```
|
|
163
165
|
|
|
164
|
-
|
|
166
|
+
```
|
|
165
167
|
|
|
166
168
|
## Running the example app
|
|
167
169
|
|
|
@@ -38,6 +38,14 @@ class MorphCardSourceManager :
|
|
|
38
38
|
view.borderRadiusDp = value.toFloat()
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
override fun setRotations(view: MorphCardSourceView, value: Double) {
|
|
42
|
+
view.rotations = value
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
override fun setRotationEndAngle(view: MorphCardSourceView, value: Double) {
|
|
46
|
+
view.rotationEndAngle = value
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
companion object {
|
|
42
50
|
const val REACT_CLASS = "RNCMorphCardSource"
|
|
43
51
|
}
|
|
@@ -33,6 +33,8 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
33
33
|
var expandDuration: Double = 0.0
|
|
34
34
|
var scaleMode: String = "aspectFill"
|
|
35
35
|
var borderRadiusDp: Float = 0f
|
|
36
|
+
var rotations: Double = 0.0
|
|
37
|
+
var rotationEndAngle: Double = 0.0
|
|
36
38
|
|
|
37
39
|
// ── Target config (set by module, in dp from JS) ──
|
|
38
40
|
var pendingTargetWidth: Float = 0f
|
|
@@ -527,6 +529,8 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
527
529
|
imageFrameForScaleMode(scaleMode, cardWidth, cardHeight, targetWidthPx, targetHeightPx)
|
|
528
530
|
} else null
|
|
529
531
|
|
|
532
|
+
val totalAngle = (rotations * 360.0 + rotationEndAngle).toFloat()
|
|
533
|
+
|
|
530
534
|
val animator = ValueAnimator.ofFloat(0f, 1f)
|
|
531
535
|
animator.duration = dur
|
|
532
536
|
animator.interpolator = springInterpolator
|
|
@@ -541,6 +545,7 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
541
545
|
lp.height = lerp(cardHeight, targetHeightPx, t).toInt()
|
|
542
546
|
cardWrapper.layoutParams = lp
|
|
543
547
|
setRoundedCorners(cardWrapper, lerp(cardCornerRadiusPx, targetCornerRadiusPx, t))
|
|
548
|
+
cardWrapper.rotation = lerp(0f, totalAngle, t)
|
|
544
549
|
|
|
545
550
|
if (content != null) {
|
|
546
551
|
if (hasWrapper) {
|
|
@@ -591,6 +596,11 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
591
596
|
}
|
|
592
597
|
this@MorphCardSourceView.alpha = 1f
|
|
593
598
|
|
|
599
|
+
// Apply end rotation to target view
|
|
600
|
+
if (rotationEndAngle != 0.0) {
|
|
601
|
+
targetView?.rotation = rotationEndAngle.toFloat()
|
|
602
|
+
}
|
|
603
|
+
|
|
594
604
|
transferSnapshotToTarget(decorView, wrapper, targetView,
|
|
595
605
|
targetWidthPx, targetHeightPx, targetCornerRadiusPx, 200L)
|
|
596
606
|
|
|
@@ -655,6 +665,8 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
655
665
|
|
|
656
666
|
target.showSnapshot(bitmap, frame, cornerRadius, null)
|
|
657
667
|
Log.d(TAG, "transferSnapshot: handed snapshot to MorphCardTargetView")
|
|
668
|
+
// Crossfade snapshot out to reveal live React children underneath
|
|
669
|
+
mainHandler.postDelayed({ target.fadeOutSnapshot() }, 50)
|
|
658
670
|
}
|
|
659
671
|
|
|
660
672
|
val fadeOut = ValueAnimator.ofFloat(1f, 0f)
|
|
@@ -716,6 +728,7 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
716
728
|
var wrapper = overlayContainer
|
|
717
729
|
if (wrapper == null) {
|
|
718
730
|
val target = targetView as? MorphCardTargetView
|
|
731
|
+
// Read position BEFORE clearing rotation — visual position depends on rotation
|
|
719
732
|
val targetLoc = if (targetView != null) getLocationInWindow(targetView) else intArrayOf(cardLeft.toInt(), cardTop.toInt())
|
|
720
733
|
val twPx = if (pendingTargetWidth > 0) pendingTargetWidth * d else cardWidth
|
|
721
734
|
val thPx = if (pendingTargetHeight > 0) pendingTargetHeight * d else cardHeight
|
|
@@ -726,11 +739,15 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
726
739
|
val cardImage = captureSnapshot()
|
|
727
740
|
alpha = 0f
|
|
728
741
|
|
|
729
|
-
// Clear the snapshot from the target
|
|
742
|
+
// Clear the snapshot from the target
|
|
730
743
|
target?.clearSnapshot()
|
|
731
744
|
|
|
745
|
+
// Use target view's actual size if available for exact match
|
|
746
|
+
val wrapW = if (targetView != null && targetView.width > 0) targetView.width.toFloat() else twPx
|
|
747
|
+
val wrapH = if (targetView != null && targetView.height > 0) targetView.height.toFloat() else thPx
|
|
748
|
+
|
|
732
749
|
wrapper = FrameLayout(context)
|
|
733
|
-
wrapper.layoutParams = FrameLayout.LayoutParams(
|
|
750
|
+
wrapper.layoutParams = FrameLayout.LayoutParams(wrapW.toInt(), wrapH.toInt())
|
|
734
751
|
wrapper.x = targetLoc[0].toFloat()
|
|
735
752
|
wrapper.y = targetLoc[1].toFloat()
|
|
736
753
|
wrapper.clipChildren = true
|
|
@@ -760,8 +777,23 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
760
777
|
}
|
|
761
778
|
|
|
762
779
|
wrapper.addView(content)
|
|
780
|
+
wrapper.translationZ = 1000f
|
|
781
|
+
|
|
782
|
+
// Match the target's exact rotation center by reading its actual size
|
|
783
|
+
val actualW = targetView?.width?.toFloat() ?: wrapW
|
|
784
|
+
val actualH = targetView?.height?.toFloat() ?: wrapH
|
|
785
|
+
// Clear target rotation first so getLocationInWindow gives un-rotated pos
|
|
786
|
+
// then position wrapper at same un-rotated pos with same rotation
|
|
787
|
+
targetView?.rotation = 0f
|
|
788
|
+
val unrotatedLoc = if (targetView != null) getLocationInWindow(targetView) else targetLoc
|
|
789
|
+
wrapper.x = unrotatedLoc[0].toFloat()
|
|
790
|
+
wrapper.y = unrotatedLoc[1].toFloat()
|
|
791
|
+
wrapper.layoutParams = FrameLayout.LayoutParams(actualW.toInt(), actualH.toInt())
|
|
792
|
+
wrapper.rotation = rotationEndAngle.toFloat()
|
|
793
|
+
|
|
763
794
|
decorView.addView(wrapper)
|
|
764
795
|
overlayContainer = wrapper
|
|
796
|
+
targetView?.visibility = View.INVISIBLE
|
|
765
797
|
}
|
|
766
798
|
|
|
767
799
|
// Show source screen underneath
|
|
@@ -782,9 +814,18 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
782
814
|
val startImgW = content?.layoutParams?.width?.toFloat() ?: cardWidth
|
|
783
815
|
val startImgH = content?.layoutParams?.height?.toFloat() ?: cardHeight
|
|
784
816
|
|
|
817
|
+
// Lock pivot to center so rotation stays stable as size changes
|
|
818
|
+
if (rotationEndAngle != 0.0) {
|
|
819
|
+
wrapper.pivotX = startWidth / 2f
|
|
820
|
+
wrapper.pivotY = startHeight / 2f
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Match iOS collapse curve: cubic bezier (0.25, 1.0, 0.5, 1.0)
|
|
824
|
+
val collapseInterpolator = PathInterpolator(0.25f, 1.0f, 0.5f, 1.0f)
|
|
825
|
+
|
|
785
826
|
val animator = ValueAnimator.ofFloat(0f, 1f)
|
|
786
827
|
animator.duration = dur
|
|
787
|
-
animator.interpolator =
|
|
828
|
+
animator.interpolator = collapseInterpolator
|
|
788
829
|
|
|
789
830
|
animator.addUpdateListener { anim ->
|
|
790
831
|
val t = anim.animatedValue as Float
|
|
@@ -795,6 +836,11 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
795
836
|
lp.height = lerp(startHeight, cardHeight, t).toInt()
|
|
796
837
|
wrapper.layoutParams = lp
|
|
797
838
|
setRoundedCorners(wrapper, lerp(startCr, cardCornerRadiusPx, t))
|
|
839
|
+
if (rotationEndAngle != 0.0) {
|
|
840
|
+
wrapper.rotation = lerp(rotationEndAngle.toFloat(), 0f, t)
|
|
841
|
+
wrapper.pivotX = lp.width / 2f
|
|
842
|
+
wrapper.pivotY = lp.height / 2f
|
|
843
|
+
}
|
|
798
844
|
|
|
799
845
|
if (content != null) {
|
|
800
846
|
if (hasWrapper) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.melivalesca.morphcard
|
|
2
2
|
|
|
3
|
+
import android.animation.ValueAnimator
|
|
3
4
|
import android.content.Context
|
|
4
5
|
import android.graphics.Bitmap
|
|
5
6
|
import android.graphics.Canvas
|
|
@@ -21,6 +22,7 @@ class MorphCardTargetView(context: Context) : ReactViewGroup(context) {
|
|
|
21
22
|
private var snapshotFrame: RectF? = null
|
|
22
23
|
private var snapshotCornerRadius: Float = 0f
|
|
23
24
|
private var snapshotBgColor: Int? = null
|
|
25
|
+
private var snapshotAlpha: Float = 1f
|
|
24
26
|
private val snapshotPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
|
|
25
27
|
private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
26
28
|
|
|
@@ -38,6 +40,8 @@ class MorphCardTargetView(context: Context) : ReactViewGroup(context) {
|
|
|
38
40
|
screenContainer.visibility = View.INVISIBLE
|
|
39
41
|
Log.d(TAG, "TargetView: set screen INVISIBLE")
|
|
40
42
|
}
|
|
43
|
+
// Hide children until snapshot is in place to avoid flash of un-rotated content
|
|
44
|
+
hideChildren()
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
|
|
@@ -58,7 +62,9 @@ class MorphCardTargetView(context: Context) : ReactViewGroup(context) {
|
|
|
58
62
|
override fun dispatchDraw(canvas: Canvas) {
|
|
59
63
|
val bmp = snapshotBitmap
|
|
60
64
|
val frame = snapshotFrame
|
|
61
|
-
if (bmp != null && frame != null) {
|
|
65
|
+
if (bmp != null && frame != null && snapshotAlpha > 0f) {
|
|
66
|
+
snapshotPaint.alpha = (snapshotAlpha * 255).toInt()
|
|
67
|
+
bgPaint.alpha = (snapshotAlpha * 255).toInt()
|
|
62
68
|
val radiusPx = snapshotCornerRadius
|
|
63
69
|
|
|
64
70
|
// Clip to rounded rect if needed
|
|
@@ -114,6 +120,40 @@ class MorphCardTargetView(context: Context) : ReactViewGroup(context) {
|
|
|
114
120
|
invalidate()
|
|
115
121
|
}
|
|
116
122
|
|
|
123
|
+
fun showChildren() {
|
|
124
|
+
for (i in 0 until childCount) {
|
|
125
|
+
getChildAt(i).alpha = 1f
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fun hideChildren() {
|
|
130
|
+
for (i in 0 until childCount) {
|
|
131
|
+
getChildAt(i).alpha = 0f
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
fun fadeOutSnapshot() {
|
|
136
|
+
if (snapshotBitmap == null) return
|
|
137
|
+
// Only fade out if there are React children underneath to reveal.
|
|
138
|
+
// If no children (scaleMode bitmap-only), keep the snapshot.
|
|
139
|
+
if (childCount == 0) return
|
|
140
|
+
val anim = ValueAnimator.ofFloat(1f, 0f)
|
|
141
|
+
anim.duration = 150
|
|
142
|
+
anim.addUpdateListener {
|
|
143
|
+
snapshotAlpha = it.animatedValue as Float
|
|
144
|
+
invalidate()
|
|
145
|
+
}
|
|
146
|
+
// Show children before snapshot fades so they're visible underneath
|
|
147
|
+
showChildren()
|
|
148
|
+
anim.addListener(object : android.animation.AnimatorListenerAdapter() {
|
|
149
|
+
override fun onAnimationEnd(animation: android.animation.Animator) {
|
|
150
|
+
clearSnapshot()
|
|
151
|
+
snapshotAlpha = 1f
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
anim.start()
|
|
155
|
+
}
|
|
156
|
+
|
|
117
157
|
fun clearSnapshot() {
|
|
118
158
|
if (snapshotBitmap != null) {
|
|
119
159
|
Log.d(TAG, "clearSnapshot: clearing bitmap")
|
|
@@ -92,6 +92,8 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
92
92
|
__weak UIView *_targetView;
|
|
93
93
|
__weak UIView *_sourceScreenContainer;
|
|
94
94
|
__weak UIView *_targetScreenContainer;
|
|
95
|
+
CGFloat _rotations;
|
|
96
|
+
CGFloat _rotationEndAngle;
|
|
95
97
|
UIView *_wrapperView;
|
|
96
98
|
UIImageView *_snapshot;
|
|
97
99
|
}
|
|
@@ -124,6 +126,8 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
124
126
|
} else {
|
|
125
127
|
_scaleMode = UIViewContentModeScaleAspectFill;
|
|
126
128
|
}
|
|
129
|
+
_rotations = newProps.rotations;
|
|
130
|
+
_rotationEndAngle = newProps.rotationEndAngle;
|
|
127
131
|
[super updateProps:props oldProps:oldProps];
|
|
128
132
|
}
|
|
129
133
|
|
|
@@ -293,17 +297,37 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
293
297
|
? (targetFrame.size.height - contentSize.height) / 2.0
|
|
294
298
|
: contentOffsetY;
|
|
295
299
|
|
|
300
|
+
// Use bounds/center instead of frame so rotation transforms work correctly
|
|
301
|
+
CGFloat totalAngleDeg = _rotations * 360.0 + _rotationEndAngle;
|
|
302
|
+
CGFloat totalAngleRad = totalAngleDeg * M_PI / 180.0;
|
|
303
|
+
wrapper.bounds = CGRectMake(0, 0, _cardFrame.size.width, _cardFrame.size.height);
|
|
304
|
+
wrapper.center = CGPointMake(CGRectGetMidX(_cardFrame), CGRectGetMidY(_cardFrame));
|
|
305
|
+
|
|
296
306
|
UIViewPropertyAnimator *animator = [[UIViewPropertyAnimator alloc]
|
|
297
307
|
initWithDuration:dur
|
|
298
308
|
dampingRatio:0.85
|
|
299
309
|
animations:^{
|
|
300
|
-
wrapper.
|
|
310
|
+
wrapper.bounds = CGRectMake(0, 0, targetFrame.size.width, targetFrame.size.height);
|
|
311
|
+
wrapper.center = CGPointMake(CGRectGetMidX(targetFrame), CGRectGetMidY(targetFrame));
|
|
301
312
|
wrapper.layer.cornerRadius = targetCornerRadius;
|
|
302
313
|
content.frame = CGRectMake(targetCx, targetCy,
|
|
303
314
|
contentSize.width,
|
|
304
315
|
contentSize.height);
|
|
305
316
|
}];
|
|
306
317
|
|
|
318
|
+
// Use CABasicAnimation for rotation — UIViewPropertyAnimator takes the
|
|
319
|
+
// shortest path and can't do multiple full spins.
|
|
320
|
+
if (totalAngleRad != 0) {
|
|
321
|
+
CABasicAnimation *rotAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
|
|
322
|
+
rotAnim.fromValue = @(0);
|
|
323
|
+
rotAnim.toValue = @(totalAngleRad);
|
|
324
|
+
rotAnim.duration = dur;
|
|
325
|
+
rotAnim.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :1.0 :0.5 :1.0];
|
|
326
|
+
rotAnim.fillMode = kCAFillModeForwards;
|
|
327
|
+
rotAnim.removedOnCompletion = NO;
|
|
328
|
+
[wrapper.layer addAnimation:rotAnim forKey:@"morphRotation"];
|
|
329
|
+
}
|
|
330
|
+
|
|
307
331
|
// Hide the target view itself so it doesn't double-render over the morph overlay.
|
|
308
332
|
if (targetView) {
|
|
309
333
|
targetView.hidden = YES;
|
|
@@ -338,8 +362,20 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
338
362
|
cornerRadius:targetCornerRadius
|
|
339
363
|
backgroundColor:wrapperBg];
|
|
340
364
|
}
|
|
365
|
+
// Crossfade snapshot out to reveal live React children underneath
|
|
366
|
+
dispatch_after(
|
|
367
|
+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)),
|
|
368
|
+
dispatch_get_main_queue(), ^{
|
|
369
|
+
[target fadeOutSnapshot];
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
if (targetView) {
|
|
373
|
+
targetView.hidden = NO;
|
|
374
|
+
CGFloat endAngleRad = self->_rotationEndAngle * M_PI / 180.0;
|
|
375
|
+
if (endAngleRad != 0) {
|
|
376
|
+
targetView.transform = CGAffineTransformMakeRotation(endAngleRad);
|
|
377
|
+
}
|
|
341
378
|
}
|
|
342
|
-
if (targetView) { targetView.hidden = NO; }
|
|
343
379
|
self.alpha = 1;
|
|
344
380
|
UIView *ts = self->_targetScreenContainer;
|
|
345
381
|
if (ts) { ts.alpha = 1; }
|
|
@@ -357,7 +393,9 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
357
393
|
UIViewContentMode scaleMode = _scaleMode;
|
|
358
394
|
CGSize imageSize = cardImage.size;
|
|
359
395
|
|
|
360
|
-
UIView *container = [[UIView alloc] initWithFrame:
|
|
396
|
+
UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
|
|
397
|
+
container.bounds = CGRectMake(0, 0, _cardFrame.size.width, _cardFrame.size.height);
|
|
398
|
+
container.center = CGPointMake(CGRectGetMidX(_cardFrame), CGRectGetMidY(_cardFrame));
|
|
361
399
|
container.clipsToBounds = YES;
|
|
362
400
|
container.layer.cornerRadius = _cardCornerRadius;
|
|
363
401
|
|
|
@@ -382,15 +420,31 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
382
420
|
CGRect targetImageFrame = imageFrameForScaleMode(
|
|
383
421
|
scaleMode, imageSize, targetFrame.size);
|
|
384
422
|
|
|
423
|
+
CGFloat totalAngleDegNoWrap = _rotations * 360.0 + _rotationEndAngle;
|
|
424
|
+
CGFloat totalAngleRadNoWrap = totalAngleDegNoWrap * M_PI / 180.0;
|
|
425
|
+
|
|
385
426
|
UIViewPropertyAnimator *animator = [[UIViewPropertyAnimator alloc]
|
|
386
427
|
initWithDuration:dur
|
|
387
428
|
dampingRatio:0.85
|
|
388
429
|
animations:^{
|
|
389
|
-
container.
|
|
430
|
+
container.bounds = CGRectMake(0, 0, targetFrame.size.width, targetFrame.size.height);
|
|
431
|
+
container.center = CGPointMake(CGRectGetMidX(targetFrame), CGRectGetMidY(targetFrame));
|
|
390
432
|
container.layer.cornerRadius = targetCornerRadius;
|
|
391
433
|
snapshot.frame = targetImageFrame;
|
|
392
434
|
}];
|
|
393
435
|
|
|
436
|
+
// Use CABasicAnimation for rotation — supports multiple full spins
|
|
437
|
+
if (totalAngleRadNoWrap != 0) {
|
|
438
|
+
CABasicAnimation *rotAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
|
|
439
|
+
rotAnim.fromValue = @(0);
|
|
440
|
+
rotAnim.toValue = @(totalAngleRadNoWrap);
|
|
441
|
+
rotAnim.duration = dur;
|
|
442
|
+
rotAnim.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :1.0 :0.5 :1.0];
|
|
443
|
+
rotAnim.fillMode = kCAFillModeForwards;
|
|
444
|
+
rotAnim.removedOnCompletion = NO;
|
|
445
|
+
[container.layer addAnimation:rotAnim forKey:@"morphRotation"];
|
|
446
|
+
}
|
|
447
|
+
|
|
394
448
|
// Start fading in screen content early (at 15% of the animation).
|
|
395
449
|
__weak RNCMorphCardSourceComponentView *weakSelf2 = self;
|
|
396
450
|
dispatch_after(
|
|
@@ -414,8 +468,20 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
414
468
|
frame:target.bounds
|
|
415
469
|
cornerRadius:targetCornerRadius
|
|
416
470
|
backgroundColor:nil];
|
|
471
|
+
// Crossfade snapshot out to reveal live React children underneath
|
|
472
|
+
dispatch_after(
|
|
473
|
+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)),
|
|
474
|
+
dispatch_get_main_queue(), ^{
|
|
475
|
+
[target fadeOutSnapshot];
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
if (targetView) {
|
|
479
|
+
targetView.hidden = NO;
|
|
480
|
+
CGFloat endAngleRad = self->_rotationEndAngle * M_PI / 180.0;
|
|
481
|
+
if (endAngleRad != 0) {
|
|
482
|
+
targetView.transform = CGAffineTransformMakeRotation(endAngleRad);
|
|
483
|
+
}
|
|
417
484
|
}
|
|
418
|
-
if (targetView) { targetView.hidden = NO; }
|
|
419
485
|
self.alpha = 1;
|
|
420
486
|
UIView *ts = self->_targetScreenContainer;
|
|
421
487
|
if (ts) { ts.alpha = 1; }
|
|
@@ -447,9 +513,12 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
447
513
|
UIView *targetScreen = _targetScreenContainer;
|
|
448
514
|
UIView *sourceScreen = _sourceScreenContainer;
|
|
449
515
|
|
|
450
|
-
// Clear the snapshot
|
|
516
|
+
// Clear the snapshot and hide the target view so live children
|
|
517
|
+
// don't show behind the animating collapse overlay
|
|
451
518
|
if (targetView && [targetView isKindOfClass:[RNCMorphCardTargetComponentView class]]) {
|
|
452
519
|
[(RNCMorphCardTargetComponentView *)targetView clearSnapshot];
|
|
520
|
+
targetView.hidden = YES;
|
|
521
|
+
targetView.transform = CGAffineTransformIdentity;
|
|
453
522
|
}
|
|
454
523
|
|
|
455
524
|
CGFloat collapseDur = 0;
|
|
@@ -479,7 +548,9 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
479
548
|
? (targetFrame.size.height - contentSize.height) / 2.0
|
|
480
549
|
: contentOffsetY;
|
|
481
550
|
|
|
482
|
-
wrapper = [[UIView alloc] initWithFrame:
|
|
551
|
+
wrapper = [[UIView alloc] initWithFrame:CGRectZero];
|
|
552
|
+
wrapper.bounds = CGRectMake(0, 0, targetFrame.size.width, targetFrame.size.height);
|
|
553
|
+
wrapper.center = CGPointMake(CGRectGetMidX(targetFrame), CGRectGetMidY(targetFrame));
|
|
483
554
|
wrapper.backgroundColor = self.backgroundColor;
|
|
484
555
|
wrapper.layer.cornerRadius = targetCornerRadius;
|
|
485
556
|
wrapper.clipsToBounds = YES;
|
|
@@ -508,13 +579,27 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
508
579
|
initWithDuration:dur
|
|
509
580
|
timingParameters:timing];
|
|
510
581
|
[animator addAnimations:^{
|
|
511
|
-
wrapper.
|
|
582
|
+
wrapper.bounds = CGRectMake(0, 0, self->_cardFrame.size.width, self->_cardFrame.size.height);
|
|
583
|
+
wrapper.center = CGPointMake(CGRectGetMidX(self->_cardFrame), CGRectGetMidY(self->_cardFrame));
|
|
512
584
|
wrapper.layer.cornerRadius = self->_cardCornerRadius;
|
|
513
585
|
if (content) {
|
|
514
586
|
content.frame = (CGRect){CGPointZero, content.frame.size};
|
|
515
587
|
}
|
|
516
588
|
}];
|
|
517
589
|
|
|
590
|
+
// Animate rotation back to 0
|
|
591
|
+
CGFloat collapseStartAngle = _rotationEndAngle * M_PI / 180.0;
|
|
592
|
+
if (collapseStartAngle != 0) {
|
|
593
|
+
CABasicAnimation *rotAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
|
|
594
|
+
rotAnim.fromValue = @(collapseStartAngle);
|
|
595
|
+
rotAnim.toValue = @(0);
|
|
596
|
+
rotAnim.duration = dur;
|
|
597
|
+
rotAnim.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :1.0 :0.5 :1.0];
|
|
598
|
+
rotAnim.fillMode = kCAFillModeForwards;
|
|
599
|
+
rotAnim.removedOnCompletion = NO;
|
|
600
|
+
[wrapper.layer addAnimation:rotAnim forKey:@"morphRotation"];
|
|
601
|
+
}
|
|
602
|
+
|
|
518
603
|
// Fade out target screen
|
|
519
604
|
[self scheduleScreenFadeOut:targetScreen duration:dur];
|
|
520
605
|
|
|
@@ -542,7 +627,9 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
542
627
|
CGRect imageFrame = imageFrameForScaleMode(
|
|
543
628
|
_scaleMode, imageSize, targetFrame.size);
|
|
544
629
|
|
|
545
|
-
container = [[UIView alloc] initWithFrame:
|
|
630
|
+
container = [[UIView alloc] initWithFrame:CGRectZero];
|
|
631
|
+
container.bounds = CGRectMake(0, 0, targetFrame.size.width, targetFrame.size.height);
|
|
632
|
+
container.center = CGPointMake(CGRectGetMidX(targetFrame), CGRectGetMidY(targetFrame));
|
|
546
633
|
container.clipsToBounds = YES;
|
|
547
634
|
container.layer.cornerRadius = targetCornerRadius;
|
|
548
635
|
|
|
@@ -568,11 +655,25 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
|
|
|
568
655
|
initWithDuration:dur
|
|
569
656
|
timingParameters:timing];
|
|
570
657
|
[animator addAnimations:^{
|
|
571
|
-
container.
|
|
658
|
+
container.bounds = CGRectMake(0, 0, self->_cardFrame.size.width, self->_cardFrame.size.height);
|
|
659
|
+
container.center = CGPointMake(CGRectGetMidX(self->_cardFrame), CGRectGetMidY(self->_cardFrame));
|
|
572
660
|
container.layer.cornerRadius = self->_cardCornerRadius;
|
|
573
661
|
snapshot.frame = (CGRect){CGPointZero, self->_cardFrame.size};
|
|
574
662
|
}];
|
|
575
663
|
|
|
664
|
+
// Animate rotation back to 0
|
|
665
|
+
CGFloat collapseStartAngleNW = _rotationEndAngle * M_PI / 180.0;
|
|
666
|
+
if (collapseStartAngleNW != 0) {
|
|
667
|
+
CABasicAnimation *rotAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
|
|
668
|
+
rotAnim.fromValue = @(collapseStartAngleNW);
|
|
669
|
+
rotAnim.toValue = @(0);
|
|
670
|
+
rotAnim.duration = dur;
|
|
671
|
+
rotAnim.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :1.0 :0.5 :1.0];
|
|
672
|
+
rotAnim.fillMode = kCAFillModeForwards;
|
|
673
|
+
rotAnim.removedOnCompletion = NO;
|
|
674
|
+
[container.layer addAnimation:rotAnim forKey:@"morphRotation"];
|
|
675
|
+
}
|
|
676
|
+
|
|
576
677
|
// Fade out target screen
|
|
577
678
|
[self scheduleScreenFadeOut:targetScreen duration:dur];
|
|
578
679
|
|
|
@@ -72,6 +72,27 @@ extern UIView *RNCMorphCardFindScreenContainer(UIView *view);
|
|
|
72
72
|
_snapshotContainer = container;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
- (void)fadeOutSnapshot {
|
|
76
|
+
UIView *snap = _snapshotContainer;
|
|
77
|
+
if (!snap) return;
|
|
78
|
+
// Only fade out if there are React children underneath to reveal.
|
|
79
|
+
// If no children (scaleMode bitmap-only), keep the snapshot.
|
|
80
|
+
BOOL hasReactChildren = NO;
|
|
81
|
+
for (UIView *child in self.subviews) {
|
|
82
|
+
if (child != _snapshotContainer) { hasReactChildren = YES; break; }
|
|
83
|
+
}
|
|
84
|
+
if (!hasReactChildren) return;
|
|
85
|
+
|
|
86
|
+
[UIView animateWithDuration:0.15
|
|
87
|
+
animations:^{ snap.alpha = 0; }
|
|
88
|
+
completion:^(BOOL finished) {
|
|
89
|
+
[snap removeFromSuperview];
|
|
90
|
+
if (self->_snapshotContainer == snap) {
|
|
91
|
+
self->_snapshotContainer = nil;
|
|
92
|
+
}
|
|
93
|
+
}];
|
|
94
|
+
}
|
|
95
|
+
|
|
75
96
|
- (void)clearSnapshot {
|
|
76
97
|
if (_snapshotContainer) {
|
|
77
98
|
[_snapshotContainer removeFromSuperview];
|
|
@@ -11,6 +11,7 @@ var React = _interopRequireWildcard(require("react"));
|
|
|
11
11
|
var _reactNative = require("react-native");
|
|
12
12
|
var _NativeMorphCardModule = _interopRequireDefault(require("./specs/NativeMorphCardModule"));
|
|
13
13
|
var _NativeMorphCardSource = _interopRequireDefault(require("./specs/NativeMorphCardSource"));
|
|
14
|
+
var _MorphChildrenRegistry = require("./MorphChildrenRegistry");
|
|
14
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
17
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -23,12 +24,25 @@ const MorphCardSource = ({
|
|
|
23
24
|
height,
|
|
24
25
|
borderRadius,
|
|
25
26
|
backgroundColor,
|
|
26
|
-
|
|
27
|
+
resizeMode,
|
|
28
|
+
rotations,
|
|
29
|
+
rotationEndAngle,
|
|
27
30
|
onPress,
|
|
28
31
|
ref
|
|
29
32
|
}) => {
|
|
30
33
|
const nativeRef = React.useRef(null);
|
|
31
34
|
React.useImperativeHandle(ref, () => nativeRef.current);
|
|
35
|
+
|
|
36
|
+
// Store children in shared registry so MorphCardTarget can clone them
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
const tag = (0, _reactNative.findNodeHandle)(nativeRef.current);
|
|
39
|
+
if (tag != null) {
|
|
40
|
+
(0, _MorphChildrenRegistry.setSourceEntry)(tag, children, backgroundColor, resizeMode);
|
|
41
|
+
}
|
|
42
|
+
return () => {
|
|
43
|
+
if (tag != null) (0, _MorphChildrenRegistry.clearSourceEntry)(tag);
|
|
44
|
+
};
|
|
45
|
+
}, [children, backgroundColor, resizeMode]);
|
|
32
46
|
const style = {};
|
|
33
47
|
if (width != null) style.width = width;
|
|
34
48
|
if (height != null) style.height = height;
|
|
@@ -37,6 +51,16 @@ const MorphCardSource = ({
|
|
|
37
51
|
style.overflow = 'hidden';
|
|
38
52
|
}
|
|
39
53
|
if (backgroundColor != null) style.backgroundColor = backgroundColor;
|
|
54
|
+
const handleLayout = React.useCallback(e => {
|
|
55
|
+
const tag = (0, _reactNative.findNodeHandle)(nativeRef.current);
|
|
56
|
+
if (tag != null) {
|
|
57
|
+
const {
|
|
58
|
+
width: lw,
|
|
59
|
+
height: lh
|
|
60
|
+
} = e.nativeEvent.layout;
|
|
61
|
+
(0, _MorphChildrenRegistry.setSourceLayout)(tag, lw, lh);
|
|
62
|
+
}
|
|
63
|
+
}, []);
|
|
40
64
|
const handlePress = React.useCallback(() => {
|
|
41
65
|
if (!onPress) return;
|
|
42
66
|
const tag = (0, _reactNative.findNodeHandle)(nativeRef.current);
|
|
@@ -52,9 +76,12 @@ const MorphCardSource = ({
|
|
|
52
76
|
ref: nativeRef,
|
|
53
77
|
duration: duration,
|
|
54
78
|
expandDuration: expandDuration,
|
|
55
|
-
scaleMode:
|
|
79
|
+
scaleMode: resizeMode === 'contain' ? 'aspectFit' : resizeMode === 'stretch' ? 'stretch' : 'aspectFill',
|
|
56
80
|
cardBorderRadius: borderRadius,
|
|
81
|
+
rotations: rotations,
|
|
82
|
+
rotationEndAngle: rotationEndAngle,
|
|
57
83
|
style: style,
|
|
84
|
+
onLayout: handleLayout,
|
|
58
85
|
children: children
|
|
59
86
|
});
|
|
60
87
|
if (onPress) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","_interopRequireWildcard","require","_reactNative","_NativeMorphCardModule","_interopRequireDefault","_NativeMorphCardSource","_jsxRuntime","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","NativeSourceView","NativeSourceViewSpec","View","MorphCardSource","children","duration","expandDuration","width","height","borderRadius","backgroundColor","
|
|
1
|
+
{"version":3,"names":["React","_interopRequireWildcard","require","_reactNative","_NativeMorphCardModule","_interopRequireDefault","_NativeMorphCardSource","_MorphChildrenRegistry","_jsxRuntime","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","NativeSourceView","NativeSourceViewSpec","View","MorphCardSource","children","duration","expandDuration","width","height","borderRadius","backgroundColor","resizeMode","rotations","rotationEndAngle","onPress","ref","nativeRef","useRef","useImperativeHandle","current","useEffect","tag","findNodeHandle","setSourceEntry","clearSourceEntry","style","overflow","handleLayout","useCallback","lw","lh","nativeEvent","layout","setSourceLayout","handlePress","NativeMorphCardModule","prepareExpand","requestAnimationFrame","content","jsx","scaleMode","cardBorderRadius","onLayout","Pressable","exports","getViewTag","viewRef","morphExpand","sourceRef","targetRef","sourceTag","targetTag","expand","morphCollapse","collapse"],"sourceRoot":"../../src","sources":["MorphCardSource.tsx"],"mappings":"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","ignoreList":[]}
|