UNPKG

@nativescript/core

Version:

A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.

238 lines • 9.25 kB
import { CoreTypes } from '../../core-types'; import { Trace } from '../../trace'; // Types. import { unsetValue } from '../core/properties'; import { Animation } from '.'; import { backgroundColorProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty, rotateProperty, opacityProperty, rotateXProperty, rotateYProperty, widthProperty, heightProperty } from '../styling/style-properties'; export class UnparsedKeyframe { } export class KeyframeDeclaration { } export class KeyframeInfo { constructor() { this.curve = CoreTypes.AnimationCurve.ease; } } export class KeyframeAnimationInfo { constructor() { this.name = ''; this.duration = 0.3; this.delay = 0; this.iterations = 1; this.curve = 'ease'; this.isForwards = false; this.isReverse = false; } } export class KeyframeAnimation { constructor() { this.delay = 0; this.iterations = 1; } static keyframeAnimationFromInfo(info) { if (!info?.keyframes?.length) { Trace.write(`No keyframes found for animation '${info.name}'.`, Trace.categories.Animation, Trace.messageType.warn); return null; } const length = info.keyframes.length; const animations = new Array(); let startDuration = 0; if (info.isReverse) { for (let index = length - 1; index >= 0; index--) { const keyframe = info.keyframes[index]; startDuration = KeyframeAnimation.parseKeyframe(info, keyframe, animations, startDuration); } } else { for (let index = 0; index < length; index++) { const keyframe = info.keyframes[index]; startDuration = KeyframeAnimation.parseKeyframe(info, keyframe, animations, startDuration); } for (let index = length - 1; index > 0; index--) { const a1 = animations[index]; const a2 = animations[index - 1]; if (a2['curve'] !== undefined) { a1['curve'] = a2['curve']; a2['curve'] = undefined; } } } animations.map((a) => (a['curve'] ? a : Object.assign(a, { curve: info.curve }))); const animation = new KeyframeAnimation(); animation.delay = info.delay; animation.iterations = info.iterations; animation.animations = animations; animation._isForwards = info.isForwards; return animation; } static parseKeyframe(info, keyframe, animations, startDuration) { const animation = {}; for (const declaration of keyframe.declarations) { animation[declaration.property] = declaration.value; } let duration = keyframe.duration; if (duration === 0) { duration = 0.01; } else { duration = info.duration * duration - startDuration; startDuration += duration; } animation.duration = info.isReverse ? info.duration - duration : duration; animation.curve = keyframe.curve; animation.forceLayer = true; animation.valueSource = 'keyframe'; animations.push(animation); return startDuration; } get isPlaying() { return this._isPlaying; } cancel() { if (!this.isPlaying) { Trace.write('Keyframe animation is already playing.', Trace.categories.Animation, Trace.messageType.warn); return; } this._isPlaying = false; for (let i = this._nativeAnimations.length - 1; i >= 0; i--) { const animation = this._nativeAnimations[i]; if (animation.isPlaying) { animation.cancel(); } } if (this._nativeAnimations.length > 0) { const animation = this._nativeAnimations[0]; this._resetAnimationValues(this._target, animation); } this._resetAnimations(); } play(view) { if (this._isPlaying) { Trace.write('Keyframe animation is already playing.', Trace.categories.Animation, Trace.messageType.warn); return new Promise((resolve) => { resolve(); }); } const animationFinishedPromise = new Promise((resolve) => { this._resolve = resolve; }); this._isPlaying = true; this._nativeAnimations = new Array(); this._target = view; if (this.delay !== 0) { setTimeout(() => this.animate(view, 0, this.iterations), this.delay); } else { this.animate(view, 0, this.iterations); } return animationFinishedPromise; } animate(view, index, iterations) { if (!this._isPlaying) { return; } if (index === 0) { const animation = this.animations[0]; if ('backgroundColor' in animation) { view.style[backgroundColorProperty.keyframe] = animation.backgroundColor; } if ('scale' in animation) { view.style[scaleXProperty.keyframe] = animation.scale.x; view.style[scaleYProperty.keyframe] = animation.scale.y; } if ('translate' in animation) { view.style[translateXProperty.keyframe] = animation.translate.x; view.style[translateYProperty.keyframe] = animation.translate.y; } if ('rotate' in animation) { view.style[rotateXProperty.keyframe] = animation.rotate.x; view.style[rotateYProperty.keyframe] = animation.rotate.y; view.style[rotateProperty.keyframe] = animation.rotate.z; } if ('opacity' in animation) { view.style[opacityProperty.keyframe] = animation.opacity; } if ('height' in animation) { view.style[heightProperty.keyframe] = animation.height; } if ('width' in animation) { view.style[widthProperty.keyframe] = animation.width; } setTimeout(() => this.animate(view, 1, iterations), 1); } else if (index < 0 || index >= this.animations.length) { iterations -= 1; if (iterations > 0) { this.animate(view, 0, iterations); } else { if (this._isForwards === false) { const animation = this.animations[this.animations.length - 1]; this._resetAnimationValues(view, animation); } this._resolveAnimationFinishedPromise(); } } else { let animation; const cachedAnimation = this._nativeAnimations[index - 1]; if (cachedAnimation) { animation = cachedAnimation; } else { const animationDef = { ...this.animations[index], target: view }; animation = new Animation([animationDef]); this._nativeAnimations.push(animation); } const isLastIteration = iterations - 1 <= 0; // Catch the animation cancel to prevent unhandled promise rejection warnings animation .play(isLastIteration) .then(() => { this.animate(view, index + 1, iterations); }, (error) => { Trace.write(typeof error === 'string' ? error : error.message, Trace.categories.Animation, Trace.messageType.warn); }) .catch((error) => { Trace.write(typeof error === 'string' ? error : error.message, Trace.categories.Animation, Trace.messageType.warn); }); // tslint:disable-line } } _resolveAnimationFinishedPromise() { this._nativeAnimations = new Array(); this._isPlaying = false; this._target = null; this._resolve(); } _resetAnimations() { this._nativeAnimations = new Array(); this._isPlaying = false; this._target = null; } _resetAnimationValues(view, animation) { if ('backgroundColor' in animation) { view.style[backgroundColorProperty.keyframe] = unsetValue; } if ('scale' in animation) { view.style[scaleXProperty.keyframe] = unsetValue; view.style[scaleYProperty.keyframe] = unsetValue; } if ('translate' in animation) { view.style[translateXProperty.keyframe] = unsetValue; view.style[translateYProperty.keyframe] = unsetValue; } if ('rotate' in animation) { view.style[rotateProperty.keyframe] = unsetValue; } if ('opacity' in animation) { view.style[opacityProperty.keyframe] = unsetValue; } if ('height' in animation) { view.style[heightProperty.keyframe] = unsetValue; } if ('width' in animation) { view.style[widthProperty.keyframe] = unsetValue; } } } //# sourceMappingURL=keyframe-animation.js.map