@@ -16,8 +16,34 @@ export interface InternalKeyframe {
1616 timestamp : number ;
1717 timingFunction : TimingFunc ;
1818 initialPoints : Point [ ] ;
19- preparedBeforePoints ?: Point [ ] ;
20- preparedAfterPoints ?: Point [ ] ;
19+ }
20+
21+ export interface RenderCache {
22+ [ frameId : string ] : {
23+ preparedEndPoints ?: Point [ ] ;
24+ preparedStartPoints ?: Point [ ] ;
25+ } ;
26+ }
27+
28+ export interface RenderInput {
29+ keyframes : InternalKeyframe [ ] ;
30+ timestamp : number ;
31+ cache : RenderCache ;
32+ }
33+
34+ export interface RenderOutput {
35+ points : Point [ ] ;
36+ lastFrameId : string | null ;
37+ cache : RenderCache ;
38+ }
39+
40+ export interface TransitionInput extends RenderInput {
41+ newFrames : Keyframe [ ] ;
42+ }
43+
44+ export interface TransitionOutput {
45+ frames : InternalKeyframe [ ] ;
46+ cache : RenderCache ;
2147}
2248
2349const genId = ( ) : string => {
@@ -34,68 +60,80 @@ export const removeStaleFrames = (
3460 return keyframes . slice ( staleCount - 1 ) ;
3561} ;
3662
37- // TODO cache prepared points? Ideally without modifying keyframes argument.
38- export const renderFramesAt = ( keyframes : InternalKeyframe [ ] , timestamp : number ) : Point [ ] => {
39- if ( keyframes . length === 0 ) return [ ] ;
63+ export const renderFramesAt = ( input : RenderInput ) : RenderOutput => {
64+ const { cache, keyframes} = input ;
65+
66+ if ( keyframes . length === 0 ) {
67+ return { cache, lastFrameId : null , points : [ ] } ;
68+ }
4069
4170 // Animation freezes at the final shape if there are no more keyframes.
42- if ( keyframes . length === 1 ) return keyframes [ 0 ] . initialPoints ;
71+ if ( keyframes . length === 1 ) {
72+ const first = keyframes [ 0 ] ;
73+ return { cache, lastFrameId : first . id , points : first . initialPoints } ;
74+ }
4375
4476 // Find the start/end keyframes according to the timestamp.
4577 let startKeyframe = keyframes [ 0 ] ;
4678 let endKeyframe = keyframes [ 1 ] ;
4779 for ( let i = 2 ; i < keyframes . length ; i ++ ) {
48- if ( endKeyframe . timestamp < timestamp ) break ;
80+ if ( endKeyframe . timestamp < input . timestamp ) break ;
4981 startKeyframe = keyframes [ i - 1 ] ;
5082 endKeyframe = keyframes [ i ] ;
5183 }
5284
5385 // Use and cache prepared points for current interpolation.
54- let preparedStartPoints : Point [ ] | undefined = startKeyframe . preparedAfterPoints ;
55- let preparedEndPoints : Point [ ] | undefined = endKeyframe . preparedBeforePoints ;
86+ let preparedStartPoints : Point [ ] | undefined = cache [ startKeyframe . id ] . preparedStartPoints ;
87+ let preparedEndPoints : Point [ ] | undefined = cache [ endKeyframe . id ] . preparedEndPoints ;
5688 if ( ! preparedStartPoints || ! preparedEndPoints ) {
5789 [ preparedStartPoints , preparedEndPoints ] = prepare (
5890 startKeyframe . initialPoints ,
5991 endKeyframe . initialPoints ,
6092 { rawAngles : false , divideRatio : 1 } ,
6193 ) ;
94+ cache [ startKeyframe . id ] . preparedStartPoints = preparedStartPoints ;
95+ cache [ endKeyframe . id ] . preparedEndPoints = preparedEndPoints ;
6296 }
6397
6498 // Calculate progress between frames as a fraction.
6599 const progress =
66- ( timestamp - startKeyframe . timestamp ) / ( endKeyframe . timestamp - startKeyframe . timestamp ) ;
100+ ( input . timestamp - startKeyframe . timestamp ) /
101+ ( endKeyframe . timestamp - startKeyframe . timestamp ) ;
67102
68103 // Apply timing function of end frame.
69104 const adjustedProgress = endKeyframe . timingFunction ( progress ) ;
70105
71106 // TODO use timing function.
72- return interpolateBetween ( adjustedProgress , preparedStartPoints , preparedEndPoints ) ;
107+ return {
108+ cache,
109+ lastFrameId : startKeyframe . id ,
110+ points : interpolateBetween ( adjustedProgress , preparedStartPoints , preparedEndPoints ) ,
111+ }
73112} ;
74113
75114// TODO generate internal frames. Delayed frames can just copy the previous one.
76115// TODO store current blob when interrupts happen to use as source.
77116// TODO don't remove any frames.
78- export const transitionFrames = (
79- currentFrames : InternalKeyframe [ ] ,
80- newFrames : Keyframe [ ] ,
81- timestamp : number ,
82- ) : InternalKeyframe [ ] => {
83- let totalTime = 0 ;
117+ export const transitionFrames = ( input : TransitionInput ) : TransitionOutput => {
118+ const { cache, timestamp, newFrames} = input ;
84119
85120 // Add current state as initial frame.
121+ const currentState = renderFramesAt ( input ) ;
86122 let internalFrames : InternalKeyframe [ ] = [
87123 {
88124 id : genId ( ) ,
89- initialPoints : renderFramesAt ( currentFrames , timestamp ) ,
125+ initialPoints : currentState . points ,
90126 timestamp : timestamp ,
91127 timingFunction : ( p ) => p ,
92128 } ,
93129 ] ;
130+
131+ let totalTime = 0 ;
94132 for ( let i = 0 ; i < newFrames . length ; i ++ ) {
95133 const keyframe = newFrames [ i ] ;
96134 if ( keyframe . delay && i > 0 ) {
97135 }
98136 }
99137
100- return [ ] ;
138+ return { cache , frames : internalFrames } ;
101139} ;
0 commit comments