@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
240 lines • 10.3 kB
JavaScript
// Requires.
import { Color } from '../../color';
import { Trace } from '../../trace';
import { PercentLength } from '../styling/style-properties';
export * from './animation-interfaces';
export var Properties;
(function (Properties) {
Properties.opacity = 'opacity';
Properties.backgroundColor = 'backgroundColor';
Properties.translate = 'translate';
Properties.rotate = 'rotate';
Properties.scale = 'scale';
Properties.height = 'height';
Properties.width = 'width';
})(Properties || (Properties = {}));
export class AnimationBase {
constructor(animationDefinitions, playSequentially) {
if (!animationDefinitions || animationDefinitions.length === 0) {
console.error('No animation definitions specified');
return;
}
if (Trace.isEnabled()) {
Trace.write('Analyzing ' + animationDefinitions.length + ' animation definitions...', Trace.categories.Animation);
}
this._propertyAnimations = new Array();
for (let i = 0, length = animationDefinitions.length; i < length; i++) {
if (animationDefinitions[i].curve) {
animationDefinitions[i].curve = this._resolveAnimationCurve(animationDefinitions[i].curve);
}
this._propertyAnimations = this._propertyAnimations.concat(AnimationBase._createPropertyAnimations(animationDefinitions[i]));
}
if (this._propertyAnimations.length === 0) {
if (Trace.isEnabled()) {
Trace.write('Nothing to animate.', Trace.categories.Animation);
}
return;
}
if (Trace.isEnabled()) {
Trace.write('Created ' + this._propertyAnimations.length + ' individual property animations.', Trace.categories.Animation);
}
this._playSequentially = playSequentially;
}
_rejectAlreadyPlaying() {
const reason = 'Animation is already playing.';
Trace.write(reason, Trace.categories.Animation, Trace.messageType.warn);
return new Promise((resolve, reject) => {
reject(reason);
});
}
play() {
// We have to actually create a "Promise" due to a bug in the v8 engine and decedent promises
// We just cast it to a animationPromise so that all the rest of the code works fine
const animationFinishedPromise = new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
this.fixupAnimationPromise(animationFinishedPromise);
this._isPlaying = true;
return animationFinishedPromise;
}
fixupAnimationPromise(promise) {
// Since we are using function() below because of arguments, TS won't automatically do a _this for those functions.
const _this = this;
promise.cancel = () => {
_this.cancel();
};
const _then = promise.then;
promise.then = function () {
// eslint-disable-next-line prefer-rest-params
const r = _then.apply(promise, arguments);
_this.fixupAnimationPromise(r);
return r;
};
const _catch = promise.catch;
promise.catch = function () {
// eslint-disable-next-line prefer-rest-params
const r = _catch.apply(promise, arguments);
_this.fixupAnimationPromise(r);
return r;
};
}
cancel() {
// Implemented in platform specific files
}
get isPlaying() {
return this._isPlaying;
}
_resolveAnimationFinishedPromise() {
this._isPlaying = false;
this._resolve();
}
_rejectAnimationFinishedPromise() {
this._isPlaying = false;
this._reject(new Error('Animation cancelled.'));
}
static _createPropertyAnimations(animationDefinition) {
if (!animationDefinition.target) {
console.error('No animation target specified.');
return;
}
for (const item in animationDefinition) {
const value = animationDefinition[item];
if (value === undefined) {
continue;
}
if ((item === Properties.opacity || item === 'duration' || item === 'delay' || item === 'iterations') && typeof value !== 'number') {
console.error(`Property ${item} must be valid number. Value: ${value}`);
return;
}
else if ((item === Properties.scale || item === Properties.translate) && (typeof value.x !== 'number' || typeof value.y !== 'number')) {
console.error(`Property ${item} must be valid Pair. Value: ${value}`);
return;
}
else if (item === Properties.backgroundColor && !Color.isValid(animationDefinition.backgroundColor)) {
console.error(`Property ${item} must be valid color. Value: ${value}`);
return;
}
else if (item === Properties.width || item === Properties.height) {
// Coerce input into a PercentLength object in case it's a string.
animationDefinition[item] = PercentLength.parse(value);
}
else if (item === Properties.rotate) {
const rotate = value;
if (typeof rotate !== 'number' && !(typeof rotate.x === 'number' && typeof rotate.y === 'number' && typeof rotate.z === 'number')) {
console.error(`Property ${rotate} must be valid number or Point3D. Value: ${value}`);
return;
}
}
}
const propertyAnimations = new Array();
// opacity
if (animationDefinition.opacity !== undefined) {
propertyAnimations.push({
target: animationDefinition.target,
property: Properties.opacity,
value: animationDefinition.opacity,
duration: animationDefinition.duration,
delay: animationDefinition.delay,
iterations: animationDefinition.iterations,
curve: animationDefinition.curve,
});
}
// backgroundColor
if (animationDefinition.backgroundColor !== undefined) {
propertyAnimations.push({
target: animationDefinition.target,
property: Properties.backgroundColor,
value: typeof animationDefinition.backgroundColor === 'string' ? new Color(animationDefinition.backgroundColor) : animationDefinition.backgroundColor,
duration: animationDefinition.duration,
delay: animationDefinition.delay,
iterations: animationDefinition.iterations,
curve: animationDefinition.curve,
});
}
// translate
if (animationDefinition.translate !== undefined) {
propertyAnimations.push({
target: animationDefinition.target,
property: Properties.translate,
value: animationDefinition.translate,
duration: animationDefinition.duration,
delay: animationDefinition.delay,
iterations: animationDefinition.iterations,
curve: animationDefinition.curve,
});
}
// scale
if (animationDefinition.scale !== undefined) {
propertyAnimations.push({
target: animationDefinition.target,
property: Properties.scale,
value: animationDefinition.scale,
duration: animationDefinition.duration,
delay: animationDefinition.delay,
iterations: animationDefinition.iterations,
curve: animationDefinition.curve,
});
}
// rotate
if (animationDefinition.rotate !== undefined) {
// Make sure the value of the rotation property is always Point3D
let rotationValue;
if (typeof animationDefinition.rotate === 'number') {
rotationValue = { x: 0, y: 0, z: animationDefinition.rotate };
}
else {
rotationValue = animationDefinition.rotate;
}
propertyAnimations.push({
target: animationDefinition.target,
property: Properties.rotate,
value: rotationValue,
duration: animationDefinition.duration,
delay: animationDefinition.delay,
iterations: animationDefinition.iterations,
curve: animationDefinition.curve,
});
}
// height
if (animationDefinition.height !== undefined) {
propertyAnimations.push({
target: animationDefinition.target,
property: Properties.height,
value: animationDefinition.height,
duration: animationDefinition.duration,
delay: animationDefinition.delay,
iterations: animationDefinition.iterations,
curve: animationDefinition.curve,
});
}
// width
if (animationDefinition.width !== undefined) {
propertyAnimations.push({
target: animationDefinition.target,
property: Properties.width,
value: animationDefinition.width,
duration: animationDefinition.duration,
delay: animationDefinition.delay,
iterations: animationDefinition.iterations,
curve: animationDefinition.curve,
});
}
if (propertyAnimations.length === 0) {
console.error('No known animation properties specified');
}
return propertyAnimations;
}
static _getAnimationInfo(animation) {
return JSON.stringify({
target: animation.target.id,
property: animation.property,
value: animation.value,
duration: animation.duration,
delay: animation.delay,
iterations: animation.iterations,
curve: animation.curve,
});
}
}
//# sourceMappingURL=animation-common.js.map