mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 20:11:24 +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
|
// << 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) {
|
export function test_CancellingAnimation(done) {
|
||||||
let label = prepareTest();
|
let label = prepareTest();
|
||||||
|
|
||||||
|
@ -10,9 +10,9 @@ import { View } from "../core/view";
|
|||||||
|
|
||||||
// Types.
|
// Types.
|
||||||
import { Color } from "../../color";
|
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 { AnimationPromise } from ".";
|
||||||
|
|
||||||
export module Properties {
|
export module Properties {
|
||||||
@ -84,11 +84,16 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
|
|||||||
|
|
||||||
abstract _resolveAnimationCurve(curve: any): any;
|
abstract _resolveAnimationCurve(curve: any): any;
|
||||||
|
|
||||||
public play(): AnimationPromiseDefinition {
|
protected _rejectAlreadyPlaying(): AnimationPromiseDefinition{
|
||||||
if (this.isPlaying) {
|
const reason = "Animation is already playing.";
|
||||||
throw new Error("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 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
|
// 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) => {
|
var animationFinishedPromise = <AnimationPromiseDefinition>new Promise<void>((resolve, reject) => {
|
||||||
@ -123,9 +128,7 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public cancel(): void {
|
public cancel(): void {
|
||||||
if (!this.isPlaying) {
|
// Implemented in platform specific files
|
||||||
throw new Error("Animation is not currently playing.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isPlaying(): boolean {
|
public get isPlaying(): boolean {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { AnimationDefinition } from ".";
|
import { AnimationDefinition } from ".";
|
||||||
import { View } from "../core/view";
|
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 {
|
import {
|
||||||
opacityProperty, backgroundColorProperty, rotateProperty,
|
opacityProperty, backgroundColorProperty, rotateProperty,
|
||||||
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
|
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
|
||||||
@ -135,6 +135,10 @@ export class Animation extends AnimationBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public play(): AnimationPromise {
|
public play(): AnimationPromise {
|
||||||
|
if (this.isPlaying) {
|
||||||
|
return this._rejectAlreadyPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
let animationFinishedPromise = super.play();
|
let animationFinishedPromise = super.play();
|
||||||
|
|
||||||
this._animators = new Array<android.animation.Animator>();
|
this._animators = new Array<android.animation.Animator>();
|
||||||
@ -170,10 +174,13 @@ export class Animation extends AnimationBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public cancel(): void {
|
public cancel(): void {
|
||||||
super.cancel();
|
if (!this.isPlaying) {
|
||||||
if (traceEnabled()) {
|
traceWrite("Animation is not currently playing.", traceCategories.Animation, traceType.warn);
|
||||||
traceWrite("Cancelling AnimatorSet.", traceCategories.Animation);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traceWrite("Cancelling AnimatorSet.", traceCategories.Animation);
|
||||||
|
|
||||||
this._animatorSet.cancel();
|
this._animatorSet.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +308,7 @@ export class Animation extends AnimationBase {
|
|||||||
} else {
|
} else {
|
||||||
propertyAnimation.target.style[backgroundColorProperty.keyframe] = originalValue1;
|
propertyAnimation.target.style[backgroundColorProperty.keyframe] = originalValue1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) {
|
if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) {
|
||||||
propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor);
|
propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor);
|
||||||
}
|
}
|
||||||
@ -414,7 +421,7 @@ export class Animation extends AnimationBase {
|
|||||||
} else {
|
} else {
|
||||||
propertyAnimation.target.style[rotateProperty.keyframe] = originalValue1;
|
propertyAnimation.target.style[rotateProperty.keyframe] = originalValue1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propertyAnimation.target.nativeViewProtected) {
|
if (propertyAnimation.target.nativeViewProtected) {
|
||||||
propertyAnimation.target[rotateProperty.setNative](propertyAnimation.target.style.rotate);
|
propertyAnimation.target[rotateProperty.setNative](propertyAnimation.target.style.rotate);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { AnimationDefinition } from ".";
|
import { AnimationDefinition } from ".";
|
||||||
import { View } from "../core/view";
|
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 {
|
import {
|
||||||
opacityProperty, backgroundColorProperty, rotateProperty,
|
opacityProperty, backgroundColorProperty, rotateProperty,
|
||||||
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
|
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
|
||||||
@ -210,6 +210,10 @@ export class Animation extends AnimationBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public play(): AnimationPromise {
|
public play(): AnimationPromise {
|
||||||
|
if (this.isPlaying) {
|
||||||
|
return this._rejectAlreadyPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
let animationFinishedPromise = super.play();
|
let animationFinishedPromise = super.play();
|
||||||
this._finishedAnimations = 0;
|
this._finishedAnimations = 0;
|
||||||
this._cancelledAnimations = 0;
|
this._cancelledAnimations = 0;
|
||||||
@ -218,7 +222,10 @@ export class Animation extends AnimationBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public cancel(): void {
|
public cancel(): void {
|
||||||
super.cancel();
|
if (!this.isPlaying) {
|
||||||
|
traceWrite("Animation is not currently playing.", traceCategories.Animation, traceType.warn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let length = this._mergedPropertyAnimations.length;
|
let length = this._mergedPropertyAnimations.length;
|
||||||
|
@ -12,6 +12,8 @@ import { View, Color } from "../core/view";
|
|||||||
|
|
||||||
import { AnimationCurve } from "../enums";
|
import { AnimationCurve } from "../enums";
|
||||||
|
|
||||||
|
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceType } from "../../trace";
|
||||||
|
|
||||||
// Types.
|
// Types.
|
||||||
import { unsetValue } from "../core/properties";
|
import { unsetValue } from "../core/properties";
|
||||||
import { Animation } from "./animation";
|
import { Animation } from "./animation";
|
||||||
@ -143,25 +145,32 @@ export class KeyframeAnimation implements KeyframeAnimationDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public cancel() {
|
public cancel() {
|
||||||
if (this._isPlaying) {
|
if (!this.isPlaying) {
|
||||||
this._isPlaying = false;
|
traceWrite("Keyframe animation is already playing.", traceCategories.Animation, traceType.warn);
|
||||||
for (let i = this._nativeAnimations.length - 1; i >= 0; i--) {
|
return;
|
||||||
let animation = this._nativeAnimations[i];
|
|
||||||
if (animation.isPlaying) {
|
|
||||||
animation.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this._nativeAnimations.length > 0) {
|
|
||||||
let animation = this._nativeAnimations[0];
|
|
||||||
this._resetAnimationValues(this._target, animation);
|
|
||||||
}
|
|
||||||
this._rejectAnimationFinishedPromise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._isPlaying = false;
|
||||||
|
for (let i = this._nativeAnimations.length - 1; i >= 0; i--) {
|
||||||
|
let animation = this._nativeAnimations[i];
|
||||||
|
if (animation.isPlaying) {
|
||||||
|
animation.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this._nativeAnimations.length > 0) {
|
||||||
|
let animation = this._nativeAnimations[0];
|
||||||
|
this._resetAnimationValues(this._target, animation);
|
||||||
|
}
|
||||||
|
this._rejectAnimationFinishedPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
public play(view: View): Promise<void> {
|
public play(view: View): Promise<void> {
|
||||||
if (this._isPlaying) {
|
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) => {
|
let animationFinishedPromise = new Promise<void>((resolve, reject) => {
|
||||||
|
Reference in New Issue
Block a user