UNPKG

42.7 kBJavaScriptView Raw
1// Requires
2import { AnimationBase, Properties, CubicBezierAnimationCurve } from './animation-common';
3import { Trace } from '../../trace';
4import { opacityProperty, backgroundColorProperty, rotateProperty, rotateXProperty, rotateYProperty, translateXProperty, translateYProperty, scaleXProperty, scaleYProperty, heightProperty, widthProperty, PercentLength } from '../styling/style-properties';
5import { ios as iosBackground } from '../styling/background';
6import { ios as iosViewUtils } from '../utils';
7import { ios as iosHelper } from '../../utils/native-helper';
8import { Screen } from '../../platform';
9export * from './animation-common';
10export { KeyframeAnimation, KeyframeAnimationInfo, KeyframeDeclaration, KeyframeInfo } from './keyframe-animation';
11const _transform = '_transform';
12const _skip = '_skip';
13const FLT_MAX = 340282346638528859811704183484516925440.0;
14class AnimationInfo {
15}
16var AnimationDelegateImpl = /** @class */ (function (_super) {
17 __extends(AnimationDelegateImpl, _super);
18 function AnimationDelegateImpl() {
19 return _super !== null && _super.apply(this, arguments) || this;
20 }
21 AnimationDelegateImpl.initWithFinishedCallback = function (finishedCallback, propertyAnimation, valueSource) {
22 var delegate = AnimationDelegateImpl.new();
23 delegate._finishedCallback = finishedCallback;
24 delegate._propertyAnimation = propertyAnimation;
25 delegate._valueSource = valueSource;
26 return delegate;
27 };
28 AnimationDelegateImpl.prototype.animationDidStart = function (anim) {
29 var value = this._propertyAnimation.value;
30 var setLocal = this._valueSource === 'animation';
31 var targetStyle = this._propertyAnimation.target.style;
32 this._propertyAnimation.target._suspendPresentationLayerUpdates();
33 switch (this._propertyAnimation.property) {
34 case Properties.backgroundColor:
35 targetStyle[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = value;
36 break;
37 case Properties.opacity:
38 targetStyle[setLocal ? opacityProperty.name : opacityProperty.keyframe] = value;
39 break;
40 case Properties.rotate:
41 targetStyle[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = value.x;
42 targetStyle[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = value.y;
43 targetStyle[setLocal ? rotateProperty.name : rotateProperty.keyframe] = value.z;
44 break;
45 case Properties.translate:
46 targetStyle[setLocal ? translateXProperty.name : translateXProperty.keyframe] = value.x;
47 targetStyle[setLocal ? translateYProperty.name : translateYProperty.keyframe] = value.y;
48 break;
49 case Properties.height:
50 targetStyle[setLocal ? heightProperty.name : heightProperty.keyframe] = value;
51 break;
52 case Properties.width:
53 targetStyle[setLocal ? widthProperty.name : widthProperty.keyframe] = value;
54 break;
55 case Properties.scale:
56 targetStyle[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = value.x === 0 ? 0.001 : value.x;
57 targetStyle[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = value.y === 0 ? 0.001 : value.y;
58 break;
59 case _transform:
60 if (value[Properties.translate] !== undefined) {
61 targetStyle[setLocal ? translateXProperty.name : translateXProperty.keyframe] = value[Properties.translate].x;
62 targetStyle[setLocal ? translateYProperty.name : translateYProperty.keyframe] = value[Properties.translate].y;
63 }
64 if (value[Properties.rotate] !== undefined) {
65 targetStyle[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = value[Properties.rotate].x;
66 targetStyle[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = value[Properties.rotate].y;
67 targetStyle[setLocal ? rotateProperty.name : rotateProperty.keyframe] = value[Properties.rotate].z;
68 }
69 if (value[Properties.scale] !== undefined) {
70 var x = value[Properties.scale].x;
71 var y = value[Properties.scale].y;
72 targetStyle[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = x === 0 ? 0.001 : x;
73 targetStyle[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = y === 0 ? 0.001 : y;
74 }
75 break;
76 }
77 this._propertyAnimation.target._resumePresentationLayerUpdates();
78 };
79 AnimationDelegateImpl.prototype.animationDidStopFinished = function (anim, finished) {
80 if (this._finishedCallback) {
81 this._finishedCallback(!finished);
82 }
83 if (finished && this.nextAnimation) {
84 this.nextAnimation();
85 }
86 };
87 // The CAAnimationDelegate protocol has been introduced in the iOS 10 SDK
88 AnimationDelegateImpl.ObjCProtocols = global.CAAnimationDelegate ? [global.CAAnimationDelegate] : [];
89 return AnimationDelegateImpl;
90}(NSObject));
91export function _resolveAnimationCurve(curve) {
92 switch (curve) {
93 case 'easeIn':
94 return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionEaseIn);
95 case 'easeOut':
96 return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionEaseOut);
97 case 'easeInOut':
98 return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionEaseInEaseOut);
99 case 'linear':
100 return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionLinear);
101 case 'spring':
102 return curve;
103 case 'ease':
104 return CAMediaTimingFunction.functionWithControlPoints(0.25, 0.1, 0.25, 1.0);
105 default:
106 if (curve instanceof CAMediaTimingFunction) {
107 return curve;
108 }
109 else if (curve instanceof CubicBezierAnimationCurve) {
110 const animationCurve = curve;
111 return CAMediaTimingFunction.functionWithControlPoints(animationCurve.x1, animationCurve.y1, animationCurve.x2, animationCurve.y2);
112 }
113 else {
114 console.error(`Invalid animation curve: ${curve}`);
115 }
116 }
117}
118export class Animation extends AnimationBase {
119 constructor(animationDefinitions, playSequentially) {
120 super(animationDefinitions, playSequentially);
121 this._valueSource = 'animation';
122 if (animationDefinitions.length > 0 && animationDefinitions[0].valueSource !== undefined) {
123 this._valueSource = animationDefinitions[0].valueSource;
124 }
125 if (!playSequentially) {
126 if (Trace.isEnabled()) {
127 Trace.write('Non-merged Property Animations: ' + this._propertyAnimations.length, Trace.categories.Animation);
128 }
129 this._mergedPropertyAnimations = Animation._mergeAffineTransformAnimations(this._propertyAnimations);
130 if (Trace.isEnabled()) {
131 Trace.write('Merged Property Animations: ' + this._mergedPropertyAnimations.length, Trace.categories.Animation);
132 }
133 }
134 else {
135 this._mergedPropertyAnimations = this._propertyAnimations;
136 }
137 const animationFinishedCallback = (cancelled) => {
138 if (this._playSequentially) {
139 // This function will be called by the last animation when done or by another animation if the user cancels them halfway through.
140 if (!cancelled) {
141 this._resolveAnimationFinishedPromise();
142 }
143 }
144 else {
145 // This callback will be called by each INDIVIDUAL animation when it finishes or is cancelled.
146 if (cancelled) {
147 this._cancelledAnimations++;
148 }
149 else {
150 this._finishedAnimations++;
151 }
152 if (this._cancelledAnimations > 0 && this._cancelledAnimations + this._finishedAnimations === this._mergedPropertyAnimations.length) {
153 if (Trace.isEnabled()) {
154 Trace.write(this._cancelledAnimations + ' animations cancelled.', Trace.categories.Animation);
155 }
156 }
157 else if (this._finishedAnimations === this._mergedPropertyAnimations.length) {
158 if (Trace.isEnabled()) {
159 Trace.write(this._finishedAnimations + ' animations finished.', Trace.categories.Animation);
160 }
161 this._resolveAnimationFinishedPromise();
162 }
163 }
164 };
165 this._iOSAnimationFunction = Animation._createiOSAnimationFunction(this._mergedPropertyAnimations, 0, this._playSequentially, this._valueSource, animationFinishedCallback);
166 }
167 play() {
168 if (this.isPlaying) {
169 return this._rejectAlreadyPlaying();
170 }
171 const animationFinishedPromise = super.play();
172 this._finishedAnimations = 0;
173 this._cancelledAnimations = 0;
174 this._iOSAnimationFunction();
175 return animationFinishedPromise;
176 }
177 cancel() {
178 if (!this.isPlaying) {
179 Trace.write('Animation is not currently playing.', Trace.categories.Animation, Trace.messageType.warn);
180 return;
181 }
182 if (this._mergedPropertyAnimations) {
183 for (let i = 0; i < this._mergedPropertyAnimations.length; i++) {
184 const propertyAnimation = this._mergedPropertyAnimations[i];
185 if (propertyAnimation) {
186 if (propertyAnimation.target?.nativeViewProtected) {
187 const nativeView = propertyAnimation.target.nativeViewProtected;
188 if (nativeView.layer.mask) {
189 nativeView.layer.mask.removeAllAnimations();
190 }
191 nativeView.layer.removeAllAnimations();
192 // Gradient background animations
193 if (nativeView.gradientLayer) {
194 nativeView.gradientLayer.removeAllAnimations();
195 }
196 // Border animations
197 if (nativeView.borderLayer) {
198 if (nativeView.borderLayer.mask) {
199 nativeView.borderLayer.mask.removeAllAnimations();
200 }
201 const borderLayers = nativeView.borderLayer.sublayers;
202 if (borderLayers?.count) {
203 for (let i = 0, count = borderLayers.count; i < count; i++) {
204 borderLayers[i].removeAllAnimations();
205 }
206 }
207 nativeView.borderLayer.removeAllAnimations();
208 }
209 // Shadow animations
210 if (nativeView.outerShadowContainerLayer) {
211 if (nativeView.outerShadowContainerLayer.mask) {
212 nativeView.outerShadowContainerLayer.mask.removeAllAnimations();
213 }
214 const outerShadowLayers = nativeView.outerShadowContainerLayer.sublayers;
215 if (outerShadowLayers?.count) {
216 for (let i = 0, count = outerShadowLayers.count; i < count; i++) {
217 const shadowLayer = outerShadowLayers[i];
218 if (shadowLayer.mask) {
219 shadowLayer.mask.removeAllAnimations();
220 }
221 shadowLayer.removeAllAnimations();
222 }
223 }
224 nativeView.outerShadowContainerLayer.removeAllAnimations();
225 }
226 }
227 if (propertyAnimation._propertyResetCallback) {
228 propertyAnimation._propertyResetCallback(propertyAnimation._originalValue, this._valueSource);
229 }
230 }
231 }
232 }
233 }
234 _resolveAnimationCurve(curve) {
235 return _resolveAnimationCurve(curve);
236 }
237 static _createiOSAnimationFunction(propertyAnimations, index, playSequentially, valueSource, finishedCallback) {
238 return (cancelled) => {
239 if (cancelled && finishedCallback) {
240 if (Trace.isEnabled()) {
241 Trace.write('Animation ' + (index - 1).toString() + ' was cancelled. Will skip the rest of animations and call finishedCallback(true).', Trace.categories.Animation);
242 }
243 finishedCallback(cancelled);
244 return;
245 }
246 const animation = propertyAnimations[index];
247 const args = Animation._getNativeAnimationArguments(animation, valueSource);
248 if (animation.curve === 'spring') {
249 Animation._createNativeSpringAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback);
250 }
251 else {
252 Animation._createNativeAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback);
253 }
254 };
255 }
256 static _getNativeAnimationArguments(animation, valueSource) {
257 const view = animation.target;
258 const style = view.style;
259 const nativeView = view.nativeViewProtected;
260 const parent = view.parent;
261 let propertyNameToAnimate = animation.property;
262 let subPropertyNameToAnimate;
263 let toValue = animation.value;
264 let fromValue;
265 if (nativeView) {
266 const setLocal = valueSource === 'animation';
267 switch (animation.property) {
268 case Properties.backgroundColor:
269 animation._originalValue = view.backgroundColor;
270 animation._propertyResetCallback = (value, valueSource) => {
271 style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = value;
272 };
273 fromValue = nativeView.layer.backgroundColor;
274 if (nativeView instanceof UILabel) {
275 nativeView.setValueForKey(UIColor.clearColor, 'backgroundColor');
276 }
277 toValue = toValue?.ios?.CGColor;
278 break;
279 case Properties.opacity:
280 animation._originalValue = view.opacity;
281 animation._propertyResetCallback = (value, valueSource) => {
282 style[setLocal ? opacityProperty.name : opacityProperty.keyframe] = value;
283 };
284 fromValue = nativeView.layer.opacity;
285 break;
286 case Properties.rotate:
287 animation._originalValue = {
288 x: view.rotateX,
289 y: view.rotateY,
290 z: view.rotate,
291 };
292 animation._propertyResetCallback = (value, valueSource) => {
293 style[setLocal ? rotateProperty.name : rotateProperty.keyframe] = value.z;
294 style[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = value.x;
295 style[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = value.y;
296 };
297 propertyNameToAnimate = 'transform.rotation';
298 subPropertyNameToAnimate = ['x', 'y', 'z'];
299 fromValue = {
300 x: nativeView.layer.valueForKeyPath('transform.rotation.x'),
301 y: nativeView.layer.valueForKeyPath('transform.rotation.y'),
302 z: nativeView.layer.valueForKeyPath('transform.rotation.z'),
303 };
304 if (animation.target.rotateX !== undefined && animation.target.rotateX !== 0 && Math.floor(toValue / 360) - toValue / 360 === 0) {
305 fromValue.x = (animation.target.rotateX * Math.PI) / 180;
306 }
307 if (animation.target.rotateY !== undefined && animation.target.rotateY !== 0 && Math.floor(toValue / 360) - toValue / 360 === 0) {
308 fromValue.y = (animation.target.rotateY * Math.PI) / 180;
309 }
310 if (animation.target.rotate !== undefined && animation.target.rotate !== 0 && Math.floor(toValue / 360) - toValue / 360 === 0) {
311 fromValue.z = (animation.target.rotate * Math.PI) / 180;
312 }
313 // Respect only value.z for back-compat until 3D rotations are implemented
314 toValue = {
315 x: (toValue.x * Math.PI) / 180,
316 y: (toValue.y * Math.PI) / 180,
317 z: (toValue.z * Math.PI) / 180,
318 };
319 break;
320 case Properties.translate:
321 animation._originalValue = {
322 x: view.translateX,
323 y: view.translateY,
324 };
325 animation._propertyResetCallback = (value, valueSource) => {
326 style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = value.x;
327 style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = value.y;
328 };
329 propertyNameToAnimate = 'transform';
330 fromValue = NSValue.valueWithCATransform3D(nativeView.layer.transform);
331 toValue = NSValue.valueWithCATransform3D(CATransform3DTranslate(nativeView.layer.transform, toValue.x, toValue.y, 0));
332 break;
333 case Properties.scale:
334 if (toValue.x === 0) {
335 toValue.x = 0.001;
336 }
337 if (toValue.y === 0) {
338 toValue.y = 0.001;
339 }
340 animation._originalValue = { x: view.scaleX, y: view.scaleY };
341 animation._propertyResetCallback = (value, valueSource) => {
342 style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = value.x;
343 style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = value.y;
344 };
345 propertyNameToAnimate = 'transform';
346 fromValue = NSValue.valueWithCATransform3D(nativeView.layer.transform);
347 toValue = NSValue.valueWithCATransform3D(CATransform3DScale(nativeView.layer.transform, toValue.x, toValue.y, 1));
348 break;
349 case _transform:
350 fromValue = NSValue.valueWithCATransform3D(nativeView.layer.transform);
351 animation._originalValue = {
352 xs: view.scaleX,
353 ys: view.scaleY,
354 xt: view.translateX,
355 yt: view.translateY,
356 rx: view.rotateX,
357 ry: view.rotateY,
358 rz: view.rotate,
359 };
360 animation._propertyResetCallback = (value, valueSource) => {
361 style[setLocal ? translateXProperty.name : translateXProperty.keyframe] = value.xt;
362 style[setLocal ? translateYProperty.name : translateYProperty.keyframe] = value.yt;
363 style[setLocal ? scaleXProperty.name : scaleXProperty.keyframe] = value.xs;
364 style[setLocal ? scaleYProperty.name : scaleYProperty.keyframe] = value.ys;
365 style[setLocal ? rotateXProperty.name : rotateXProperty.keyframe] = value.rx;
366 style[setLocal ? rotateYProperty.name : rotateYProperty.keyframe] = value.ry;
367 style[setLocal ? rotateProperty.name : rotateProperty.keyframe] = value.rz;
368 };
369 propertyNameToAnimate = 'transform';
370 toValue = NSValue.valueWithCATransform3D(Animation._createNativeAffineTransform(animation));
371 break;
372 case Properties.width:
373 case Properties.height: {
374 const direction = animation.property;
375 const isHeight = direction === 'height';
376 propertyNameToAnimate = 'bounds';
377 if (!parent) {
378 console.error(`cannot animate ${direction} on root view`);
379 }
380 const parentExtent = isHeight ? parent.getMeasuredHeight() : parent.getMeasuredWidth();
381 const asNumber = PercentLength.toDevicePixels(PercentLength.parse(toValue), parentExtent, parentExtent) / Screen.mainScreen.scale;
382 const currentBounds = nativeView.layer.bounds;
383 const extentX = isHeight ? currentBounds.size.width : asNumber;
384 const extentY = isHeight ? asNumber : currentBounds.size.height;
385 fromValue = NSValue.valueWithCGRect(currentBounds);
386 toValue = NSValue.valueWithCGRect(CGRectMake(currentBounds.origin.x, currentBounds.origin.y, extentX, extentY));
387 animation._originalValue = view[isHeight ? 'height' : 'width'];
388 animation._propertyResetCallback = (value, valueSource) => {
389 const prop = isHeight ? heightProperty : widthProperty;
390 style[setLocal ? prop.name : prop.keyframe] = value;
391 };
392 break;
393 }
394 default:
395 console.error(`Animating property '${animation.property}' is unsupported`);
396 }
397 }
398 let duration = 0.3;
399 if (animation.duration !== undefined) {
400 duration = animation.duration / 1000.0;
401 }
402 let delay = undefined;
403 if (animation.delay) {
404 delay = animation.delay / 1000.0;
405 }
406 let repeatCount = undefined;
407 if (animation.iterations !== undefined) {
408 if (animation.iterations === Number.POSITIVE_INFINITY) {
409 repeatCount = FLT_MAX;
410 }
411 else {
412 repeatCount = animation.iterations;
413 }
414 }
415 return {
416 propertyNameToAnimate: propertyNameToAnimate,
417 fromValue: fromValue,
418 subPropertiesToAnimate: subPropertyNameToAnimate,
419 toValue: toValue,
420 duration: duration,
421 repeatCount: repeatCount,
422 delay: delay,
423 };
424 }
425 static _createNativeAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback) {
426 const nativeView = animation.target.nativeViewProtected;
427 let nativeAnimation;
428 if (args.subPropertiesToAnimate) {
429 nativeAnimation = this._createGroupAnimation(args, animation);
430 }
431 else {
432 nativeAnimation = this._createBasicAnimation(args, animation);
433 }
434 const animationDelegate = AnimationDelegateImpl.initWithFinishedCallback(finishedCallback, animation, valueSource);
435 nativeAnimation.setValueForKey(animationDelegate, 'delegate');
436 if (nativeView) {
437 nativeView.layer.addAnimationForKey(nativeAnimation, args.propertyNameToAnimate);
438 if (args.propertyNameToAnimate === 'bounds') {
439 this.animateNestedLayerSizeUsingBasicAnimation(nativeView, args.toValue.CGRectValue, animation, args, nativeAnimation);
440 }
441 // Shadow layers do not inherit from animating view layer
442 if (nativeView.outerShadowContainerLayer) {
443 nativeView.outerShadowContainerLayer.addAnimationForKey(nativeAnimation, args.propertyNameToAnimate);
444 }
445 }
446 let callback = undefined;
447 if (index + 1 < propertyAnimations.length) {
448 callback = Animation._createiOSAnimationFunction(propertyAnimations, index + 1, playSequentially, valueSource, finishedCallback);
449 if (!playSequentially) {
450 callback();
451 }
452 else {
453 animationDelegate.nextAnimation = callback;
454 }
455 }
456 }
457 static _createGroupAnimation(args, animation) {
458 const groupAnimation = CAAnimationGroup.new();
459 groupAnimation.duration = args.duration;
460 if (args.repeatCount !== undefined) {
461 groupAnimation.repeatCount = args.repeatCount;
462 }
463 if (args.delay !== undefined) {
464 groupAnimation.beginTime = CACurrentMediaTime() + args.delay;
465 }
466 if (animation.curve !== undefined) {
467 groupAnimation.timingFunction = animation.curve;
468 }
469 const animations = NSMutableArray.alloc().initWithCapacity(3);
470 args.subPropertiesToAnimate.forEach((property) => {
471 const basicAnimationArgs = { ...args, duration: undefined, repeatCount: undefined, delay: undefined, curve: undefined };
472 basicAnimationArgs.propertyNameToAnimate = `${args.propertyNameToAnimate}.${property}`;
473 basicAnimationArgs.fromValue = args.fromValue[property];
474 basicAnimationArgs.toValue = args.toValue[property];
475 const basicAnimation = this._createBasicAnimation(basicAnimationArgs, animation);
476 animations.addObject(basicAnimation);
477 });
478 groupAnimation.animations = animations;
479 return groupAnimation;
480 }
481 static _createBasicAnimation(args, animation) {
482 const basicAnimation = CABasicAnimation.animationWithKeyPath(args.propertyNameToAnimate);
483 basicAnimation.fromValue = args.fromValue;
484 basicAnimation.toValue = args.toValue;
485 basicAnimation.duration = args.duration;
486 if (args.repeatCount !== undefined) {
487 basicAnimation.repeatCount = args.repeatCount;
488 }
489 if (args.delay !== undefined) {
490 basicAnimation.beginTime = CACurrentMediaTime() + args.delay;
491 }
492 if (animation.curve !== undefined) {
493 basicAnimation.timingFunction = animation.curve;
494 }
495 return basicAnimation;
496 }
497 static _createNativeSpringAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback) {
498 const nativeView = animation.target.nativeViewProtected;
499 let callback = undefined;
500 let nextAnimation;
501 if (index + 1 < propertyAnimations.length) {
502 callback = Animation._createiOSAnimationFunction(propertyAnimations, index + 1, playSequentially, valueSource, finishedCallback);
503 if (!playSequentially) {
504 callback();
505 }
506 else {
507 nextAnimation = callback;
508 }
509 }
510 let delay = 0;
511 if (args.delay) {
512 delay = args.delay;
513 }
514 UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(args.duration, delay, 0.2, 0, 196608 /* UIViewAnimationOptions.CurveLinear */, () => {
515 if (args.repeatCount !== undefined) {
516 UIView.setAnimationRepeatCount(args.repeatCount);
517 }
518 switch (animation.property) {
519 case Properties.backgroundColor:
520 animation.target.backgroundColor = args.toValue;
521 break;
522 case Properties.opacity:
523 animation.target.opacity = args.toValue;
524 break;
525 case Properties.height:
526 case Properties.width:
527 animation._originalValue = animation.target[animation.property];
528 nativeView.layer.setValueForKey(args.toValue, args.propertyNameToAnimate);
529 // Resize background during animation
530 iosBackground.drawBackgroundVisualEffects(animation.target);
531 animation._propertyResetCallback = function (value) {
532 animation.target[animation.property] = value;
533 };
534 break;
535 case _transform:
536 animation._originalValue = nativeView.layer.transform;
537 nativeView.layer.setValueForKey(args.toValue, args.propertyNameToAnimate);
538 // Shadow layers do not inherit from animating view layer
539 if (nativeView.outerShadowContainerLayer) {
540 nativeView.outerShadowContainerLayer.setValueForKey(args.toValue, args.propertyNameToAnimate);
541 }
542 animation._propertyResetCallback = function (value) {
543 nativeView.layer.transform = value;
544 };
545 break;
546 }
547 }, function (animationDidFinish) {
548 if (animationDidFinish) {
549 if (animation.property === _transform) {
550 if (animation.value[Properties.translate] !== undefined) {
551 animation.target.translateX = animation.value[Properties.translate].x;
552 animation.target.translateY = animation.value[Properties.translate].y;
553 }
554 if (animation.value[Properties.rotate] !== undefined) {
555 animation.target.rotateX = animation.value[Properties.rotate].x;
556 animation.target.rotateY = animation.value[Properties.rotate].y;
557 animation.target.rotate = animation.value[Properties.rotate].z;
558 }
559 if (animation.value[Properties.scale] !== undefined) {
560 animation.target.scaleX = animation.value[Properties.scale].x;
561 animation.target.scaleY = animation.value[Properties.scale].y;
562 }
563 }
564 }
565 else {
566 if (animation._propertyResetCallback) {
567 animation._propertyResetCallback(animation._originalValue);
568 }
569 }
570 if (finishedCallback) {
571 const cancelled = !animationDidFinish;
572 finishedCallback(cancelled);
573 }
574 if (animationDidFinish && nextAnimation) {
575 nextAnimation();
576 }
577 });
578 }
579 static _createNativeAffineTransform(animation) {
580 const value = animation.value;
581 let result = CATransform3DIdentity;
582 if (value[Properties.translate] !== undefined) {
583 const x = value[Properties.translate].x;
584 const y = value[Properties.translate].y;
585 result = CATransform3DTranslate(result, x, y, 0);
586 }
587 if (value[Properties.scale] !== undefined) {
588 const x = value[Properties.scale].x;
589 const y = value[Properties.scale].y;
590 result = CATransform3DScale(result, x === 0 ? 0.001 : x, y === 0 ? 0.001 : y, 1);
591 }
592 return result;
593 }
594 static _isAffineTransform(property) {
595 return property === _transform || property === Properties.translate || property === Properties.scale;
596 }
597 static _canBeMerged(animation1, animation2) {
598 const result = Animation._isAffineTransform(animation1.property) && Animation._isAffineTransform(animation2.property) && animation1.target === animation2.target && animation1.duration === animation2.duration && animation1.delay === animation2.delay && animation1.iterations === animation2.iterations && animation1.curve === animation2.curve;
599 return result;
600 }
601 static _mergeAffineTransformAnimations(propertyAnimations) {
602 const result = new Array();
603 let i = 0;
604 let j;
605 const length = propertyAnimations.length;
606 for (; i < length; i++) {
607 if (propertyAnimations[i][_skip]) {
608 continue;
609 }
610 if (!Animation._isAffineTransform(propertyAnimations[i].property)) {
611 // This is not an affine transform animation, so there is nothing to merge.
612 result.push(propertyAnimations[i]);
613 }
614 else {
615 // This animation has not been merged anywhere. Create a new transform animation.
616 // The value becomes a JSON object combining all affine transforms together like this:
617 // {
618 // translate: {x: 100, y: 100 },
619 // rotate: 90,
620 // scale: {x: 2, y: 2 }
621 // }
622 const newTransformAnimation = {
623 target: propertyAnimations[i].target,
624 property: _transform,
625 value: {},
626 duration: propertyAnimations[i].duration,
627 delay: propertyAnimations[i].delay,
628 iterations: propertyAnimations[i].iterations,
629 curve: propertyAnimations[i].curve,
630 };
631 if (Trace.isEnabled()) {
632 Trace.write('Curve: ' + propertyAnimations[i].curve, Trace.categories.Animation);
633 }
634 newTransformAnimation.value[propertyAnimations[i].property] = propertyAnimations[i].value;
635 if (Trace.isEnabled()) {
636 Trace.write('Created new transform animation: ' + Animation._getAnimationInfo(newTransformAnimation), Trace.categories.Animation);
637 }
638 // Merge all compatible affine transform animations to the right into this new animation.
639 j = i + 1;
640 if (j < length) {
641 for (; j < length; j++) {
642 if (Animation._canBeMerged(propertyAnimations[i], propertyAnimations[j])) {
643 if (Trace.isEnabled()) {
644 Trace.write('Merging animations: ' + Animation._getAnimationInfo(newTransformAnimation) + ' + ' + Animation._getAnimationInfo(propertyAnimations[j]) + ';', Trace.categories.Animation);
645 }
646 newTransformAnimation.value[propertyAnimations[j].property] = propertyAnimations[j].value;
647 // Mark that it has been merged so we can skip it on our outer loop.
648 propertyAnimations[j][_skip] = true;
649 }
650 }
651 }
652 result.push(newTransformAnimation);
653 }
654 }
655 return result;
656 }
657 static animateNestedLayerSizeUsingBasicAnimation(nativeView, bounds, animation, args, nativeAnimation) {
658 const view = animation.target;
659 // Gradient background animation
660 if (nativeView.gradientLayer) {
661 nativeView.gradientLayer.addAnimationForKey(nativeAnimation, 'bounds');
662 }
663 let clipPath; // This is also used for animating shadow
664 // Clipping mask animation
665 if (nativeView.layer.mask instanceof CAShapeLayer) {
666 let toValue;
667 if (nativeView.maskType === iosViewUtils.LayerMask.BORDER) {
668 toValue = iosBackground.generateNonUniformBorderOuterClipRoundedPath(view, bounds);
669 }
670 else if (nativeView.maskType === iosViewUtils.LayerMask.CLIP_PATH) {
671 clipPath = iosBackground.generateClipPath(view, bounds);
672 toValue = clipPath;
673 }
674 else {
675 Trace.write('Unknown mask on animating view: ' + view, Trace.categories.Animation, Trace.messageType.info);
676 }
677 if (toValue) {
678 nativeView.layer.mask.addAnimationForKey(this._createBasicAnimation({
679 ...args,
680 propertyNameToAnimate: 'path',
681 fromValue: nativeView.layer.mask.path,
682 toValue,
683 }, animation), 'path');
684 }
685 }
686 // Border animations (uniform and non-uniform)
687 if (nativeView.hasNonUniformBorder) {
688 if (nativeView.borderLayer) {
689 const innerClipPath = iosBackground.generateNonUniformBorderInnerClipRoundedPath(animation.target, bounds);
690 if (nativeView.hasNonUniformBorderColor) {
691 const borderMask = nativeView.borderLayer.mask;
692 if (borderMask instanceof CAShapeLayer) {
693 borderMask.addAnimationForKey(this._createBasicAnimation({
694 ...args,
695 propertyNameToAnimate: 'path',
696 fromValue: borderMask.path,
697 toValue: innerClipPath,
698 }, animation), 'path');
699 }
700 const borderLayers = nativeView.borderLayer.sublayers;
701 if (borderLayers?.count) {
702 const paths = iosBackground.generateNonUniformMultiColorBorderRoundedPaths(animation.target, bounds);
703 for (let i = 0, count = borderLayers.count; i < count; i++) {
704 const layer = nativeView.borderLayer.sublayers[i];
705 if (layer instanceof CAShapeLayer) {
706 layer.addAnimationForKey(this._createBasicAnimation({
707 ...args,
708 propertyNameToAnimate: 'path',
709 fromValue: layer.path,
710 toValue: paths[i],
711 }, animation), 'path');
712 }
713 }
714 }
715 }
716 else {
717 nativeView.borderLayer.addAnimationForKey(this._createBasicAnimation({
718 ...args,
719 propertyNameToAnimate: 'path',
720 fromValue: nativeView.borderLayer.path,
721 toValue: innerClipPath,
722 }, animation), 'path');
723 }
724 }
725 }
726 else {
727 // TODO: Animate border width when borders get support for percentage values
728 // Uniform corner radius also relies on view size
729 if (nativeView.layer.cornerRadius) {
730 nativeView.layer.addAnimationForKey(this._createBasicAnimation({
731 ...args,
732 propertyNameToAnimate: 'cornerRadius',
733 fromValue: nativeView.layer.cornerRadius,
734 toValue: iosBackground.getUniformBorderRadius(animation.target, bounds),
735 }, animation), 'cornerRadius');
736 }
737 }
738 // Shadow layers do not inherit from animating view layer
739 if (nativeView.outerShadowContainerLayer) {
740 const shadowClipMask = nativeView.outerShadowContainerLayer.mask;
741 // This is for animating view clip path on shadow
742 if (clipPath && shadowClipMask instanceof CAShapeLayer) {
743 shadowClipMask.addAnimationForKey(this._createBasicAnimation({
744 ...args,
745 propertyNameToAnimate: 'path',
746 fromValue: shadowClipMask.path,
747 toValue: clipPath,
748 }, animation), 'path');
749 }
750 const outerShadowLayers = nativeView.outerShadowContainerLayer.sublayers;
751 if (outerShadowLayers?.count) {
752 const { maskPath, shadowPath } = iosBackground.generateShadowLayerPaths(view, bounds);
753 for (let i = 0, count = outerShadowLayers.count; i < count; i++) {
754 const shadowLayer = outerShadowLayers[i];
755 shadowLayer.addAnimationForKey(this._createBasicAnimation({
756 ...args,
757 propertyNameToAnimate: 'shadowPath',
758 fromValue: shadowLayer.shadowPath,
759 toValue: shadowPath,
760 }, animation), 'shadowPath');
761 if (shadowLayer.mask instanceof CAShapeLayer) {
762 shadowLayer.mask.addAnimationForKey(this._createBasicAnimation({
763 ...args,
764 propertyNameToAnimate: 'path',
765 fromValue: shadowLayer.mask.path,
766 toValue: maskPath,
767 }, animation), 'path');
768 }
769 }
770 }
771 }
772 }
773}
774export function _getTransformMismatchErrorMessage(view) {
775 const expectedTransform = calculateTransform(view);
776 const expectedTransformString = getCATransform3DString(expectedTransform);
777 const actualTransformString = getCATransform3DString(view.nativeViewProtected.layer.transform);
778 if (actualTransformString !== expectedTransformString) {
779 return 'View and Native transforms do not match.\nActual: ' + actualTransformString + ';\nExpected: ' + expectedTransformString;
780 }
781 return undefined;
782}
783function calculateTransform(view) {
784 const scaleX = view.scaleX || 1e-6;
785 const scaleY = view.scaleY || 1e-6;
786 const perspective = view.perspective || 300;
787 // Order is important: translate, rotate, scale
788 let expectedTransform = new CATransform3D(CATransform3DIdentity);
789 // Only set perspective if there is 3D rotation
790 if (view.rotateX || view.rotateY) {
791 expectedTransform.m34 = -1 / perspective;
792 }
793 expectedTransform = CATransform3DTranslate(expectedTransform, view.translateX, view.translateY, 0);
794 expectedTransform = iosHelper.applyRotateTransform(expectedTransform, view.rotateX, view.rotateY, view.rotate);
795 expectedTransform = CATransform3DScale(expectedTransform, scaleX, scaleY, 1);
796 return expectedTransform;
797}
798function getCATransform3DString(t) {
799 return `[
800 ${t.m11}, ${t.m12}, ${t.m13}, ${t.m14},
801 ${t.m21}, ${t.m22}, ${t.m23}, ${t.m24},
802 ${t.m31}, ${t.m32}, ${t.m33}, ${t.m34},
803 ${t.m41}, ${t.m42}, ${t.m43}, ${t.m44}]`;
804}
805//# sourceMappingURL=index.ios.js.map
\No newline at end of file