Smart Ticker - 支持任意字符的高性能文本差异动画滚动组件

前言

最近在做一个AI配置的组件,突然有了这个做动画的简单初始想法

例如: 当我选择 GPT-5.2 mini 去替换 GPT-5.1 时能不能前缀 GPT-5. 不动后,面的 2 mini 替换 1 执行一个动画

捣鼓了一段时间,于是Smart Ticker 诞生了

官网有很多场景演示 DemoSmart Ticker Live Preview
GitHub仓库,详情见READMEtombcato/smart-ticker

Gif有点掉帧,详情见官网

欢迎去 GitHub 点个 Star 🌟,或者在评论区提 Issue!

为什么做 Smart Ticker?

市面上其实已经有不少数字滚动库了,为什么还要造这个轮子?最大的痛点是:它们都不支持中文、Emoji 或者混排替换。

特性 / 库 Smart Ticker 🚀 Odometer.js React CountUp Number Flow
核心机制 Levenshtein Diff (智能最短路径) 数值轮播 (0-9 循环) 数值插值 (数字跳动) 布局过渡 (Layout transition)
字符支持 全字符集 (中英文/Emoji/符号/数字) 仅数字 仅数字 主要是数字
动画效果 精准点对点 (只动变化的字符) 滚轮式 (必须滚过中间数字) 只有数字在变,无位移 现代且平滑
中文/全角适配 自动宽窄字符适配 容易挤压或乱码 N/A 需手动处理
多框架支持 React + Vue 3 (同构核心) JS Wrapper React 多框架
动画中断 平滑流体过渡 重置或生硬跳转 平滑 平滑

最大的区别在于:大多数库是把内容当成"数字"处理,而 Smart Ticker 是把内容当成 "字符串" 处理。这意味着你可以用它来滚动的不仅仅是金额,还可以是用户名、状态文本等等。

核心难题:如何让动画"聪明"起来?

如果你直接把 1234 变成 1254,最笨的办法是把 3 销毁,创建 5。但我们想要的是:只有 3 变成 5,其他数字保持静止。

如果字符串长度不一样呢?比如 Smart 变成 Start

  • 笨办法:5 个字母全换。
  • 聪明办法S, a, r, t 不动,把中间的 m 删了,插入一个 t?不对,是 m 变成了 t

这本质上是一个 Diff 问题 。我们需要找到从"旧字符串"到"新字符串"的最短编辑路径

算法引入:Levenshtein Distance

这里我引入了 Levenshtein Distance(编辑距离) 算法的变种。通常我们用它来计算两个字符串相似度,但在这里,我用它来生成操作队列

通过动态规划矩阵,我们可以计算出每一个字符应该执行什么动作:

  1. SAME (不变):位置不变,UI 保持静止。
  2. MODIFY (修改) :字符变了(如 3 -> 5),执行滚动动画。
  3. INSERT (插入):新串变长,执行"挤入"动画。
  4. DELETE (删除):新串变短,执行"挤出"动画。

代码实现

Core 模块中,我构建了一个 (N+1) x (M+1) 的矩阵,回溯计算最优路径:

typescript 复制代码
// 简化的回溯逻辑
while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && oldStr[i-1] === newStr[j-1]) {
        // 字符相等 -> SAME
        actions.unshift({ type: 'SAME', char: oldStr[i-1] });
        i--; j--;
    } else if (costMatrix[i][j] === costMatrix[i-1][j-1] + 1) {
        // 替换操作 -> MODIFY (Scroll)
        actions.unshift({ type: 'MODIFY', from: oldStr[i-1], to: newStr[j-1] });
        i--; j--;
    } else if (/* ... */) {
        // 处理 INSERT / DELETE
    }
}

正是因为有了这个算法,Smart Ticker 才能做到:

  • 100 -> 105 :只有 5 在动。
  • Downloading... -> Done:智能识别哪些字母保留,哪些字母滚走。

挑战二:不仅是数字,中英字母Emoji我全都要!

搞定了算法,渲染层又是一个坑。 中文字符(全角)和英文字符(半角)宽度不一样。如果强行用等宽字体,中文会被挤成一团或者英文间距过大。

我也遇到过这种"灾难现场",中文挤在一起根本没法看。

解决方案: 我实现了一个 charWidth 动态计算机制。

  1. 检测字符类型:通过 Unicode 范围检测当前字符是半角还是全角(或者是 Emoji)。
  2. 动态基准宽度
    • 半角字符(数字/英文):基准宽 0.8em(紧凑美观)。
    • 全角字符(中文):基准宽 1.25em(防止重叠)。
    • Emoji:特殊处理,确保不被截断。

这样一来,无论输 BTC $98,000 还是 哈基米 🐱,排版都稳如老狗。

挑战三:双框架支持 (React + Vue)

有些库只有 React 版,我的AI配置组件项目是基于 Vue。为了雨露均沾,我采用 "Core + Adapter" 的架构设计。

  • src/core/: 纯 TypeScript 实现 Diff 算法、字符列表生成、缓动函数。不依赖任何框架。
  • src/components/Ticker.tsx : React 封装层,使用 useRefRAF 管理动画。
  • src/components/vue/Ticker.vue : Vue 3 封装层,使用 watchrequestAnimationFrame

结果:两套组件 API 完全一致,维护起来也极其轻松。

最终效果 & 如何使用

现在,你只需要一行代码就能用上这个"聪明"的组件:

安装:

bash 复制代码
npm install @tombcato/smart-ticker

React 使用:

tsx 复制代码
import { Ticker } from '@tombcato/smart-ticker';
import '@tombcato/smart-ticker/style.css';

<Ticker value="98,456.32" />

Vue 使用:

vue 复制代码
<script setup>
import { Ticker } from '@tombcato/smart-ticker/vue';
import '@tombcato/smart-ticker/style.css';
</script>

<template>
  <Ticker value="🚀 发射成功" />
</template>

总结

这就是一个从小灵感到落地实现的过程,坚持走下去从实现小的想法到实现大的想法,如果阿祖你能看到这里请帮我点个star,感谢感谢!

相关推荐
开开心心就好1 小时前
批量提取PDF中的图片,直接导出原图
前端·javascript·支持向量机·智能手机·pdf·html·启发式算法
Setsuna_F_Seiei4 小时前
前端转型 Agent 开发 05 之 Agent Hooks 与 Checkpointer(让 Agent 从全自动转变人为可掌控)
前端·agent·ai编程
百万蹄蹄向前冲5 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙5 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan7 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
狗狗狗狗狗乐啊7 小时前
搭一个 AI 对话工作台 AChat:从 0 到可用的完整记录(一)
python·react.js·ai编程
kyriewen7 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理8 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
+VX:Fegn08958 小时前
计算机毕业设计|基于springboot + vue旅游管理系统(源码+数据库+文档)
java·开发语言·vue.js·spring boot·课程设计
雪芽蓝域zzs8 小时前
第 7 章:双 Y 轴组合图(柱状 + 折线,看板高频场景)
vue.js·echars