Skip to content

Commit 37a28d1

Browse files
committed
refactor: update spring parameters in HistogramChart for smoother animations
- Changed spring parameters to critically damped settings for improved deceleration without oscillation. - Enhanced the springProgress function to handle critically damped and overdamped scenarios, ensuring a more natural animation feel. Signed-off-by: Innei <tukon479@gmail.com>
1 parent 55ec982 commit 37a28d1

File tree

1 file changed

+33
-14
lines changed

1 file changed

+33
-14
lines changed

apps/web/src/components/ui/photo-viewer/HistogramChart.tsx

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -270,22 +270,41 @@ export const HistogramChart: FC<{
270270
const startAt = performance.now()
271271
const prev = previousHistogramRef.current
272272

273-
// Spring parameters (slightly underdamped for natural feel)
274-
const frequency = 8 // rad/s, controls oscillation speed (lower = smoother)
275-
const damping = 7 // higher = faster decay, tuned for subtle bounce
276-
const restDelta = 0.001
277-
const maxMs = 1200
273+
// Spring parameters (critically damped for smooth deceleration)
274+
const stiffness = 100 // Spring stiffness
275+
const damping = 20 // Critical damping for no oscillation
276+
const mass = 1
277+
const restDelta = 0.0001
278+
const maxMs = 1500
278279

279280
const springProgress = (tSec: number) => {
280-
// Analytic solution for underdamped second-order system step response
281-
// y(t) = 1 - e^{-d t} (cos(w t) + (d/w) sin(w t))
282-
const w = frequency
283-
const d = damping
284-
const exp = Math.exp(-d * tSec)
285-
const value =
286-
1 - exp * (Math.cos(w * tSec) + (d / w) * Math.sin(w * tSec))
287-
// Clamp to [0, 1] to avoid overshoot drawing artifacts
288-
return Math.max(0, Math.min(1, value))
281+
// Critically damped spring: smooth approach with no oscillation
282+
// Using exponential decay with linear term for natural deceleration feel
283+
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass))
284+
285+
if (dampingRatio >= 1) {
286+
// Overdamped or critically damped
287+
const w0 = Math.sqrt(stiffness / mass)
288+
const zeta = dampingRatio
289+
const wd = w0 * Math.sqrt(Math.abs(zeta * zeta - 1))
290+
291+
if (dampingRatio === 1) {
292+
// Critically damped (fastest approach without overshoot)
293+
const value = 1 - (1 + w0 * tSec) * Math.exp(-w0 * tSec)
294+
return Math.max(0, Math.min(1, value))
295+
} else {
296+
// Overdamped (slower, more damped)
297+
const exp = Math.exp(-zeta * w0 * tSec)
298+
const value =
299+
1 -
300+
exp *
301+
(Math.cosh(wd * tSec) + ((zeta * w0) / wd) * Math.sinh(wd * tSec))
302+
return Math.max(0, Math.min(1, value))
303+
}
304+
}
305+
306+
// Fallback to simple exponential ease-out
307+
return 1 - Math.exp(-5 * tSec)
289308
}
290309

291310
const lerpArray = (from: number[], to: number[], p: number) =>

0 commit comments

Comments
 (0)