Skip to content

Commit 08ff8bb

Browse files
committed
remove cleaning funs that are no longer required if transitions are abrupt
1 parent 869d0d3 commit 08ff8bb

File tree

2 files changed

+22
-57
lines changed

2 files changed

+22
-57
lines changed

internal/animate/state.ts

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export interface InternalKeyframe {
1414
timestamp: number;
1515
timingFunction: TimingFunc;
1616
initialPoints: Point[];
17-
transitionSourceFrameIndex: number | null;
17+
transitionSourceFrameIndex: number;
18+
isSynthetic: boolean;
1819
}
1920

2021
export interface RenderCache {
@@ -43,34 +44,12 @@ export interface TransitionInput<T extends Keyframe> extends RenderInput {
4344

4445
export interface TransitionOutput {
4546
newFrames: InternalKeyframe[];
46-
renderCache: RenderCache;
4747
}
4848

4949
const genId = (): string => {
5050
return String(Math.random()).substr(2);
5151
};
5252

53-
export const removeStaleFrames = (
54-
keyframes: InternalKeyframe[],
55-
timestamp: number,
56-
): InternalKeyframe[] => {
57-
if (keyframes.length <= 1) return keyframes;
58-
const staleCount = keyframes.filter((k) => k.timestamp < timestamp).length;
59-
// Keep a single stale frame for the current transition.
60-
return keyframes.slice(staleCount - 1);
61-
};
62-
63-
export const cleanRenderCache = (
64-
keyframes: InternalKeyframe[],
65-
renderCache: RenderCache,
66-
): RenderCache => {
67-
const newCache: RenderCache = {};
68-
for (const frame of keyframes) {
69-
newCache[frame.id] = renderCache[frame.id];
70-
}
71-
return newCache;
72-
}
73-
7453
export const renderFramesAt = (input: RenderInput): RenderOutput => {
7554
const {renderCache, currentFrames} = input;
7655

@@ -123,13 +102,14 @@ export const renderFramesAt = (input: RenderInput): RenderOutput => {
123102

124103
// TODO generate internal frames. Delayed frames can just copy the previous one.
125104
// TODO store current shape when interrupts happen to use as source.
126-
// TODO don't remove any frames.
105+
// TODO defend against "bad" keyframes like negative timing.
106+
// TODO copy keyframes as soon as possible to make sure they aren't modified afterwards.
127107
export const transitionFrames = <T extends Keyframe>(input: TransitionInput<T>): TransitionOutput => {
128-
const {renderCache, timestamp, newFrames} = input;
108+
const {timestamp, newFrames} = input;
129109

130110
// Wipe animation when given no keyframes.
131111
if (input.newFrames.length === 0) {
132-
return {renderCache: input.renderCache, newFrames: []};
112+
return {newFrames: []};
133113
}
134114

135115
// Add current state as initial frame.
@@ -140,7 +120,8 @@ export const transitionFrames = <T extends Keyframe>(input: TransitionInput<T>):
140120
initialPoints: currentState.points,
141121
timestamp: timestamp,
142122
timingFunction: (p) => p,
143-
transitionSourceFrameIndex: null,
123+
transitionSourceFrameIndex: -1,
124+
isSynthetic: true,
144125
},
145126
];
146127

@@ -151,5 +132,5 @@ export const transitionFrames = <T extends Keyframe>(input: TransitionInput<T>):
151132
}
152133
}
153134

154-
return {renderCache, newFrames: internalFrames};
135+
return {newFrames: internalFrames};
155136
};

public/animate.ts

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,10 @@ import {
88
renderFramesAt,
99
transitionFrames,
1010
Keyframe,
11-
removeStaleFrames,
1211
RenderCache,
13-
cleanRenderCache,
1412
} from "../internal/animate/state";
1513

16-
// TODO copy keyframes as soon as possible to make sure they aren't modified afterwards.
17-
// TODO make sure callbacks don't fill up the stack.
18-
// TODO defend against "bad" keyframes like negative timing.
19-
// TODO keyframe callbacks
20-
14+
// TODO make sure recursive callbacks don't fill up the stack.
2115
interface CallbackKeyframe extends Keyframe {
2216
callback?: () => void;
2317
}
@@ -38,17 +32,6 @@ interface CallbackStore {
3832
[frameId: string]: () => void;
3933
}
4034

41-
const removeExpiredFrameCallbacks = (
42-
frames: InternalKeyframe[],
43-
oldStore: CallbackStore,
44-
): CallbackStore => {
45-
const newStore: CallbackStore = {};
46-
for (const frame of frames) {
47-
newStore[frame.id] = oldStore[frame.id];
48-
}
49-
return newStore;
50-
};
51-
5235
const canvasBlobGenerator = (keyframe: CanvasKeyframe): Point[] => {
5336
return mapPoints(genFromOptions(keyframe.blobOptions), ({curr}) => {
5437
curr.x += keyframe?.canvasOptions?.offsetX || 0;
@@ -63,38 +46,39 @@ export const canvasPath = (): CanvasAnimation => {
6346
let callbackStore: CallbackStore = {};
6447

6548
const renderFrame: CanvasAnimation["renderFrame"] = () => {
66-
const renderTime = Date.now();
67-
internalFrames = removeStaleFrames(internalFrames, renderTime);
6849
const renderOutput = renderFramesAt({
6950
renderCache: renderCache,
70-
timestamp: renderTime,
51+
timestamp: Date.now(),
7152
currentFrames: internalFrames,
7253
});
54+
55+
// Update render cache with returned value.
7356
renderCache = renderOutput.renderCache;
57+
58+
// Invoke callback if defined and the first time the frame is reached.
7459
if (renderOutput.lastFrameId && callbackStore[renderOutput.lastFrameId]) {
7560
callbackStore[renderOutput.lastFrameId]();
7661
delete callbackStore[renderOutput.lastFrameId];
7762
}
63+
7864
return renderPath2D(renderOutput.points);
7965
};
8066

8167
const transition: CanvasAnimation["transition"] = (...keyframes) => {
82-
const transitionTime = Date.now();
8368
const transitionOutput = transitionFrames<CanvasKeyframe>({
8469
renderCache: renderCache,
85-
timestamp: transitionTime,
70+
timestamp: Date.now(),
8671
currentFrames: internalFrames,
8772
newFrames: keyframes,
8873
shapeGenerator: canvasBlobGenerator,
8974
});
90-
renderCache = transitionOutput.renderCache;
91-
internalFrames = transitionOutput.newFrames;
9275

93-
// Cleanup stored data that is no longer associated with a known frame.
94-
callbackStore = removeExpiredFrameCallbacks(internalFrames, callbackStore);
95-
renderCache = cleanRenderCache(internalFrames, renderCache);
76+
// Reset internal state..
77+
internalFrames = transitionOutput.newFrames;
78+
callbackStore = {};
79+
renderCache = {};
9680

97-
// Populate the callback using returned frame ids.
81+
// Populate callback store using returned frame ids.
9882
for (const newFrame of internalFrames) {
9983
if (newFrame.transitionSourceFrameIndex === null) continue;
10084
const {callback} = keyframes[newFrame.transitionSourceFrameIndex];

0 commit comments

Comments
 (0)