Skip to content

Commit 996aeb4

Browse files
committed
fix(reanimated): drop the finished controller before cancel can stop it
1 parent 5e30445 commit 996aeb4

3 files changed

Lines changed: 42 additions & 11 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@plextv/react-lightning-plugin-reanimated': patch
3+
---
4+
5+
don't stop a finished, pooled animation controller when a composed animation is cancelled

‎packages/plugin-reanimated/src/animation/runAnimationProgram.test.ts‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { AnimationSettings } from '@lightningjs/renderer';
22
import { describe, expect, it } from 'vitest';
3+
34
import type { LightningElement } from '@plextv/react-lightning';
5+
46
import { leafProgram, repeatProgram, sequenceProgram } from './animationProgram';
57
import { runAnimationProgram } from './runAnimationProgram';
68

7-
const settings = (
8-
over: Partial<AnimationSettings> = {},
9-
): AnimationSettings => ({
9+
const settings = (over: Partial<AnimationSettings> = {}): AnimationSettings => ({
1010
duration: 10,
1111
easing: 'linear',
1212
delay: 0,
@@ -96,6 +96,30 @@ describe('runAnimationProgram', () => {
9696
expect(view.applied.length).toBeLessThanOrEqual(1);
9797
});
9898

99+
it('cancel after the program finished does not stop the pooled controller', async () => {
100+
const view = makeView();
101+
let stops = 0;
102+
view.animateStyle = function (key: PropertyKey, value: unknown) {
103+
this.applied.push({ key, value });
104+
return {
105+
waitUntilStopped: () => Promise.resolve(),
106+
stop() {
107+
stops++;
108+
},
109+
};
110+
};
111+
112+
const cancel = runAnimationProgram(
113+
view as unknown as LightningElement,
114+
'x',
115+
leafProgram({ toValue: 1, lngAnimation: settings() }),
116+
);
117+
await flush();
118+
cancel();
119+
120+
expect(stops).toBe(0);
121+
});
122+
99123
it('repeats a finite positive count that many times', async () => {
100124
const view = makeView();
101125

‎packages/plugin-reanimated/src/animation/runAnimationProgram.ts‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { IAnimationController } from '@lightningjs/renderer';
2-
import type {
3-
LightningElement,
4-
LightningElementStyle,
5-
} from '@plextv/react-lightning';
2+
3+
import type { LightningElement, LightningElementStyle } from '@plextv/react-lightning';
4+
65
import type { AnimationProgram, ProgramLeaf } from './animationProgram';
76

87
export type CancelAnimation = () => void;
@@ -30,16 +29,19 @@ export function runAnimationProgram(
3029
// Call as a method: extracting animateStyle drops `this`, so it throws
3130
// and the catch below silently freezes the whole composed animation.
3231
const animatableView = view as unknown as {
33-
animateStyle: (
34-
key: keyof LightningElementStyle,
35-
value: unknown,
36-
) => IAnimationController;
32+
animateStyle: (key: keyof LightningElementStyle, value: unknown) => IAnimationController;
3733
};
3834
const controller = animatableView.animateStyle(prop, leaf.toValue);
3935

4036
current = controller;
4137

4238
await controller.waitUntilStopped();
39+
40+
// The renderer pools controllers: once stopped this one may already drive
41+
// another animation, so cancel must not stop it.
42+
if (current === controller) {
43+
current = undefined;
44+
}
4345
} catch {
4446
// node was destroyed or recycled mid-flight; stop quietly
4547
cancelled = true;

0 commit comments

Comments
 (0)