1 |
|
2 | import { AnimationBase, Properties, CubicBezierAnimationCurve } from './animation-common';
|
3 | import { Trace } from '../../trace';
|
4 | import { opacityProperty, backgroundColorProperty, rotateProperty, rotateXProperty, rotateYProperty, translateXProperty, translateYProperty, scaleXProperty, scaleYProperty, heightProperty, widthProperty, PercentLength } from '../styling/style-properties';
|
5 | import { ios as iosBackground } from '../styling/background';
|
6 | import { ios as iosViewUtils } from '../utils';
|
7 | import { ios as iosHelper } from '../../utils/native-helper';
|
8 | import { Screen } from '../../platform';
|
9 | export * from './animation-common';
|
10 | export { KeyframeAnimation, KeyframeAnimationInfo, KeyframeDeclaration, KeyframeInfo } from './keyframe-animation';
|
11 | const _transform = '_transform';
|
12 | const _skip = '_skip';
|
13 | const FLT_MAX = 340282346638528859811704183484516925440.0;
|
14 | class AnimationInfo {
|
15 | }
|
16 | var AnimationDelegateImpl = (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 |
|
88 | AnimationDelegateImpl.ObjCProtocols = global.CAAnimationDelegate ? [global.CAAnimationDelegate] : [];
|
89 | return AnimationDelegateImpl;
|
90 | }(NSObject));
|
91 | export 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 | }
|
118 | export 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 |
|
140 | if (!cancelled) {
|
141 | this._resolveAnimationFinishedPromise();
|
142 | }
|
143 | }
|
144 | else {
|
145 |
|
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 |
|
193 | if (nativeView.gradientLayer) {
|
194 | nativeView.gradientLayer.removeAllAnimations();
|
195 | }
|
196 |
|
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 |
|
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 |
|
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 |
|
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 , () => {
|
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 |
|
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 |
|
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 |
|
612 | result.push(propertyAnimations[i]);
|
613 | }
|
614 | else {
|
615 |
|
616 |
|
617 |
|
618 |
|
619 |
|
620 |
|
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 |
|
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 |
|
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 |
|
660 | if (nativeView.gradientLayer) {
|
661 | nativeView.gradientLayer.addAnimationForKey(nativeAnimation, 'bounds');
|
662 | }
|
663 | let clipPath;
|
664 |
|
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 |
|
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 |
|
728 |
|
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 |
|
739 | if (nativeView.outerShadowContainerLayer) {
|
740 | const shadowClipMask = nativeView.outerShadowContainerLayer.mask;
|
741 |
|
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 | }
|
774 | export 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 | }
|
783 | function calculateTransform(view) {
|
784 | const scaleX = view.scaleX || 1e-6;
|
785 | const scaleY = view.scaleY || 1e-6;
|
786 | const perspective = view.perspective || 300;
|
787 |
|
788 | let expectedTransform = new CATransform3D(CATransform3DIdentity);
|
789 |
|
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 | }
|
798 | function 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 |
|
\ | No newline at end of file |