Smashing Magazine

Building Tactile UX: Honoring Intentional Design With Lottie

8.5内容质量
Building Tactile UX: Honoring Intentional Design With Lottie

TL;DR · AI 摘要

使用Lottie替代物理引擎实现精确动画控制,适合需要高度设计意图的交互场景。

核心要点

  • Lottie可实现181帧精准动画控制,替代Matter.js等物理引擎
  • 通过DOM事件与距离数学计算实现实时挤压反馈
  • Stress Release项目验证了非物理引擎方案的可行性

结构提纲

按章节快速跳转。

  1. 介绍数字减压玩具项目对动画控制的特殊需求

  2. 要求181帧精准动画序列替代物理引擎的随机运动

  3. 通过Lottie状态控制+DOM操作实现确定性动画

  4. 使用距离数学计算实现点击反馈与动画同步

  5. Stress Release项目证明方案有效性

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • Lottie动画控制方案
    • 设计需求
      • 181帧精确控制
      • 确定性动画
    • 技术实现
      • Lottie状态控制
      • DOM事件处理
      • 距离数学计算
    • 项目案例
      • Stress Release

金句 / Highlights

值得收藏与分享的关键句。

#Lottie#前端#动画#UX设计
打开原文

构建触觉 UX:用 Lottie 实现有意设计的交互 —— Smashing Magazine

  • 2026 年 8 月 11 日
  • 0 条评论

构建触觉 UX:用 Lottie 实现有意设计的交互

  • 9 分钟阅读
  • UX,设计,动画
  • 在 Twitter 上分享,在 LinkedIn 上分享

#### 关于作者

拥有 14 年经验,专精 API 开发、第三方工具集成和可扩展软件解决方案的 Alexey,擅长……更多关于 Alexey 的信息 ↬

#### 邮件通讯

每周前端与 UX 技巧。182,000+ 人信赖。

当需要构建高度互动的触觉网络体验时,架构必须服务于艺术方向。在本文中,Alexey Kopytin 解释了他们如何通过 Lottie 动画、DOM 事件和基于距离的数学计算,实现对设计师有意运动的绝对控制,从而构建数字减压捏捏乐游戏的架构设计原理。

本文由我们的挚友 Isadora Agency 慷慨支持,他们帮助企业品牌和市场领导者驾驭数字化转型、实现盈利增长,并推出令人难忘的网站、产品和营销活动。感谢你们!

当前端开发人员和 UX 工程师需要构建具有触觉感、弹跳感或破坏感的网页界面时,行业本能几乎总是相同:使用物理引擎。Matter.js、Cannon.js 或定制的 WebGL 解决方案已成为创建沉浸式、游戏化网站的黄金标准。

当 Isadora Agency 团队着手开发 Stress Release(一款专为疲惫创意人设计的数字减压捏捏乐,允许用户捏碎、拉伸和扭曲动画 UI 角色)时,我们最初也探索了这条路线。目标是构建一种高度触觉化的体验,每次点击都能产生令人满意的柔软反馈。

但随着原型开发的推进,我们意识到一个关键点:物理引擎能产生合理的运动,但在我们的情况下,动画师创造了有意的运动。

我们不需要角色像真实橡胶球一样在画布上无控制地弹跳。我们需要它们以非常具体、高度设计化的方式做出反应。因此,我们彻底放弃了物理引擎。

在本文中,我们将分解如何在不使用任何一行 WebGL 或 Matter.js 的情况下,通过完全依赖程序化的 Lottie 状态控制、DOM 操作和基于距离的数学计算,构建实时减压捏捏乐。

Stress Release 的角色货架向玩家介绍了系列减压玩具,每个玩具都由定制的 Lottie 动画状态驱动。(大图预览)

设计需求:有意运动

Stress Release 的核心需求是绝对的确定性控制。我们的动画师精心制作了定制的 .json Lottie 文件,需要精确的逐帧序列。

例如,我们的“超级捏压”反应需要精确的 181 帧预热,随后是特定的释放序列。为了尊重这一设计,我们需要一种架构,不会用算法近似覆盖动画师精心制作的关键帧。

点击反馈循环(点击 → 捏压 → 得分)越紧密,就越需要确定性的帧控制。通过选择使用 Lottie 原生 API 的程序化状态控制,我们确保交互层成为动画层的完美触发器。

角色卡片展示了专门为每个动画序列和交互状态设计的个性特征和视觉标识。(大型预览)

