@@ -108,12 +108,17 @@ export function spring(value, opts = {}) {
108108 return false ;
109109 }
110110 inv_mass = Math . min ( inv_mass + inv_mass_recovery_rate , 1 ) ;
111+
112+ // clamp elapsed time to 1/30th of a second, so that longer pauses
113+ // (blocked thread or inactive tab) don't cause the spring to go haywire
114+ const elapsed = Math . min ( now - last_time , 1000 / 30 ) ;
115+
111116 /** @type {TickContext } */
112117 const ctx = {
113118 inv_mass,
114119 opts : spring ,
115120 settled : true ,
116- dt : ( ( now - last_time ) * 60 ) / 1000
121+ dt : ( elapsed * 60 ) / 1000
117122 } ;
118123 // @ts -ignore
119124 const next_value = tick_spring ( ctx , last_value , value , target_value ) ;
@@ -236,6 +241,10 @@ export class Spring {
236241 this . #task ??= loop ( ( now ) => {
237242 this . #inverse_mass = Math . min ( this . #inverse_mass + inv_mass_recovery_rate , 1 ) ;
238243
244+ // clamp elapsed time to 1/30th of a second, so that longer pauses
245+ // (blocked thread or inactive tab) don't cause the spring to go haywire
246+ const elapsed = Math . min ( now - this . #last_time, 1000 / 30 ) ;
247+
239248 /** @type {import('./private').TickContext } */
240249 const ctx = {
241250 inv_mass : this . #inverse_mass,
@@ -245,7 +254,7 @@ export class Spring {
245254 precision : this . #precision. v
246255 } ,
247256 settled : true ,
248- dt : ( ( now - this . #last_time ) * 60 ) / 1000
257+ dt : ( elapsed * 60 ) / 1000
249258 } ;
250259
251260 var next = tick_spring ( ctx , this . #last_value, this . #current. v , this . #target. v ) ;
0 commit comments