mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 03:31:45 +08:00
257 lines
9.4 KiB
TypeScript
257 lines
9.4 KiB
TypeScript
import definition = require("ui/animation");
|
|
import viewModule = require("ui/core/view");
|
|
import colorModule = require("color");
|
|
import types = require("utils/types");
|
|
import * as traceModule from "trace";
|
|
|
|
var trace: typeof traceModule;
|
|
function ensureTrace() {
|
|
if (!trace) {
|
|
trace = require("trace");
|
|
}
|
|
}
|
|
|
|
export module Properties {
|
|
export var opacity = "opacity";
|
|
export var backgroundColor = "backgroundColor";
|
|
export var translate = "translate";
|
|
export var rotate = "rotate";
|
|
export var scale = "scale";
|
|
}
|
|
|
|
export interface PropertyAnimation {
|
|
target: viewModule.View;
|
|
property: string;
|
|
value: any;
|
|
duration?: number;
|
|
delay?: number;
|
|
iterations?: number;
|
|
curve?: any;
|
|
}
|
|
|
|
export class CubicBezierAnimationCurve implements definition.CubicBezierAnimationCurve {
|
|
|
|
public x1: number;
|
|
public y1: number;
|
|
public x2: number;
|
|
public y2: number;
|
|
|
|
constructor(x1: number, y1: number, x2: number, y2: number) {
|
|
this.x1 = x1;
|
|
this.y1 = y1;
|
|
this.x2 = x2;
|
|
this.y2 = y2;
|
|
}
|
|
}
|
|
|
|
// This is a BOGUS Class to make TypeScript happy - This is not needed other than to make TS happy.
|
|
// We didn't want to actually modify Promise; as the cancel() is ONLY valid for animations "Promise"
|
|
export class AnimationPromise implements definition.AnimationPromise {
|
|
public cancel(): void { /* Do Nothing */ }
|
|
public then(onFulfilled?: (value?: any) => void, onRejected?: (error?: any) => void): AnimationPromise { return new AnimationPromise(); }
|
|
public catch(onRejected?: (error?: any) => void): AnimationPromise { return new AnimationPromise(); }
|
|
}
|
|
|
|
export class Animation implements definition.Animation {
|
|
public _propertyAnimations: Array<PropertyAnimation>;
|
|
public _playSequentially: boolean;
|
|
private _isPlaying: boolean;
|
|
private _resolve;
|
|
private _reject;
|
|
|
|
public play(): AnimationPromise {
|
|
if (this.isPlaying) {
|
|
throw new Error("Animation is already playing.");
|
|
}
|
|
|
|
// 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
|
|
var animationFinishedPromise = <AnimationPromise>new Promise<void>((resolve, reject) => {
|
|
this._resolve = resolve;
|
|
this._reject = reject;
|
|
});
|
|
|
|
this.fixupAnimationPromise(animationFinishedPromise);
|
|
|
|
this._isPlaying = true;
|
|
return animationFinishedPromise;
|
|
}
|
|
|
|
private fixupAnimationPromise(promise: AnimationPromise): void {
|
|
// Since we are using function() below because of arguments, TS won't automatically do a _this for those functions.
|
|
var _this = this;
|
|
promise.cancel = () => {
|
|
_this.cancel();
|
|
};
|
|
var _then = promise.then;
|
|
promise.then = function() {
|
|
var r = _then.apply(promise, arguments);
|
|
_this.fixupAnimationPromise(r);
|
|
return r;
|
|
};
|
|
var _catch = promise.catch;
|
|
promise.catch = function() {
|
|
var r = _catch.apply(promise, arguments);
|
|
_this.fixupAnimationPromise(r);
|
|
return r;
|
|
};
|
|
}
|
|
|
|
public cancel(): void {
|
|
if (!this.isPlaying) {
|
|
throw new Error("Animation is not currently playing.");
|
|
}
|
|
}
|
|
|
|
public get isPlaying(): boolean {
|
|
return this._isPlaying;
|
|
}
|
|
|
|
constructor(animationDefinitions: Array<definition.AnimationDefinition>, playSequentially?: boolean) {
|
|
if (!animationDefinitions || animationDefinitions.length === 0) {
|
|
throw new Error("No animation definitions specified");
|
|
}
|
|
|
|
ensureTrace();
|
|
|
|
trace.write("Analyzing " + animationDefinitions.length + " animation definitions...", trace.categories.Animation);
|
|
this._propertyAnimations = new Array<PropertyAnimation>();
|
|
var i = 0;
|
|
var length = animationDefinitions.length;
|
|
for (; i < length; i++) {
|
|
animationDefinitions[i].curve = definition._resolveAnimationCurve(animationDefinitions[i].curve);
|
|
this._propertyAnimations = this._propertyAnimations.concat(Animation._createPropertyAnimations(animationDefinitions[i]));
|
|
}
|
|
|
|
if (this._propertyAnimations.length === 0) {
|
|
throw new Error("Nothing to animate.");
|
|
}
|
|
trace.write("Created " + this._propertyAnimations.length + " individual property animations.", trace.categories.Animation);
|
|
|
|
this._playSequentially = playSequentially;
|
|
}
|
|
|
|
public _resolveAnimationFinishedPromise() {
|
|
this._isPlaying = false;
|
|
this._resolve();
|
|
}
|
|
|
|
public _rejectAnimationFinishedPromise() {
|
|
this._isPlaying = false;
|
|
this._reject(new Error("Animation cancelled."));
|
|
}
|
|
|
|
private static _createPropertyAnimations(animationDefinition: definition.AnimationDefinition): Array<PropertyAnimation> {
|
|
if (!animationDefinition.target) {
|
|
throw new Error("No animation target specified.");
|
|
}
|
|
|
|
for (let item in animationDefinition) {
|
|
if (!types.isDefined(animationDefinition[item])) {
|
|
continue;
|
|
}
|
|
|
|
if ((item === Properties.opacity ||
|
|
item === Properties.rotate ||
|
|
item === "duration" ||
|
|
item === "delay" ||
|
|
item === "iterations") && !types.isNumber(animationDefinition[item])) {
|
|
throw new Error(`Property ${item} must be valid number. Value: ${animationDefinition[item]}`);
|
|
} else if ((item === Properties.scale ||
|
|
item === Properties.translate) &&
|
|
(!types.isNumber((<definition.Pair>animationDefinition[item]).x) ||
|
|
!types.isNumber((<definition.Pair>animationDefinition[item]).y))) {
|
|
throw new Error(`Property ${item} must be valid Pair. Value: ${animationDefinition[item]}`);
|
|
} else if (item === Properties.backgroundColor && !colorModule.Color.isValid(animationDefinition.backgroundColor)) {
|
|
throw new Error(`Property ${item} must be valid color. Value: ${animationDefinition[item]}`);
|
|
}
|
|
}
|
|
|
|
var propertyAnimations = new Array<PropertyAnimation>();
|
|
|
|
// 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: types.isString(animationDefinition.backgroundColor) ?
|
|
new colorModule.Color(<any>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) {
|
|
propertyAnimations.push({
|
|
target: animationDefinition.target,
|
|
property: Properties.rotate,
|
|
value: animationDefinition.rotate,
|
|
duration: animationDefinition.duration,
|
|
delay: animationDefinition.delay,
|
|
iterations: animationDefinition.iterations,
|
|
curve: animationDefinition.curve
|
|
});
|
|
}
|
|
|
|
if (propertyAnimations.length === 0) {
|
|
throw new Error("No animation property specified.");
|
|
}
|
|
|
|
return propertyAnimations;
|
|
}
|
|
|
|
public static _getAnimationInfo(animation: PropertyAnimation): string {
|
|
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
|
|
});
|
|
}
|
|
}
|