创建触觉反馈:将 DOM 元素映射到 Lottie 状态

由于我们的架构依赖于 Lottie 和标准 DOM,渲染直接由 Lottie 运行时处理,该运行时内部将基于 JSON 的矢量动画作为 SVG 渲染。我们通过 ID 和 CSS 类直接选择元素,结合 Lottie 动画片段、CSS 变换和点击事件数学计算来驱动它们的行为。

为了在击中角色时实现令人满意的“触觉感”,我们使用了径向输入映射。第一步是将点击位置从页面坐标转换为角色的局部坐标空间。

每次点击都会以角色的中心点为基准进行测量,然后转换为得分、反馈强度和爆炸位置:

code
// 角色在自身坐标空间中的中心点
var x_center = parseFloat($("#playChar").width()  / 2);
var y_center = parseFloat($("#playChar").height() / 2);

// 相对于角色左上角的点击位置
var offset = $("#playChar").offset(); // 文档相对位置
var X = parseFloat(e.pageX - offset.left);
var Y = parseFloat(e.pageY - offset.top);

// 从中心到点击点的向量
var a = parseFloat(X - x_center);
var b = parseFloat(Y - y_center);

然后我们使用勾股定理计算从点击中心到点击点的直线距离:

code
var distance = Math.hypot(a, b);

这个单一数值驱动了所有效果:得分、反馈强度以及爆炸动画的位置:

code
// 距离区域映射到得分奖励
if      (distance < 10)  givePts = 100; // 十字准星区域
else if (distance < 40)  givePts = getRndInteger(70, 90);
else if (distance < 70)  givePts = getRndInteger(40, 70);
else if (distance < 100) givePts = getRndInteger(20, 40);
else if (distance < 120) givePts = getRndInteger(10, 20);
else if (distance < 145) givePts = getRndInteger(1,  10);
else givePts = 0; // 未命中

// 将爆炸 Lottie 精确定位到点击点
var shiftPosition = window.innerWidth < 1023 ? -20 : 200;
$("#explosionChar").css({
  "margin-left": a + shiftPosition + "px",
  "margin-top":  b + shiftPosition + "px",
});

// 立即触发压缩动画
explosion.goToAndPlay(0);

最终效果是一个同心区域系统——围绕角色中心点形成完美的得分环,类似于飞镖盘。Lottie SVG 的视觉复杂度与命中检测完全无关;命中框始终是一个干净的圆形。关键的是,爆炸 Lottie 动画被重新定位到 (a, b)——与得分计算使用的相同向量,因此总是在玩家点击的确切位置出现。这种空间精度完全通过数学计算和 DOM 定位创造了“我击中了”的触觉感受。

基于距离的点击检测和同步的 Lottie 反应创造了物理击中角色的触觉感受。(大型预览)

由于游戏需要在特定时间点触发视觉反应,Lottie 通过其动画曲线内部处理所有挤压和弹跳的感知。每个角色都有定义好的动画片段集合(空闲循环、反应帧和结束状态),以帧范围形式存储。当点击发生时,我们会直接跳转到与当前游戏状态匹配的精确片段:

code
// 每个角色定义的动画片段帧范围
const play_segments = [{
  charId: 0,
  sections: {
    idle:     [0,  40],   // 空闲循环状态
    squeeze1: [41, 80],   // 轻度反应
    squeeze2: [81, 120],  // 中度反应
    squeeze3: [121, 160], // 重度反应
  },
  playOrder: ["squeeze1", "squeeze2", "squeeze3"],
  endAnimation: [161, 200]
}];

每次点击时,我们按播放顺序推进并触发下一个片段:

code
function stepAnim() {
  let p         = play_segments[0];
  let i         = p["playOrder"][curr_order_play];
  let playNow   = p["sections"][i];

  playChar.stop();                    // 立即停止当前片段
  playChar.loop = false;              // 不循环 - 仅播放一次后停止
  playChar.playSegments(playNow, true); // 跳转到精确帧,强制立即播放

  curr_order_play++;
  canPlayAnim = 0;                    // 动画中禁止其他点击

  if (curr_order_play > p["playOrder"].length - 1) {
    curr_order_play = 0;              // 循环回到序列开头
  }
}

当片段播放完成时,控制权会返回到空闲循环:

code
playChar.onComplete = function() {
  canPlayAnim = 1;          // 重新启用点击
  if (!playEnd) playIdleState();
};

