Skip to content

Commit fa80355

Browse files
NathanWalkerSvetoslavTsenov
authored andcommitted
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
1 parent 8141737 commit fa80355

File tree

5 files changed

+81
-30
lines changed

5 files changed

+81
-30
lines changed

tests/app/ui/animation/animation-tests.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,31 @@ export function test_AnimatingProperties(done) {
5454
// << animation-properties
5555
}
5656

57+
export function test_PlayRejectsWhenAlreadyPlayingAnimation(done) {
58+
let label = prepareTest();
59+
60+
var animation = label.createAnimation({ translate: { x: 100, y: 100 }, duration: 5 });
61+
62+
animation.play();
63+
animation.play().then(() => {
64+
// should never get here
65+
throw new Error("Already playing.");
66+
}, (e) => {
67+
TKUnit.assert(animation.isPlaying === true, "animation.isPlaying should be true since it's currently playing.");
68+
if (e === "Animation is already playing.") {
69+
done();
70+
}
71+
});
72+
}
73+
74+
export function test_CancelIgnoredWhenNotPlayingAnimation() {
75+
let label = prepareTest();
76+
77+
var animation = label.createAnimation({ translate: { x: 100, y: 100 }, duration: 5 });
78+
animation.cancel(); // should not throw
79+
TKUnit.assert(!animation.isPlaying, "animation.isPlaying should be falsey since it was never played.");
80+
}
81+
5782
export function test_CancellingAnimation(done) {
5883
let label = prepareTest();
5984

tns-core-modules/ui/animation/animation-common.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import { View } from "../core/view";
1010

1111
// Types.
1212
import { Color } from "../../color";
13-
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories } from "../../trace";
13+
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceType } from "../../trace";
1414

15-
export { Color, traceEnabled, traceWrite, traceCategories };
15+
export { Color, traceEnabled, traceWrite, traceCategories, traceType };
1616
export { AnimationPromise } from ".";
1717

