piapia123
zh

弹簧动画参数生成器

样式

所有处理都在浏览器本地完成,数据不上传服务器

实时预览改上面的参数,这里立刻跟着变
CSS 代码

调三个物理量(质量、刚度、阻尼)与初速,得到 Framer Motion 的 `spring` 参数,以及采样成 60 个点的 CSS `linear()` 缓动函数 —— 这是 CSS 目前最接近弹簧的写法。 弹簧和「时长 + 缓动曲线」讲不清同一件事。一条 `cubic-bezier` 只有固定的快慢形状,而弹簧可能过冲、可能不过冲、也可能慢慢爬,这三种的观感差别很大,而它们的「时长」甚至不是参数的一部分(弹簧按物理自然停下)。 ⚠️ 判定只看阻尼比 ζ = c / (2·√(m·k)):ζ < 1 欠阻尼(会过冲)、= 1 临界、> 1 过阻尼(不过冲但回得慢)。这个数写在下方的说明里。 ⚠️ 数值积分用固定步长(1/240 秒)的半隐式欧拉。刚度大、质量小时显式积分会发散成 `NaN`,而 `NaN` 写进 `linear()` 会让整条声明失效(页面上只看到「动画没动」)。所以参数先夹到安全范围,积分过程中一旦越界就报错,绝不输出 `NaN`。阻尼为 0 的弹簧永远在振荡、不会停下,这种情况也会明确报错。

功能

  • 质量、刚度、阻尼、初速四个参数可调
  • 五个官方预设:gentle / wobbly / stiff / slow / molasses
  • 生成 Framer Motion 的 transition={{ type: "spring" }} 代码
  • 把弹簧响应采样成 60 个点,写成 CSS 的 linear() 缓动
  • 同时给出采样的时长(弹簧自然停下的时间)
  • ⚠️ 给出阻尼比 ζ、固有频率 ω0 与「会不会过冲」的判定
  • ⚠️ 固定步长半隐式欧拉积分,越界参数报错而不是输出 NaN
  • 实时预览里方块会一直来回弹,过冲幅度一眼可见
  • 全部在浏览器本地完成,不上传任何内容

使用方法

  1. 选一个预设,或自己调质量、刚度、阻尼与初速
  2. 看下方的阻尼比与「会不会过冲」的判定
  3. 复制 Framer Motion 的代码块,或复制带 linear() 的 CSS
  4. 在预览里看方块的弹跳,感受过冲幅度

常见问题

阻尼比 ζ 是什么,怎么判断会不会过冲?
`ζ = c / (2·√(m·k))`,其中 c 是阻尼、m 是质量、k 是刚度。ζ < 1 欠阻尼,弹簧会冲过目标再弹回来(过冲);ζ = 1 临界阻尼,恰好不过冲且回得最快;ζ > 1 过阻尼,不过冲但回来得慢。它只由这三个数决定,与初速无关(初速只影响前段的形状)。
为什么 CSS 里要用 linear()?
因为 CSS 的缓动函数里只有 `linear()` 能逐点指定进度,于是可以表达「冲过头再回来」这种过冲 —— `cubic-bezier` 在纵轴上虽然也能超出 0–1,但要精确匹配一条弹簧响应很难。把弹簧采样成 60 个点写进去,是目前 CSS 最接近弹簧的做法。代价是它定长:所以要同时给出弹簧自然停下的时长。
为什么阻尼设成 0 会报错?
因为阻尼为 0 的弹簧永远不会停下 —— 它会一直以同样的幅度振荡下去,没有「稳定」的那一刻,也就没有可以拿去采样的有限时长。真实的界面里也不该出现这种动画(用户会一直看着它抖)。把阻尼调大一点,让它最终停下就好。
这组参数对应到 Framer Motion 里怎么用?
直接用生成的那行:`transition={{ type: "spring", mass: …, stiffness: …, damping: …, velocity: … }}`。Framer Motion 的 spring 就是这套物理模型,`velocity` 是动画开始时的初速(拖拽甩出去时常见)。预设名(gentle / wobbly 等)是官方给的几组常用值,选了它上面的四个数就不参与计算。

相关工具