Jelly UI 浪潮:当软体物理交互重塑前端设计语言

过去十年,前端设计语言在"扁平化—拟物化—新拟态"的循环中不断摇摆,而 2024 年前后悄然兴起的一股 Jelly UI(软体弹性 UI)风潮,让设计师重新将目光投向物理直觉。这一次,设计师不只是为了好看,而是为了让每一次点击、滑动、状态切换都符合用户对"物质世界"的心智模型。更值得关注的是,这场复兴不再依赖庞大的 JavaScript 库,而是建立在 CSS 原生能力、Web Animations API、View Transitions 等基础之上的工程化实践。本文将围绕这一趋势,探讨软体物理交互如何复兴原生前端设计语言,以及如何以可工程化的方法承载无障碍体验。

一、Jelly UI 的核心特征:把"物质性"还给界面

Jelly UI 并非某家公司的专有体系,而是一种设计语言的取向:按钮被按下时像果冻一样凹陷、回弹,卡片拖拽时存在阻尼与轻微的形变,Modal 弹出时伴随弹簧曲线而非线性插值。这种设计有三个共同特征:

  • 弹性反馈:交互响应具备 overshoot(过冲)与 settle(回稳)过程,使用户对触发结果有明确的因果感知。
  • 质量与重力:不同元素根据视觉权重,呈现不同的"惯性"。底部 Tab 栏的弹动通常比顶部按钮更夸张,因为它被赋予更高的"物理地位"。
  • 连续而非离散的过渡:界面状态不再硬切,而是通过位置、缩放、不透明度多通道插值,实现接近真实物体的运动连续性。

这种语言之所以在 2024 年再次引爆,核心推力来自 CSS 工作组对 motion primitives 的大幅扩展。当原生平台开始提供 spring-like 的能力,工程团队便有了脱离"黑盒动画库"的现实路径。

二、原生 CSS 设计语言的复兴

2.1 关键 API 概览

理解 Jelly UI 工程化,必须先盘点近年成为主流的原生特性:

API能力在 Jelly UI 中的角色
linear() easing多点贝塞尔 / 步进曲线模拟弹性曲线,无需 JS
View Transitions API同文档跨状态过渡承载卡片变形、列表重排
@starting-style进入动画起点元素 mount 时的"展开"
scroll-driven animations滚动驱动动画视差、头部回弹缩放
animation-composition组合多个动画多通道插值的合成
Discrete transitions for display进出动画作用于 display折叠面板、菜单的弹性展示

2.2 一个可复用的弹性进入模式

传统上,我们要给 mount 元素做"弹入"效果,通常要借助 JS 钩子。在原生 CSS 体系下,可以这样写:

.card {
  animation: jelly-in 600ms linear(0, 0.006, 0.025, 0.056, 0.113, 0.194, 0.301, ...);
  animation-composition: add;
}

@keyframes jelly-in {
  from { opacity: 0; transform: scale(0.6); }
  to   { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .card { animation: none; }
}

linear() 函数通过密集采样逼近真实弹簧曲线,在 GPU 合成层完成计算,性能开销远低于 JS 驱动的 spring loop。

三、软体物理的工程实现路径

3.1 何时选择 CSS,何时选择 JS?

落地策略可以归结为三条原则:

  1. 能 CSS 就不 JS:只要动画是确定性的(明确开始、明确结束、明确路径),优先用 linear()@keyframesView Transitions
  2. 必须 JS 的场景:

    • 用户实时拖拽时的橡皮筋效果(如 iOS 列表越界回弹);
    • 多元素之间存在物理耦合(如手风琴折叠联动);
    • 需要根据滚动速度、方向动态调参的场景。
  3. 框架内的轻量方案:React 生态下,Motion(原 Framer Motion)、@react-spring/web 已经把 spring 解算器与可访问性 hooks 融为一体,适合复杂编排。

3.2 弹簧方程的前端简化

现实工程不必为每一次动效计算完整 ODE,但工程师必须理解三件事,才能在团队中正确选择参数:

  • 质量 m:决定响应惯性,按钮常用低质量,Modal 用中质量,拖拽放手回弹用高质量。
  • 阻尼比 ζ:ζ < 1 欠阻尼(弹簧 overshoot),ζ = 1 临界阻尼(无 overshoot),ζ > 1 过阻尼(慢速缓动)。Jelly UI 偏好 ζ ≈ 0.6。
  • 刚度 k:与 stiffness 同义,k 越大,响应越快、曲线越急促。

经验参数:Material Design 3 的 emphasized motion 推荐 stiffness: 380, damping: 30,Apple HIG 的 spring 默认 mass: 1, stiffness: 170, damping: 26。把这些预设写入设计 token,可在团队中保持一致的物理感。

四、无障碍体验的工程化方法论

软体物理交互最容易掉进的陷阱,就是动画过度化。一个合格的工程体系必须建立以下约束。

4.1 必须强制遵守的降级策略

  • prefers-reduced-motion: reduce:当用户开启降低动效偏好时,所有 spring 曲线降级为 ease-out,时长压缩到 ≤ 100ms,或者直接以不透明度淡入替代位移。
  • 焦点不应跟随运动:键盘用户使用 Tab 浏览时,焦点元素的位置变化必须立即可读,不应依赖动画完成才可被屏幕阅读器无障碍树识别。
  • 避免纯动效传达信息:颜色 + 图标 + 文本三重冗余依然必要,动效仅起到强化作用。

4.2 ARIA 与动效生命周期同步

当一个 Modal 以弹性曲线入场时,正确做法是:

  1. 在动画开始前即将 aria-hidden=true 应用于背后内容;
  2. 在动画的 animationend 事件中,将焦点 focus() 到 Modal 内部首位可聚焦元素;
  3. 提供显式的关闭手势,例如 Esc 键绑定的非动画 fallback 路径。

这要求团队在动效组件库中,内置一组"无障碍契约",而不仅仅提供视觉 API。

五、性能预算与跨端一致性

5.1 性能基线

  • 高频弹性动画必须只触发 transform 与 opacity;触发 layout、paint 的属性(如 widthtop)会导致 60fps 失守。
  • 在低端机(Android Go、入门级 iPhone)上将 spring 计算限制在主线程外,优先使用 CSS linear() 而非 JS RAF 循环。
  • will-change 不应滥用,应当按需开启并在 animationend 后移除,避免长期占用合成层导致内存压力。

5.2 跨端交付的一致性策略

设计系统团队应当在以下三层落地约束:

  1. Token 层:定义 --motion-stiffness-emphasized: 380 等命名空间语义,连接 iOS HIG、Android M3、自研规范;
  2. 组件层:封装 <MotionButton><MotionSheet> 等聚合组件,内部决定降级策略;
  3. 观测层:接入 RUM(Real User Monitoring),统计 INPCLS 在动效开启前后的差异,作为体验验证依据。

六、结语:让原生回归,以工程承载美学

Jelly UI 浪潮并不只是视觉风格的轮回,它真正昭示的是前端设计语言向原生、向可工程化方向的回归。当 CSS 工作组持续输出 linear()@starting-styleView Transitions 这类一等公民能力,前端工程师无需在交互丰富与可维护性之间二选一;相反,通过弹簧参数 token、动效组件契约、降级策略矩阵的体系化建设,我们可以同时交付出充满物质感人人可用的产品。

未来的设计系统之争,大概率不在"用什么曲线",而在"怎样用最克制、最可访问、最接近原生的方式,让动效真正为理解服务"。这是 Jelly UI 复兴留给每一位前端工程师的命题,也是前端设计语言在新一轮周期的方向标。