☰
TextFold平滑缩放是怎么做到的:FLIP动画、关键帧预布局与zoombench帧级实测
2026/10/30 2:21:29 网站建设 项目流程

【免费下载链接】textfold

Fold papers, web pages, files, and AI replies into five levels of detail. Zoom from an overview to the original text.

项目地址:https://gitcode.com/gh_mirrors/te/textfold
点击查看免费下载

TextFold 是一个把论文、网页、文件和 AI 回复折叠成五个详细程度级别的阅读工具,支持从主题概览一路放大到原文。很多读者第一次体验时都会问同一个问题:用滚轮、双指捏合或缩放条去"缩放"一篇文档,为什么文字会平滑地滑动、缩放、淡入淡出,而不是生硬地重新排版?

这篇文章带你拆解TextFold 平滑缩放背后的三层实现:用FLIP 动画完成"逐词变形"、用关键帧预布局让缩放过程零布局开销,再用zoombench从屏幕帧级实测平滑度,作为每次改动的回归门槛。

一、五个级别:缩放是一个"位置",不是一次"跳转" 🎚️

TextFold 把任意文档折成五档:

级别名称你看到什么
4Full原文,每个字
3Brief作者的 1–3 句原话
2Key每个要点的结论和理由
1Takeaway每个章节的一句话
0Topic全文变成章节主题地图

在实现上,滚轮、捏合、滑条并不是"跳到下一个级别",而是移动一个连续的缩放位置(4 = Full … 0 = Topic)。你可以停在半路、随时折返;手一停,位置就会弹性地吸附到最近的级别。这一点在 textfold/web/reader.js 开头的注释里写得很直白:zoom as a position, not a jump。

二、FLIP 动画:逐词变形的原理 🔍

先说结论:TextFold 阅读器里,每一个可见的部分——词、公式、圆点、标题、图——都是一个带data-id的元素,这个 id 在所有级别之间保持不变。级别切换时,它做的事情就是标准的 FLIP(First, Last, Invert, Play):

  1. First:切换前,对屏幕附近的元素拍一张"快照"——记录每个部分的位置和外观。快照只量视口附近的东西(一篇完整论文有约 2 万个这样的小块),并且按"容器 + 类名"缓存计算样式,避免反复触发样式计算,见 snapshot();
  2. Last:把内容重新渲染成新级别;
  3. Invert:对每个"幸存"的部分,算出它从旧位置到新位置、从旧大小到新大小的差值;
  4. Play:用 Web Animations API 给每个元素挂一个transform: translate() scale()的动画,让它滑回自己的新位置,消失的部分则由一个"幽灵副本"在原地淡出。

核心动画构建在 buildMotion(),一次完整的定时变形由 morph() 串起来。FLIP 整段代码就在 textfold/web/reader.js 里。

下面这段动图就是 FLIP 的效果:级别切换时,词和公式沿着连续的路径滑动、缩放,而不是整页刷新:

三、关键帧预布局:一个"不做任何布局"的舞台 🎬

FLIP 解决的是"切换那一瞬间"的动画,但还有一个更棘手的问题:每跨越一个级别,屏幕附近的内容就要重新排版一次(普通论文上约 30–70 ms)。这个停顿,手放在滚轮和滑条上能明显感觉到卡顿。

