Dynamics · 15
Tween
定时补间 · tweenMove from the current value to a target over a fixed duration, even when interrupted.
Tweens suit discrete UI changes with a known duration. If a second click arrives mid-flight and the next tween starts from the original origin, the object jumps backward. Retarget from its current displayed value to keep the path continuous.
Formula
p = ease(clamp(elapsed / duration)) value = lerp(from, to, p) on retarget: from ← current; to ← next; elapsed ← 0
Parameters
| This page | Mature libraries | |
|---|---|---|
| duration | 0.45 s | — |
| ease | power(3, out) | GSAP power eases |
Doing it well
- On interruption, start from the visible current value, not the origin of the first action.
- Normalize time before easing; easing shapes progress without changing the tween duration.
- Give drag and tween one clear owner for the property; stop or retarget the tween on press.
In our cases
67 studies use tweens. folio updates index and title tracks each frame while launching a GSAP tween for the summary; continuous motion and brief transitions need clear state ownership.
Used in 67 cases
01Momentum Slider02Drag Gallery03Formation04Cardwall05Riffle06Revolver07Rotunda08Iris09Lens Bloom10Frieze11Aperture12Folio13Cinema Reel14Stream Showcase15Hardback16Picasso Ecommerce17Assembly18Fathom
+49 more
19Emulsion20Portfolio Photographer21Reverie22Boutique23Nova24Alive25Shape Trail26Membrane27Liquid Type28Filament29Pixels30Cipher31Console32Cutout33Crema34Aster35Spring36Benchmark37Amour38Fluid Gallery39Marathon40Atelier41Desk 9542Isle43Pantheon44Atlas47Overworld48Moments49Burst50Dimension51Morph52Meow53Relay54Lumen55Serene56Fleur57Atrium58Skyward59Helix60Crate61Prism62Flashlight63Viewfinder64Sand65Arrange66Tunnel67Reel68Type69Loupe
Reference implementation · basics.js
/** Progress of a tween at time t (seconds since start): delay, then duration, eased. Pure. */
export const tweenProgress = (t, { delay = 0, duration = 1, ease = power(3) } = {}) =>
ease(clamp((t - delay) / duration));
/**
* A tween that can be interrupted: retarget() starts from the CURRENT value,
* so a second click mid-flight never jumps back to the old start.
*/
export class Tween {
constructor({ value = 0, duration = 0.4, ease = power(3) } = {}) {
Object.assign(this, { value, duration, ease }); this.from = this.to = value; this.t = 1;
}
retarget(to) { this.from = this.value; this.to = to; this.t = 0; }
step(dt) {
if (this.t >= 1) return this.value;
this.t = Math.min(1, this.t + dt / this.duration);
this.value = lerp(this.from, this.to, this.ease(this.t));
return this.value;
}
}