react-native-morph-card 0.1.15 → 0.2.1

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.
@@ -100,124 +100,19 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
100
100
 
101
101
  // ── Snapshot ──
102
102
 
103
- /**
104
- * Capture a snapshot of the source card's children WITHOUT border radius clipping.
105
- * Like iOS, we want the raw content (e.g. the full rectangular image), not what's
106
- * visually clipped on screen. The border radius is applied separately during animation.
107
- *
108
- * This disables both Android's clipToOutline AND Fresco's RoundingParams (used by
109
- * React Native's Image component) to capture the full rectangular content.
110
- */
111
103
  private fun captureSnapshot(): Bitmap {
112
104
  val w = width
113
105
  val h = height
114
106
  val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
115
107
  val canvas = Canvas(bitmap)
116
-
117
- // Track Fresco views to restore rounding after capture
118
- data class FrescoState(val view: View, val hierarchy: Any, val roundingParams: Any)
119
- val frescoStates = mutableListOf<FrescoState>()
120
- // Track views whose clipToOutline was disabled
121
- val clippedViews = mutableListOf<View>()
122
-
123
- // Map of Fresco views to their hierarchy (for drawing drawable directly)
124
- val frescoViews = mutableMapOf<View, Any>()
125
-
126
- fun prepareView(view: View) {
127
- // Disable outline clipping
128
- if (view.clipToOutline) {
129
- clippedViews.add(view)
130
- view.clipToOutline = false
131
- }
132
-
133
- // Detect Fresco DraweeView and disable rounding
134
- try {
135
- val getHierarchy = view.javaClass.getMethod("getHierarchy")
136
- val hierarchy = getHierarchy.invoke(view)
137
- if (hierarchy != null) {
138
- val getRounding = hierarchy.javaClass.getMethod("getRoundingParams")
139
- val rounding = getRounding.invoke(hierarchy)
140
- if (rounding != null) {
141
- val roundingClass = Class.forName("com.facebook.drawee.generic.RoundingParams")
142
- val setRounding = hierarchy.javaClass.getMethod("setRoundingParams", roundingClass)
143
- setRounding.invoke(hierarchy, null)
144
- frescoStates.add(FrescoState(view, hierarchy, rounding))
145
- frescoViews[view] = hierarchy
146
- Log.d(TAG, "captureSnapshot: disabled Fresco rounding on ${view.width}x${view.height}")
147
- }
148
- }
149
- } catch (_: Exception) {}
150
-
151
- if (view is ViewGroup) {
152
- for (i in 0 until view.childCount) {
153
- prepareView(view.getChildAt(i))
154
- }
155
- }
156
- }
157
-
158
- for (i in 0 until childCount) {
159
- prepareView(getChildAt(i))
160
- }
161
-
162
- // Draw children. For Fresco views, draw the top-level drawable directly
163
- // (since the internal drawable is rebuilt when roundingParams changes).
164
- fun drawView(view: View, c: Canvas) {
165
- val hierarchy = frescoViews[view]
166
- if (hierarchy != null) {
167
- try {
168
- val getTopDrawable = hierarchy.javaClass.getMethod("getTopLevelDrawable")
169
- val drawable = getTopDrawable.invoke(hierarchy) as? android.graphics.drawable.Drawable
170
- if (drawable != null) {
171
- drawable.setBounds(0, 0, view.width, view.height)
172
- drawable.invalidateSelf()
173
- drawable.draw(c)
174
- Log.d(TAG, "captureSnapshot: drew Fresco drawable directly ${view.width}x${view.height}")
175
- return
176
- }
177
- } catch (_: Exception) {}
178
- }
179
-
180
- if (view is ViewGroup) {
181
- // Draw background
182
- view.background?.let { bg ->
183
- bg.setBounds(0, 0, view.width, view.height)
184
- bg.draw(c)
185
- }
186
- for (i in 0 until view.childCount) {
187
- val child = view.getChildAt(i)
188
- if (child.visibility != VISIBLE) continue
189
- c.save()
190
- c.translate(child.left.toFloat(), child.top.toFloat())
191
- drawView(child, c)
192
- c.restore()
193
- }
194
- } else {
195
- view.draw(c)
196
- }
197
- }
198
-
199
108
  for (i in 0 until childCount) {
200
109
  val child = getChildAt(i)
201
110
  if (child.visibility != VISIBLE) continue
202
111
  canvas.save()
203
112
  canvas.translate(child.left.toFloat(), child.top.toFloat())
204
- drawView(child, canvas)
113
+ child.draw(canvas)
205
114
  canvas.restore()
206
115
  }
207
-
208
- // Restore clipToOutline
209
- for (view in clippedViews) {
210
- view.clipToOutline = true
211
- }
212
- // Restore Fresco rounding params
213
- for (state in frescoStates) {
214
- try {
215
- val roundingClass = Class.forName("com.facebook.drawee.generic.RoundingParams")
216
- val setRounding = state.hierarchy.javaClass.getMethod("setRoundingParams", roundingClass)
217
- setRounding.invoke(state.hierarchy, state.roundingParams)
218
- } catch (_: Exception) {}
219
- }
220
-
221
116
  return bitmap
222
117
  }
