mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 11:42:04 +08:00
fix(animations): change throw -> trace to avoid unnecessary app crash (#5475)
* fix(animations): change throw -> trace to avoid unnecessary app crash Fixes major cause of crashes/bugs in production apps using animation. * Fix fix animation throw (#1) * chore(tests): Cleanup code snippets comments * refactor(animations): Plat-specific cancel and play methods refactored
This commit is contained in:
@ -54,6 +54,31 @@ export function test_AnimatingProperties(done) {
|
||||
// << animation-properties
|
||||
}
|
||||
|
||||
export function test_PlayRejectsWhenAlreadyPlayingAnimation(done) {
|
||||
let label = prepareTest();
|
||||
|
||||
var animation = label.createAnimation({ translate: { x: 100, y: 100 }, duration: 5 });
|
||||
|
||||
animation.play();
|
||||
animation.play().then(() => {
|
||||
// should never get here
|
||||
throw new Error("Already playing.");
|
||||
}, (e) => {
|
||||
TKUnit.assert(animation.isPlaying === true, "animation.isPlaying should be true since it's currently playing.");
|
||||
if (e === "Animation is already playing.") {
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function test_CancelIgnoredWhenNotPlayingAnimation() {
|
||||
let label = prepareTest();
|
||||
|
||||
var animation = label.createAnimation({ translate: { x: 100, y: 100 }, duration: 5 });
|
||||
animation.cancel(); // should not throw
|
||||
TKUnit.assert(!animation.isPlaying, "animation.isPlaying should be falsey since it was never played.");
|
||||
}
|
||||
|
||||
export function test_CancellingAnimation(done) {
|
||||
let label = prepareTest();
|
||||
|
||||
|
@ -10,9 +10,9 @@ import { View } from "../core/view";
|
||||
|
||||
// Types.
|
||||
import { Color } from "../../color";
|
||||
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories } from "../../trace";
|
||||
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceType } from "../../trace";
|
||||
|
||||
export { Color, traceEnabled, traceWrite, traceCategories };
|
||||
export { Color, traceEnabled, traceWrite, traceCategories, traceType };
|
||||
export { AnimationPromise } from ".";
|
||||
|
||||
export module Properties {
|
||||
@ -84,11 +84,16 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
|
||||
|
||||
abstract _resolveAnimationCurve(curve: any): any;
|
||||
|
||||
public play(): AnimationPromiseDefinition {
|
||||
if (this.isPlaying) {
|
||||
throw new Error("Animation is already playing.");
|
||||
protected _rejectAlreadyPlaying(): AnimationPromiseDefinition{
|
||||
const reason = "Animation is already playing.";
|
||||
traceWrite(reason, traceCategories.Animation, traceType.warn);
|
||||
|
||||
return <AnimationPromiseDefinition>new Promise<void>((resolve, reject) => {
|
||||
reject(reason);
|
||||
});
|
||||
}
|
||||
|
||||
public play(): AnimationPromiseDefinition {
|
||||
// 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 = <AnimationPromiseDefinition>new Promise<void>((resolve, reject) => {
|
||||
@ -123,9 +128,7 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (!this.isPlaying) {
|
||||
throw new Error("Animation is not currently playing.");
|
||||
}
|
||||
// Implemented in platform specific files
|
||||
}
|
||||
|
||||
public get isPlaying(): boolean {
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { AnimationDefinition } from ".";
|
||||
import { View } from "../core/view";
|
||||
|
||||
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, Color, traceWrite, traceEnabled, traceCategories } from "./animation-common";
|
||||
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, Color, traceWrite, traceEnabled, traceCategories, traceType } from "./animation-common";
|
||||
import {
|
||||
opacityProperty, backgroundColorProperty, rotateProperty,
|
||||
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
|
||||
@ -135,6 +135,10 @@ export class Animation extends AnimationBase {
|
||||
}
|
||||
|
||||
public play(): AnimationPromise {
|
||||
if (this.isPlaying) {
|
||||
return this._rejectAlreadyPlaying();
|
||||
}
|
||||
|
||||
let animationFinishedPromise = super.play();
|
||||
|
||||
this._animators = new Array<android.animation.Animator>();
|
||||
@ -170,10 +174,13 @@ export class Animation extends AnimationBase {
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
super.cancel();
|
||||
if (traceEnabled()) {
|
||||
traceWrite("Cancelling AnimatorSet.", traceCategories.Animation);
|
||||
if (!this.isPlaying) {
|
||||
traceWrite("Animation is not currently playing.", traceCategories.Animation, traceType.warn);
|
||||
return;
|
||||
}
|
||||
|
||||
traceWrite("Cancelling AnimatorSet.", traceCategories.Animation);
|
||||
|
||||
this._animatorSet.cancel();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AnimationDefinition } from ".";
|
||||
import { View } from "../core/view";
|
||||
|
||||
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, traceWrite, traceEnabled, traceCategories } from "./animation-common";
|
||||
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, traceWrite, traceEnabled, traceCategories, traceType } from "./animation-common";
|
||||
import {
|
||||
opacityProperty, backgroundColorProperty, rotateProperty,
|
||||
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
|
||||
@ -210,6 +210,10 @@ export class Animation extends AnimationBase {
|
||||
}
|
||||
|
||||
public play(): AnimationPromise {
|
||||
if (this.isPlaying) {
|
||||
return this._rejectAlreadyPlaying();
|
||||
}
|
||||
|
||||
let animationFinishedPromise = super.play();
|
||||
this._finishedAnimations = 0;
|
||||
this._cancelledAnimations = 0;
|
||||
@ -218,7 +222,10 @@ export class Animation extends AnimationBase {
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
super.cancel();
|
||||
if (!this.isPlaying) {
|
||||
traceWrite("Animation is not currently playing.", traceCategories.Animation, traceType.warn);
|
||||
return;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
let length = this._mergedPropertyAnimations.length;
|
||||
|
@ -12,6 +12,8 @@ import { View, Color } from "../core/view";
|
||||
|
||||
import { AnimationCurve } from "../enums";
|
||||
|
||||
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceType } from "../../trace";
|
||||
|
||||
// Types.
|
||||
import { unsetValue } from "../core/properties";
|
||||
import { Animation } from "./animation";
|
||||
@ -143,7 +145,11 @@ export class KeyframeAnimation implements KeyframeAnimationDefinition {
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
if (this._isPlaying) {
|
||||
if (!this.isPlaying) {
|
||||
traceWrite("Keyframe animation is already playing.", traceCategories.Animation, traceType.warn);
|
||||
return;
|
||||
}
|
||||
|
||||
this._isPlaying = false;
|
||||
for (let i = this._nativeAnimations.length - 1; i >= 0; i--) {
|
||||
let animation = this._nativeAnimations[i];
|
||||
@ -157,11 +163,14 @@ export class KeyframeAnimation implements KeyframeAnimationDefinition {
|
||||
}
|
||||
this._rejectAnimationFinishedPromise();
|
||||
}
|
||||
}
|
||||
|
||||
public play(view: View): Promise<void> {
|
||||
if (this._isPlaying) {
|
||||
throw new Error("Animation is already playing.");
|
||||
const reason = "Keyframe animation is already playing.";
|
||||
traceWrite(reason, traceCategories.Animation, traceType.warn);
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
reject(reason);
|
||||
});
|
||||
}
|
||||
|
||||
let animationFinishedPromise = new Promise<void>((resolve, reject) => {
|
||||
|
Reference in New Issue
Block a user