UNPKG

3.19 kBJavaScriptView Raw
1/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
2/* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass
3 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
4let generateSpringRK4 = (function(){
5 function springAccelerationForState( state ){
6 return (-state.tension * state.x) - (state.friction * state.v);
7 }
8
9 function springEvaluateStateWithDerivative( initialState, dt, derivative ){
10 let state = {
11 x: initialState.x + derivative.dx * dt,
12 v: initialState.v + derivative.dv * dt,
13 tension: initialState.tension,
14 friction: initialState.friction
15 };
16
17 return { dx: state.v, dv: springAccelerationForState( state ) };
18 }
19
20 function springIntegrateState( state, dt ){
21 let a = {
22 dx: state.v,
23 dv: springAccelerationForState( state )
24 },
25 b = springEvaluateStateWithDerivative( state, dt * 0.5, a ),
26 c = springEvaluateStateWithDerivative( state, dt * 0.5, b ),
27 d = springEvaluateStateWithDerivative( state, dt, c ),
28 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
29 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
30
31 state.x = state.x + dxdt * dt;
32 state.v = state.v + dvdt * dt;
33
34 return state;
35 }
36
37 return function springRK4Factory( tension, friction, duration ){
38
39 let initState = {
40 x: -1,
41 v: 0,
42 tension: null,
43 friction: null
44 },
45 path = [0],
46 time_lapsed = 0,
47 tolerance = 1 / 10000,
48 DT = 16 / 1000,
49 have_duration, dt, last_state;
50
51 tension = parseFloat( tension ) || 500;
52 friction = parseFloat( friction ) || 20;
53 duration = duration || null;
54
55 initState.tension = tension;
56 initState.friction = friction;
57
58 have_duration = duration !== null;
59
60 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
61 if( have_duration ){
62 /* Run the simulation without a duration. */
63 time_lapsed = springRK4Factory( tension, friction );
64 /* Compute the adjusted time delta. */
65 dt = time_lapsed / duration * DT;
66 } else {
67 dt = DT;
68 }
69
70 for(;;){
71 /* Next/step function .*/
72 last_state = springIntegrateState( last_state || initState, dt );
73 /* Store the position. */
74 path.push( 1 + last_state.x );
75 time_lapsed += 16;
76 /* If the change threshold is reached, break. */
77 if( !(Math.abs( last_state.x ) > tolerance && Math.abs( last_state.v ) > tolerance) ){
78 break;
79 }
80 }
81
82 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
83 computed path and returns a snapshot of the position according to a given percentComplete. */
84 return !have_duration ? time_lapsed : function( percentComplete ){ return path[ (percentComplete * (path.length - 1)) | 0 ]; };
85 };
86}());
87
88export default generateSpringRK4;