223
118
 
@@ -141,7 +141,8 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
141
141
  return [renderer imageWithActions:^(UIGraphicsImageRendererContext *ctx) {
142
142
  for (UIView *child in self.subviews) {
143
143
  CGContextSaveGState(ctx.CGContext);
144
- CGContextTranslateCTM(ctx.CGContext, child.frame.origin.x, child.frame.origin.y);
144
+ CGContextTranslateCTM(ctx.CGContext, child.frame.origin.x,
145
+ child.frame.origin.y);
145
146
  [child drawViewHierarchyInRect:(CGRect){CGPointZero, child.frame.size}
146
147
  afterScreenUpdates:NO];
147
148
  CGContextRestoreGState(ctx.CGContext);
@@ -258,9 +259,7 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
258
259
  UIView *ts = self->_targetScreenContainer;
259
260
  if (ts) {
260
261
  [UIView animateWithDuration:dur * 0.5
261
- animations:^{
262
- ts.alpha = 1;
263
- }
262
+ animations:^{ ts.alpha = 1; }
264
263
  completion:nil];
265
264
  }
266
265
  });
@@ -284,14 +283,8 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
284
283
  self.alpha = 1;
285
284
  UIView *ts = self->_targetScreenContainer;
286
285
  if (ts) { ts.alpha = 1; }
287
- [UIView animateWithDuration:0.2
288
- animations:^{
289
- wrapper.alpha = 0;
290
- }
291
- completion:^(BOOL finished) {
292
- [wrapper removeFromSuperview];
293
- self->_wrapperView = nil;
294
- }];
286
+ [wrapper removeFromSuperview];
287
+ self->_wrapperView = nil;
295
288
  resolve(@(YES));
296
289
  }];
297
290
 
@@ -345,9 +338,7 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
345
338
  UIView *ts = self->_targetScreenContainer;
346
339
  if (ts) {
347
340
  [UIView animateWithDuration:dur * 0.5
348
- animations:^{
349
- ts.alpha = 1;
350
- }
341
+ animations:^{ ts.alpha = 1; }
351
342
  completion:nil];
352
343
  }
353
344
  });
@@ -365,15 +356,9 @@ static CGRect imageFrameForScaleMode(UIViewContentMode mode,
365
356
  self.alpha = 1;
366
357
  UIView *ts = self->_targetScreenContainer;
367
358
  if (ts) { ts.alpha = 1; }
368
- [UIView animateWithDuration:0.2
369
- animations:^{
370
- container.alpha = 0;
371
- }
372
- completion:^(BOOL finished) {
373
- [container removeFromSuperview];
374
- self->_wrapperView = nil;
375
- self->_snapshot = nil;
376
- }];
359
+ [container removeFromSuperview];
360
+ self->_wrapperView = nil;
361
+ self->_snapshot = nil;
377
362
  resolve(@(YES));
378
363
  }];
379
364
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-morph-card",
3
- "version": "0.1.15",
3
+ "version": "0.2.1",
4
4
  "description": "Native card-to-modal morph transition for React Native. iOS App Store-style expand animation.",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",