function playIdleState() {
  playChar.playSegments([0, 40], true); // 返回空闲循环
  playChar.loop = true;
}

通过编程触发精确的 Lottie 动画片段,交互层可以对每个挤压和变形状态保持确定性控制。(大图预览)

对于最大挤压的蓄力效果,条形图会在特定帧范围循环,直到被触发:

code
// 循环"准备释放"帧直到玩家激活
indikL.loop = true;
indikL.playSegments([181, 302], true);

// 激活时 - 播放释放序列一次
indikL.loop = false;
indikL.playSegments([96, 396], true);
indikL.goToAndStop(0, true); // 完成后强制重置

DOM 元素的响应式优势

我们在架构决策中另一个重要因素是响应式行为。由于 Stress Release 是在 DOM 中构建的,我们绕过了在不同设备上缩放边界框和碰撞向量的复杂性。

我们完全通过 CSS 变量处理响应式调整。通过在每次调整大小时重新计算 CSS 自定义属性,布局会自动响应更新后的变量,Lottie SVG 在容器中自然缩放而不会丢失状态:

code
const appHeight = () => {
  const doc = document.documentElement;
  doc.style.setProperty("--doc-height", `${window.innerHeight}px`);
  doc.style.setProperty("--doc-width", `${doc.clientWidth}px`);
};
window.addEventListener("resize", appHeight);
appHeight(); // 初始化时立即运行

移动端性能优化:Lottie 的代价

虽然这种架构让我们对艺术方向有完全控制,但也带来了新的挑战:文件体积。

code
Lottie JSON 文件可能体积较大。我们有 21 个不同的角色动画,加上多个需要加载的爆炸变体。为确保体验保持流畅——尤其是在移动设备上——我们实施了若干激进的优化策略:

- 连接监控:我们使用 performance.now() 跟踪初始资源加载时间,以检测慢速连接并在加载时间超过 5 秒时进行标记。

- 顺序加载资源:我们没有同时初始化所有 21 个角色动画,而是使用 await 按对加载,仅在每对加载完成后才继续。这可防止低端设备上同时的网络请求和渲染工作造成浏览器阻塞。

- 激进的内存管理:我们没有将占用内存较大的爆炸动画保留在内存中,而是根据需要实时销毁并重新创建它们。这以微小的初始化成本换取了更低的空闲内存占用。

- 动态降低画质:画质降低是一个 API 调用,会在每个货架角色加载后立即应用。关键在于根据角色在场景中的作用应用不同的画质级别:

// 货架界面 - 21 个动画同时播放 shelf = lottie.loadAnimation({ container: document.getElementById("charShelf" + i), renderer: "svg", loop: true, autoplay: true, path: "assets/shelf/" + shelfFolders[i] + "/" + shelfFolders[i] + ".json", }); lottie.setQuality(0.5); // 50% 画质 - 减少插值计算 shelf.setSpeed(0.6); // 60% 速度 - 每秒减少帧计算量

// 播放界面 - 单个聚焦角色 playChar = lottie.loadAnimation({ container: document.getElementById("playChar"), renderer: "svg", loop: true, autoplay: true, path: chosenChar.url, }); lottie.setQuality(1); // 全画质 - 同时仅有一个动画

code

## 结论:为设计选择合适的技术

在确定游戏化网页体验的技术栈时,关键是要让设计需求主导技术选择。

“Mega Squeeze” 状态结合了分层动画序列、背景过渡和定时交互反馈,以增强冲击感。(大预览图)

由于我们的交互需要定制的、高度可控的视觉反应,我们选择了程序化状态控制而非涌现式模拟。这一决定使动画师能够完全掌控体验的触感,让代码专注于其最擅长的工作:监听、计算和触发。

> 通过将 Lottie 本机时间轴功能映射到 DOM,您可以在保持对艺术方向绝对控制的同时,提供极其丰富的触觉用户体验。

### 进一步资源

想亲自尝试实现,或在浏览器中体验实际效果?查看这些资源:

- 体验代码:我们在 CodePen 上准备了一个简化版演示示例,展示角色如何通过 playSegments() 对点击做出反应。

- 查看最终成果:访问 Stress Release 网站的实时版本,查看所有 21 个角色和优化策略的实际应用。

- 阅读文档:探索 Lottie Web 官方文档,了解我们使用的播放器控制功能。特别关注 loadAnimation()、playSegments()、setSpeed() 和 setQuality() —— 这四个方法构成了本文所述交互层的核心。

(yk)

探索更多内容