Motion 动效工具
库内部用的动效内核也一并导出:把弹簧参数换算成 linear() 缓动的 spring()、五个预设、WAAPI 的薄封装 animate()、布局位移用的 FLIP,以及处理进退场卸载时机的 usePresence。它们都读全局倍率 --ok-ms,开了减弱动效会自动归零。
先用 CSS
进退场、悬停、落位这类状态过渡,直接写 CSS 过渡并引用令牌。只有需要量位置、或者由事件一次性触发的动画才用脚本。
/* 能用 CSS 过渡解决的,优先用 CSS:时长和弹簧都是令牌 */
.thing { transition: transform var(--ok-spring-dur) var(--ok-spring), opacity var(--ok-dur-2) var(--ok-ease-out); }animate 与弹簧预设
animate(node, keyframes, options) 就是 element.animate(),多做三件事:时长乘以 --ok-ms;options.spring 传预设名或弹簧参数时自动换成对应的时长和 linear() 缓动;默认 240ms、ease-out。
五个预设(presets)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
soft | stiffness 170 · damping 26 | 587ms | 无过冲。高度展开、进度;悬浮卡片进场用它的曲线(时长压到 303ms)。过冲约 0%。 |
spring | stiffness 260 · damping 24 | 454ms | 默认。开关、分段、滑块落位。过冲约 3%。 |
snappy | stiffness 520 · damping 34 | 325ms | 和默认一样的过冲,更快。小控件。过冲约 3%。 |
bouncy | stiffness 300 · damping 18 | 542ms | 明显回弹。松手回弹。过冲约 15%。 |
wobbly | stiffness 180 · damping 9 | 1104ms | 大幅摆动,只用于品牌时刻(标志、点赞)。过冲约 33%。 |
上表“默认值”一栏是换算出的时长。CSS 里对应 --ok-spring-* 和 --ok-spring-*-dur 这组令牌。
spring():自己调一条
spring({ stiffness, damping, mass }) 返回 { duration, easing, overshoot, … }。easing 是可以直接贴进 CSS 的 linear(…)。
import { spring } from 'oink-ui';
const s = spring({ stiffness: 300, damping: 18 });
s.duration; // 542(ms)
s.overshoot; // 0.148
s.easing; // 'linear(0, 0.028, 0.101, …, 1)',可以直接写进 CSS
node.style.transition = `transform ${s.duration}ms ${s.easing}`;想边调参数边看曲线和效果,用“动效”一章里的 弹簧实验台:拖动刚度、阻尼、质量,实时算出时长、过冲和 linear(),可以一键复制。
playFlip:布局变了,元素滑过去
FLIP 分两步:变更之前记下每个节点的位置;变更之后调用 playFlip(first),它量出新位置并把每个节点从旧位置弹到新位置。React 里第二步放在 useLayoutEffect 中。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
不在 React 的渲染流程里(直接改 DOM)时可以用一步到位的 flip(nodes, mutate, options?)。
usePresence:等退场结束再卸载
条件渲染的元素一卸载就没了,来不及播退场。usePresence(open, exitMs) 让节点多活一会儿:state 用来写 [data-state] 过渡,mounted 决定是否渲染。
浮在页面上的容器不需要自己用它:FloatingCard 和各个弹出组件已经处理好了进退场。
API
函数
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
animate | (node, keyframes, options?) => Animation | null | — | WAAPI 封装。options 是 KeyframeAnimationOptions 加一个 spring(预设名或弹簧参数)。默认 duration: 240、easing: ease.out,不保留 fill;有 delay 时自动 fill: 'backwards'。 |
spring | (options?) => SpringResult | — | 弹簧参数(stiffness 默认 260、damping 默认 24、mass 默认 1)换算成时长和 linear() 缓动。 |
presets | Record<SpringPreset, SpringResult> | — | 五个预设的换算结果。 |
ease | { out, in, inOut, standard } | — | 四条贝塞尔曲线的字符串,和 --ok-ease-* 一致。 |
playFlip | (first: Map<Element, DOMRect>, options?) => void | — | FLIP 的第二步:从记录的位置弹到当前位置。位移小于 0.5px 的节点跳过。默认用 spring 预设。 |
flip | (nodes, mutate, options?) => void | — | 一步到位的 FLIP:记录、执行 mutate、播放。 |
speed | () => number | — | 读当前的 --ok-ms(减弱动效时为 0)。 |
wait | (ms: number) => Promise<void> | — | 按 --ok-ms 缩放过的延时,用来串联几段动画。 |
floatMotion | { in: 303, fade: 100, out: 160 } | — | 悬浮卡片共用的进场位移、进场淡入、退场时长(ms,未乘 --ok-ms),和 --ok-float-in-dur / --ok-float-fade-dur / --ok-float-out-dur 一致。 |
supportsLinear | boolean | — | 浏览器是否支持 linear() 缓动;不支持时 animate 用预设里的贝塞尔回退。 |
usePresence(open, exitMs?)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
open* | boolean | — | 是否应该显示。 |
exitMs | number | 400 | 退场的兜底时长:过渡没有触发 transitionend 时,到点也会卸载。 |
返回 mounted | boolean | — | 是否渲染节点。 |
返回 state | 'open' | 'closed' | — | 写到 data-state 上。进场时会先以 closed 渲染一帧,过渡才有起点。 |
返回 ref | MutableRefObject<HTMLElement | null> | — | 接到做过渡的那个节点上,用来监听它的 transitionend。 |
减弱动效。不需要自己判断 prefers-reduced-motion:animate() 的时长会乘以 0,动画直接到位;CSS 里用时长令牌的过渡同理。不要用 setTimeout 写死毫秒数去等动画结束,用 animation.finished 或 wait()。