Skip to content

Commit c6f2872

Browse files
authored
fix: Stop jank in animation when duration changes on SpringValue (#1378)
* feat: handle config.duration increasing mid animation * refactor: use memoized duration instead of progress differences allows for time to be extended or shortened
1 parent 8a2d5dd commit c6f2872

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

packages/animated/src/AnimatedValue.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export class AnimatedValue<T = any> extends Animated {
88
lastPosition!: number
99
lastVelocity?: number | null
1010
v0?: number | null
11+
durationProgress = 0
1112

1213
constructor(protected _value: T) {
1314
super()
@@ -51,6 +52,7 @@ export class AnimatedValue<T = any> extends Animated {
5152
this.done = false
5253
if (is.num(this._value)) {
5354
this.elapsedTime = 0
55+
this.durationProgress = 0
5456
this.lastPosition = this._value
5557
if (done) this.lastVelocity = null
5658
this.v0 = null

packages/core/src/SpringValue.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ export class SpringValue<T = any> extends FrameValue<T> {
104104
/** The last `scheduleProps` call that changed the `to` prop */
105105
protected _lastToId = 0
106106

107+
protected _memoizedDuration = 0
108+
107109
constructor(from: Exclude<T, object>, props?: SpringUpdate<T>)
108110
constructor(props?: SpringUpdate<T>)
109111
constructor(arg1?: any, arg2?: any) {
@@ -191,7 +193,7 @@ export class SpringValue<T = any> extends FrameValue<T> {
191193
return
192194
}
193195

194-
const elapsed = (node.elapsedTime += dt)
196+
let elapsed = (node.elapsedTime += dt)
195197
const from = anim.fromValues[i]
196198

197199
const v0 =
@@ -207,8 +209,31 @@ export class SpringValue<T = any> extends FrameValue<T> {
207209
if (!is.und(config.duration)) {
208210
let p = 1
209211
if (config.duration > 0) {
210-
p = (config.progress || 0) + elapsed / config.duration
212+
/**
213+
* Here we check if the duration has changed in the config
214+
* and if so update the elapsed time to the percentage
215+
* of completition so there is no jank in the animation
216+
* https://github.com/pmndrs/react-spring/issues/1163
217+
*/
218+
if (this._memoizedDuration !== config.duration) {
219+
// update the memoized version to the new duration
220+
this._memoizedDuration = config.duration
221+
222+
// if the value has started animating we need to update it
223+
if (node.durationProgress > 0) {
224+
// set elapsed time to be the same percentage of progress as the previous duration
225+
node.elapsedTime = config.duration * node.durationProgress
226+
// add the delta so the below updates work as expected
227+
elapsed = node.elapsedTime += dt
228+
}
229+
}
230+
231+
// calculate the new progress
232+
p = (config.progress || 0) + elapsed / this._memoizedDuration
233+
// p is clamped between 0-1
211234
p = p > 1 ? 1 : p < 0 ? 0 : p
235+
// store our new progress
236+
node.durationProgress = p
212237
}
213238

214239
position = from + config.easing(p) * (to - from)

0 commit comments

Comments
 (0)