Spring Parameters: mass, stiffness, damping
Spring animation không hoạt động như cubic-bezier. Thay vì một đường cong cố định với duration xác định, spring là một hệ vật lý: nó settle khi velocity về gần 0. Hiểu ba parameter của hệ này cho phép bạn thiết kế motion từ nguyên lý thay vì copy preset.
Xem thêm phần giới thiệu về spring trong Ease vs Spring.
Phương trình spring
F = -k·x - d·v
k= stiffness: lực kéo về điểm cân bằngd= damping: lực cản (ma sát)x= displacement: khoảng cách từ điểm cân bằngv= velocity: vận tốc hiện tại
Mỗi frame, hệ thống tính lực F, cập nhật velocity, cập nhật vị trí. Animation kết thúc khi |x| < threshold && |v| < threshold.
Ba parameter
Stiffness (độ cứng lò xo)
Stiffness quyết định lực kéo về đích mạnh đến đâu. Stiffness cao = lò xo cứng = animation nhanh và crisp. Stiffness thấp = lò xo mềm = animation chậm và lazy.
| Stiffness | Cảm giác | Dùng cho |
|---|---|---|
| 50–100 | Lazy, slow | Content cards, long reveals |
| 200–300 | Standard, balanced | Most UI elements |
| 400–600 | Snappy, crisp | Micro-interactions, tooltips |
| 800+ | Very stiff | Near-instant with slight bounce |
Damping (độ giảm chấn)
Damping quyết định lò xo settle nhanh hay nảy nhiều. Damping thấp = nảy nhiều (underdamped). Damping cao = không nảy, settle chậm (overdamped). Điểm giữa = critically damped (settle nhanh nhất không nảy).
Critical damping = 2 × √(stiffness × mass)
| Trạng thái | Damping / Critical | Behavior |
|---|---|---|
| Underdamped | < 1 | Overshoot, oscillate rồi settle |
| Critically damped | = 1 | Settle nhanh nhất, không overshoot |
| Overdamped | > 1 | Không overshoot, settle chậm hơn critical |
Với Framer Motion, giá trị damping khoảng 10–15 cho underdamped nhẹ (bounce tự nhiên), 20–30 cho critically damped.
Mass (khối lượng)
Mass quyết định quán tính — vật nặng bao nhiêu. Mass cao = khởi động chậm, dừng muộn. Mass thấp = phản hồi tức thì.
Mass thường để mặc định (= 1) và điều chỉnh stiffness + damping để đạt feel mong muốn. Mass hữu ích nhất khi muốn “nặng” hoặc “nhẹ” mà không thay đổi bounce amount.
Tổ hợp phổ biến
Gentle bounce — toggle thumb, like button:
{ stiffness: 260, damping: 20, mass: 1 }
Snappy, no bounce — tooltip, dropdown:
{ stiffness: 400, damping: 30, mass: 1 }
Heavy, slow settle — modal, bottom sheet:
{ stiffness: 200, damping: 25, mass: 1.2 }
Playful — notification popup, celebration:
{ stiffness: 180, damping: 12, mass: 1 }
CSS vs JavaScript spring
CSS chỉ có cubic-bezier — không có spring thật. Spring được giả lập bằng control point y > 1:
/* Giả lập spring — cố định duration */
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
Giới hạn: CSS spring luôn có duration cố định. Nếu element bị interrupt giữa chừng, animation sẽ “jump” thay vì preserve velocity.
JavaScript (Framer Motion, React Spring, GSAP) dùng vật lý thật:
// Framer Motion
animate(element, { x: 100 }, {
type: 'spring',
stiffness: 260,
damping: 20
})
// React Spring
useSpring({ x: 100, config: { stiffness: 260, damping: 20 } })
JS spring có thể interrupt và resume với velocity được preserve — kéo rồi thả ở giữa chừng sẽ continue từ vận tốc hiện tại thay vì restart.
Velocity-based spring
Điểm mạnh nhất của spring thật: velocity propagation. Khi người dùng swipe một card, spring nhận velocity từ gesture và tiếp tục theo quán tính đó. Đây là lý do iOS scroll cảm giác tự nhiên — nó là spring nhận velocity từ finger velocity.
const gesture = useGesture({
onDrag: ({ velocity, offset }) => {
api.start({
x: offset[0],
config: { velocity: velocity[0], tension: 200, friction: 30 }
})
}
})
Khi nào dùng spring thay vì bezier
Dùng spring khi: element phản hồi direct interaction (drag, press, swipe), hoặc khi cần feel “vật lý” (toggle, like button, notification).
Dùng bezier khi: animation không phụ thuộc interaction (page transition, skeleton fade, scroll reveal) hoặc khi cần timing chính xác để sync với audio/video.
Xem thêm: Cubic Bezier chuyên sâu, Gesture Feedback Motion, Modal & Sheet Animation.