1818
export module Properties {
@@ -84,11 +84,16 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
8484

8585
abstract _resolveAnimationCurve(curve: any): any;
8686

87-
public play(): AnimationPromiseDefinition {
88-
if (this.isPlaying) {
89-
throw new Error("Animation is already playing.");
90-
}
87+
protected _rejectAlreadyPlaying(): AnimationPromiseDefinition{
88+
const reason = "Animation is already playing.";
89+
traceWrite(reason, traceCategories.Animation, traceType.warn);
90+
91+
return <AnimationPromiseDefinition>new Promise<void>((resolve, reject) => {
92+
reject(reason);
93+
});
94+
}
9195

96+
public play(): AnimationPromiseDefinition {
9297
// We have to actually create a "Promise" due to a bug in the v8 engine and decedent promises
9398
// We just cast it to a animationPromise so that all the rest of the code works fine
9499
var animationFinishedPromise = <AnimationPromiseDefinition>new Promise<void>((resolve, reject) => {
@@ -123,9 +128,7 @@ export abstract class AnimationBase implements AnimationBaseDefinition {
123128
}
124129

125130
public cancel(): void {
126-
if (!this.isPlaying) {
127-
throw new Error("Animation is not currently playing.");
128-
}
131+
// Implemented in platform specific files
129132
}
130133

131134
public get isPlaying(): boolean {

tns-core-modules/ui/animation/animation.android.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { AnimationDefinition } from ".";
33
import { View } from "../core/view";
44

5-
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, Color, traceWrite, traceEnabled, traceCategories } from "./animation-common";
5+
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, Color, traceWrite, traceEnabled, traceCategories, traceType } from "./animation-common";
66
import {
77
opacityProperty, backgroundColorProperty, rotateProperty,
88
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
@@ -135,6 +135,10 @@ export class Animation extends AnimationBase {
135135
}
136136

137137
public play(): AnimationPromise {
138+
if (this.isPlaying) {
139+
return this._rejectAlreadyPlaying();
140+
}
141+
138142
let animationFinishedPromise = super.play();
139143

140144
this._animators = new Array<android.animation.Animator>();
@@ -170,10 +174,13 @@ export class Animation extends AnimationBase {
170174
}
171175

172176
public cancel(): void {
173-
super.cancel();
174-
if (traceEnabled()) {
175-
traceWrite("Cancelling AnimatorSet.", traceCategories.Animation);
177+
if (!this.isPlaying) {
178+
traceWrite("Animation is not currently playing.", traceCategories.Animation, traceType.warn);
179+
return;
176180
}
181+
182+
traceWrite("Cancelling AnimatorSet.", traceCategories.Animation);
183+
177184
this._animatorSet.cancel();
178185
}
179186

@@ -301,7 +308,7 @@ export class Animation extends AnimationBase {
301308
} else {
302309
propertyAnimation.target.style[backgroundColorProperty.keyframe] = originalValue1;
303310
}
304-
311+
305312
if (propertyAnimation.target.nativeViewProtected && propertyAnimation.target[backgroundColorProperty.setNative]) {
306313
propertyAnimation.target[backgroundColorProperty.setNative](propertyAnimation.target.style.backgroundColor);
307314
}
@@ -414,7 +421,7 @@ export class Animation extends AnimationBase {
414421
} else {
415422
propertyAnimation.target.style[rotateProperty.keyframe] = originalValue1;
416423
}
417-
424+
418425
if (propertyAnimation.target.nativeViewProtected) {
419426
propertyAnimation.target[rotateProperty.setNative](propertyAnimation.target.style.rotate);
420427
}

tns-core-modules/ui/animation/animation.ios.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { AnimationDefinition } from ".";
22
import { View } from "../core/view";
33

4-
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, traceWrite, traceEnabled, traceCategories } from "./animation-common";
4+
import { AnimationBase, Properties, PropertyAnimation, CubicBezierAnimationCurve, AnimationPromise, traceWrite, traceEnabled, traceCategories, traceType } from "./animation-common";
55
import {
66
opacityProperty, backgroundColorProperty, rotateProperty,
77
translateXProperty, translateYProperty, scaleXProperty, scaleYProperty
@@ -210,6 +210,10 @@ export class Animation extends AnimationBase {
210210
}
211211

212212
public play(): AnimationPromise {
213+
if (this.isPlaying) {
214+
return this._rejectAlreadyPlaying();
215+
}
216+
213217
let animationFinishedPromise = super.play();
214218
this._finishedAnimations = 0;
215219
this._cancelledAnimations = 0;
@@ -218,7 +222,10 @@ export class Animation extends AnimationBase {
218222
}
219223

220224
public cancel(): void {
221-
super.cancel();
225+
if (!this.isPlaying) {
226+
traceWrite("Animation is not currently playing.", traceCategories.Animation, traceType.warn);
227+
return;
228+
}
222229

223230
let i = 0;
224231
let length = this._mergedPropertyAnimations.length;

tns-core-modules/ui/animation/keyframe-animation.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { View, Color } from "../core/view";
1212

1313
import { AnimationCurve } from "../enums";
1414

15+
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceType } from "../../trace";
16+
1517
// Types.
1618
import { unsetValue } from "../core/properties";
1719
import { Animation } from "./animation";
@@ -143,25 +145,32 @@ export class KeyframeAnimation implements KeyframeAnimationDefinition {
143145
}
144146

145147
public cancel() {
146-
if (this._isPlaying) {
147-
this._isPlaying = false;
148-
for (let i = this._nativeAnimations.length - 1; i >= 0; i--) {
149-
let animation = this._nativeAnimations[i];
150-
if (animation.isPlaying) {
151-
animation.cancel();
152-
}
153-
}
154-
if (this._nativeAnimations.length > 0) {
155-
let animation = this._nativeAnimations[0];
156-
this._resetAnimationValues(this._target, animation);
148+
if (!this.isPlaying) {
149+
traceWrite("Keyframe animation is already playing.", traceCategories.Animation, traceType.warn);
150+
return;
151+
}
152+
153+
this._isPlaying = false;
154+
for (let i = this._nativeAnimations.length - 1; i >= 0; i--) {
155+
let animation = this._nativeAnimations[i];
156+
if (animation.isPlaying) {
157+
animation.cancel();
157158
}
158-
this._rejectAnimationFinishedPromise();
159159
}
160+
if (this._nativeAnimations.length > 0) {
161+
let animation = this._nativeAnimations[0];
162+
this._resetAnimationValues(this._target, animation);
163+
}
164+
this._rejectAnimationFinishedPromise();
160165
}
161166

162167
public play(view: View): Promise<void> {
163168
if (this._isPlaying) {
164-
throw new Error("Animation is already playing.");
169+
const reason = "Keyframe animation is already playing.";
170+
traceWrite(reason, traceCategories.Animation, traceType.warn);
171+
return new Promise<void>((resolve, reject) => {
172+
reject(reason);
173+
});
165174
}
166175

167176
let animationFinishedPromise = new Promise<void>((resolve, reject) => {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy