This adds the ability to Cancel the Animation via the Promise returned via a play command.

This commit is contained in:
Nathanael Anderson
2016-03-21 22:52:55 -06:00
parent 25852e9bc6
commit ccdc394be4
7 changed files with 104 additions and 13 deletions

View File

@ -83,21 +83,69 @@ export var test_CancellingAnimation = function (done) {
// <snippet module="ui/animation" title="animation"> // <snippet module="ui/animation" title="animation">
// # Cancelling animation // # Cancelling animation
// ``` JavaScript // ``` JavaScript
var animation1 = label.createAnimation({ translate: { x: 100, y: 100 } }); var animation1 = label.createAnimation({ translate: { x: 100, y: 100}, duration: 500 });
animation1.play() animation1.play()
.then(() => { .then(() => {
////console.log("Animation finished"); ////console.log("Animation finished");
// <hide> // <hide>
assertIOSNativeTransformIsCorrect(label); throw new Error("Cancelling Animation - Should not be in the Promise Then()");
helper.goBack();
done();
// </hide> // </hide>
}) })
.catch((e) => { .catch((e) => {
////console.log("Animation cancelled"); ////console.log("Animation cancelled");
// <hide> // <hide>
helper.goBack(); helper.goBack();
done(); if (!e) {
done(new Error("Cancel path did not have proper error"));
} else if (e.toString() === "Error: Animation cancelled.") {
done()
} else {
done(e);
}
// </hide>
});
animation1.cancel();
// ```
// </snippet>
}
export var test_CancellingAnimate = function (done) {
var mainPage: pageModule.Page;
var label: labelModule.Label;
var pageFactory = function (): pageModule.Page {
label = new labelModule.Label();
label.text = "label";
var stackLayout = new stackLayoutModule.StackLayout();
stackLayout.addChild(label);
mainPage = new pageModule.Page();
mainPage.content = stackLayout;
return mainPage;
};
helper.navigate(pageFactory);
TKUnit.waitUntilReady(() => { return label.isLoaded });
// <snippet module="ui/animation" title="animation">
// # Cancelling animation
// ``` JavaScript
var animation1 = label.animate({ translate: { x: 100, y: 100 }, duration: 500 })
.then(() => {
////console.log("Animation finished");
// <hide>
throw new Error("Cancelling Animate - Should not be in Promise Then()");
// </hide>
})
.catch((e) => {
////console.log("Animation cancelled");
// <hide>
helper.goBack();
if (!e) {
done(new Error("Cancel path did not have proper error"));
} else if (e.toString() === "Error: Animation cancelled.") {
done()
} else {
done(e);
}
// </hide> // </hide>
}); });
animation1.cancel(); animation1.cancel();

View File

@ -42,6 +42,14 @@ export class CubicBezierAnimationCurve implements definition.CubicBezierAnimatio
} }
} }
// 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 { export class Animation implements definition.Animation {
public _propertyAnimations: Array<PropertyAnimation>; public _propertyAnimations: Array<PropertyAnimation>;
public _playSequentially: boolean; public _playSequentially: boolean;
@ -49,20 +57,44 @@ export class Animation implements definition.Animation {
private _resolve; private _resolve;
private _reject; private _reject;
public play(): Promise<void> { public play(): AnimationPromise {
if (this.isPlaying) { if (this.isPlaying) {
throw new Error("Animation is already playing."); throw new Error("Animation is already playing.");
} }
var animationFinishedPromise = new Promise<void>((resolve, reject) => { // 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._resolve = resolve;
this._reject = reject; this._reject = reject;
}); });
this.fixupAnimationPromise(animationFinishedPromise);
this._isPlaying = true; this._isPlaying = true;
return animationFinishedPromise; 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 { public cancel(): void {
if (!this.isPlaying) { if (!this.isPlaying) {
throw new Error("Animation is not currently playing."); throw new Error("Animation is not currently playing.");

View File

@ -31,7 +31,7 @@ export class Animation extends common.Animation implements definition.Animation
private _propertyUpdateCallbacks: Array<Function>; private _propertyUpdateCallbacks: Array<Function>;
private _propertyResetCallbacks: Array<Function>; private _propertyResetCallbacks: Array<Function>;
public play(): Promise<void> { public play(): definition.AnimationPromise {
var animationFinishedPromise = super.play(); var animationFinishedPromise = super.play();
var i: number; var i: number;

View File

@ -82,12 +82,23 @@
y: number; y: number;
} }
/**
* Create Promise that can cancel the animation, we have to pretend our returns itself along with the cancel
*/
export class AnimationPromise extends Promise<void> {
cancel(): void;
then(onFulfilled?: (value?: any) => Thenable<void>, onRejected?: (error?: any) => Thenable<void>): AnimationPromise;
then(onFulfilled?: (value?: any) => void, onRejected?: (error?: any) => void): AnimationPromise;
catch(onRejected?: (error?: any) => Thenable<void>): AnimationPromise;
catch(onRejected?: (error?: any) => void): AnimationPromise;
}
/** /**
* Defines a animation set. * Defines a animation set.
*/ */
export class Animation { export class Animation {
constructor(animationDefinitions: Array<AnimationDefinition>, playSequentially?: boolean); constructor(animationDefinitions: Array<AnimationDefinition>, playSequentially?: boolean);
public play: () => Promise<void>; public play: () => AnimationPromise;
public cancel: () => void; public cancel: () => void;
public isPlaying: boolean; public isPlaying: boolean;
} }

View File

@ -88,7 +88,7 @@ export class Animation extends common.Animation implements definition.Animation
private _cancelledAnimations: number; private _cancelledAnimations: number;
private _mergedPropertyAnimations: Array<common.PropertyAnimation>; private _mergedPropertyAnimations: Array<common.PropertyAnimation>;
public play(): Promise<void> { public play(): definition.AnimationPromise {
var animationFinishedPromise = super.play(); var animationFinishedPromise = super.play();
this._finishedAnimations = 0; this._finishedAnimations = 0;
this._cancelledAnimations = 0; this._cancelledAnimations = 0;

View File

@ -1164,7 +1164,7 @@ export class View extends ProxyObject implements definition.View {
} }
} }
public animate(animation: any): Promise<void> { public animate(animation: any): animModule.AnimationPromise {
return this.createAnimation(animation).play(); return this.createAnimation(animation).play();
} }

2
ui/core/view.d.ts vendored
View File

@ -518,7 +518,7 @@ declare module "ui/core/view" {
/** /**
* Animates one or more properties of the view based on the supplied options. * Animates one or more properties of the view based on the supplied options.
*/ */
public animate(options: animation.AnimationDefinition): Promise<void>; public animate(options: animation.AnimationDefinition): animation.AnimationPromise;
/** /**
* Creates an Animation object based on the supplied options. * Creates an Animation object based on the supplied options.