TextFold 的解法叫 "the stage"(舞台),源码注释在 reader.js,核心思想非常巧妙:

  • 空闲时预布局:当读者停下来不动时,程序利用每一个空闲瞬间,在一个隐藏的sandbox(沙盒)里,把当前可见片段在每一个级别各排版一次,并把每个可见部分的"定位副本"存下来——一共5 张关键帧。预热的调度逻辑在 warm(),一次只排一个级别,绝不抢占交互。
  • 相邻级别预建"配对":两张关键帧之间,每个部分要么整体平移缩放(move)、要么淡出(out)、要么淡入(in)、要么换字体时交叉淡化(x)。把运动方式相同的几百个词合并成一组,整组只有一个 GPU 图层、一条动画——密集论文从"几千个词各跑各的"变成"几百组一起动"。配对构建在 buildPair(),注释里还留着一个真实教训:不分组时,某篇论文一半的帧都被丢掉了。
  • 缩放时只放片,不排版:缩放一触发,页面被舞台盖住(页面保持原样、不再重绘),每一帧都只是移动和淡入淡出副本——包括跨越级别的那几帧,全程零布局。舞台的准入在 stageBegin(),逐帧推进在 stageFrame()。
  • 静止时换回真页面:缩放停在某个级别时,页面按该级别关键帧的样子画出真身,在同一帧内接管舞台的位置,观感上完全无缝,见 stageCommit()。

两个工程细节值得一提:沙盒和舞台都放在一个封闭的 shadow tree里——几千个副本的增删移不在文档里,浏览器插件(翻译器、广告拦截)就无须处理它们,实测证明留在页面里会让页面"冻结数秒";而当关键帧还没就绪(新开的页面、跨度过大的跳跃)时,缩放会回退到第二节说的实时 FLIP 动画,保证任何时候都有得放。

四、zoombench:以屏幕视角的帧级实测 📈

"感觉流畅"不是指标。TextFold 把平滑度当规则——项目文档 docs/DEVELOPMENT.md 里有一节专门叫Smoothness is a rule:任何改动都不许让缩放变得更不流畅,改之前和改之后都必须实测。

测量工具是 tools/zoombench.py。它为什么这么设计,文件开头的注释讲得很清楚:

  • 用 Playwright 驱动 Chromium,开 Mac 的 GPU(Metal)、2 倍像素密度,模拟真实读者的屏幕;
  • 在每个版本页面上重放同样的手势:捏合放大、捏合缩小、拖拽级别条、点击 Full;
  • 然后读Chrome 自己的帧 trace:实际呈现的帧数、丢帧数、最长的帧间隔(统计逻辑见 stats())。

为什么不用requestAnimationFrame量帧间隔?因为默认无头浏览器是软件渲染、1 倍像素,只看得见主线程——v0.8 版本在那个环境里"每一帧都在 17 ms 以内、完美无缺",却在真实 GPU 2 倍屏上每 5 帧丢 1 帧,而后者才是读者真正感觉到的。

运行方式(在密集论文 2512.23916 上对比各版本):

python3 -m textfold export 2512.23916 python3 tools/zoombench.py 2512.23916

输出类似:pinch out ->L3: {'ms': 1760, 'presented': 105, 'dropped': 0, 'drop%': 0, 'worst gaps ms': [18, 17, 17, 17]}——丢帧数和最长卡顿一目了然。

五、小结:三层机制各管一段

  • FLIP 动画:级别离散跳转时的逐词变形,靠data-id贯穿五个级别,量旧位、排新版、反转变形、滑回原位;
  • 关键帧预布局 + 舞台:连续缩放时的零布局体验,空闲时把五个级别全部预排好,缩放时只移动 GPU 上的副本;
  • zoombench 帧级实测:用真实 GPU、真实屏幕帧 trace 把"平滑"变成一个可回归的硬指标。

想继续深挖,入口是 textfold/web/reader.js(渲染 + FLIP 变形 + 舞台)和 tools/zoombench.py(平滑度实测);项目文档在 docs/DEVELOPMENT.md 的Smoothness is a rule一节。

【免费下载链接】textfold

Fold papers, web pages, files, and AI replies into five levels of detail. Zoom from an overview to the original text.

项目地址:https://gitcode.com/gh_mirrors/te/textfold
点击查看免费下载

相关推荐

上一篇:《The Book of Shaders》开篇导读:Fragment Shader(片段着色器)并行渲染原理与完整学习路径
下一篇:深色系架构图生成技能 architecture-diagram:Claude Skill 设计规范与实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询