Communitygithub.com

GA-MO/iso

用纯手写 SVG + CSS/WAAPI 绘制动画等距产品插图,风格为高级开发者工具风格(圆角板块、层叠堆栈、带光脉冲的连线、标签、循环解释性动效),无需任何动画库。

iso 是什么?

GA-MO/iso 是一个用于生成动画等距解释性场景的开发者工具套件,专为打造高级开发者工具风格的产品插图而设计。它通过纯手写 SVG 和 Web Animations API (WAAPI) 实现动画,不依赖任何外部动画库,所有元素都是真实的 SVG 和 HTML 文本,可缩放并支持无障碍的 `prefers-reduced-motion`。该工具包包含场景构建器 `Scene`、运行时播放器 `runtime.ts` 以及一系列用于渲染、截图、性能测试和对比度检查的脚本。它提供了 64 个场景示例,涵盖简单到复杂的各种布局(如宽屏、对角、面板、径向、场、分解和物体),设计师可以从中选择最接近自己故事需求的示例进行参考和修改。此外,该工具还支持交互功能(如播放、拖动、探索),并强调了在手机端优先进行设计,提供了紧凑和宽屏两种响应式布局方案。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/GA-MO/isokit/tree/HEAD/skill

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

iso:动画等距解释器

一个场景用一段 6 到 14 秒的循环讲述一个产品故事:物体到达,光线在它们之间传播,每次只有一个部分亮起,循环回到第一帧。一个流程只有一种形状。从节拍表中选择故事所需的形状:一个打开以显示其层的盒子,两个对象之间的交接,一个逐渐填充的网格,一个旋转选择的堆栈,或者用户通过悬停来抬起的瓦片。一切都是真实的 SVG 和 HTML 文本,随其容器缩放,在屏幕外暂停,并为减少动效设置静止。

该工具包与此文件位于同一目录:

  • lib/iso.ts:Scene 构建器(投影、板块、面、连线、脉冲、标签、运动轨迹),scene.exportSvg(),responsive() 和 lazyBox()。
  • lib/runtime.ts:使用 WAAPI 播放渲染的场景,当 40%(或对于高场景,半个屏幕高度)进入视口时开始其介绍动画,在屏幕外暂停,当它离开超过半个屏幕时丢弃其动画,并在返回前将其恢复到同一帧,为减少动效设置静止,暴露 window.isoSeek(t)。
  • scripts/render.ts:将一个场景模块(默认导出 Page)渲染为一个独立的 HTML 文件,并内联运行时。
  • scripts/shots.mjs:在选定时间和宽度下截取任何页面的屏幕截图,并写入 sheet.png。
  • scripts/check.ts:对磁盘上任何位置的场景文件针对工具包进行严格的类型检查(bun scripts/check.ts scene.ts)。
  • scripts/page-idle.mjs:一次性滚动包含多个场景的页面,然后在 0%、20%、35%、50%、60% 和 80% 处停止,并在 CPU 为 1x 和 4x 时打印主线程时间(node scripts/page-idle.mjs page=<url> width=393 dpr=3 scroll=4)。在任何包含多于几个场景的页面上运行它。
  • scripts/perf.mjs:在 Chrome 中以手机宽度并控制 CPU 限制播放一个页面(node scripts/perf.mjs page=<out.html> width=393 throttle=4 seconds=20),并打印帧率、帧时间百分位数、延迟帧、长任务和主线程时间。在任何包含 30 个或更多运动对象的场景上运行它。
  • scripts/contrast.mjs,scripts/render-all.ts:在静止帧以及另外四个时刻,测量渲染页面中每个文本与其字形后方像素的对比度,低于 4.5:1 则失败(node scripts/contrast.mjs <out.html>;bun scripts/render-all.ts <dir> 首先写入所有目录示例)。在每个场景上运行它,然后才能称其为完成。
  • scripts/study-rive.mjs,scripts/compare-rive.mjs:可选工具,用于逐帧研究您自己的 .riv 动画,并将其与工具包场景进行比较。两者都需要 runtime=<folder with canvas_advanced.mjs and rive.wasm>。
  • examples/:64 个虚构产品的场景,每个文件导出 example(标题、故事、提示、级别、类别、布局、交互和 art:来自 art(compact, wide, breakpoint) 的紧凑和宽屏场景)以及默认的 Page,因此 scripts/render.ts 可以渲染其中任何一个。一个 spot 场景是一个单独的小场景,art(compact)。hero.ts 导出 hero(一个 Art),material-gallery.ts 导出 galleries(每个饰面系列一个 Art),每个都有默认的 Page。examples/shared.ts 包含表面、颜色渐变、paints、字体和 fitX(),它保持标签在其将放置的最窄卡片内。

在开始绘制之前,选择最接近您故事的示例并阅读它:

示例级别展示内容
notifications.ts简单最小文件:一个核心扇出到三个瓦片,脉冲,到达闪光和跳跃。请先阅读它
ci-deploy.ts简单一个包裹沿链条跳跃,落地时压缩,仅在末尾显示状态颜色
file-upload.ts简单块填充进度条,然后副本扩展到边缘点;空槽上显示行进虚线
db-branching.ts简单虚线幽灵变为实线,一个变化瓦片滑回并合并
service-map.ts简单悬停:九个包含 { hover } 的 slab() 瓦片,悬停或点击时显示标签,同时循环仍讲述故事
auth-flow.ts中等一个直立的 panel() 电话,文本输入,携带令牌的交接幽灵
etl-pipeline.ts中等源瓦片沿着连线馈送核心,连线随之绘制,然后是一个填充和清空网格;覆盖层与其所在的板块分组,以便一起移动
queue-workers.ts中等由事件表驱动的槽队列,构建验证该表(无溢出,不从空队列取出)
search-index.ts中等浅色页面上的线条艺术瓦片,键入的查询,匹配项高亮,排名结果上升
feature-flags.ts中等播放:标签中的真实 <button> 和内联脚本,通过 seek 重放一个循环步骤
agent-tools.ts复杂具有卫星的核心,呼叫和回复脉冲,一个输入答案的卡片
checkout-payment.ts复杂带虚线返回路径的步骤环,以及一个交叉淡入淡出的状态行
edge-cache.ts复杂相同请求两次:原始服务器上的冷未命中,然后边缘上的热命中
observability.ts复杂三种信号样式(虚线、点、跨度),带有 heightTo 的 prism 柱状图,警报和恢复
keycard-slot.ts, battery-charge.ts, tile-loader.ts, empty-box.ts点状用于功能列表、加载器或空状态的一个小对象;在插槽边缘裁剪的直立 panel() 卡片,heightTo 单元格,对角线下降波,行进虚线
sync-loop.ts, inbox-fill.ts, alert-stack.ts, table-scan.ts点状压缩到平面上的箭头,玻璃面板托盘,滑到堆栈上的卡片,带有计数并骑在顶部卡片上的徽章,逐行滑动的镜头
audit-ledger.ts, dns-resolve.ts, cron-jobs.ts简单带封条的堆叠页面,一次跳跃并回复,触发作业的拨盘
vault-rotate.ts, firewall-filter.ts, zero-trust.ts中等使用槽模式交换密钥,被拒绝的数据包弹回,每个请求都加盖戳记
workload-scheduler.ts, autoscale.ts, health-check.ts中等从小规则表进行放置,为新节点腾出空间,拉出并重新加入的节点
blue-green.ts, snapshot-restore.ts中等重新定向的连线,沿时间线向后滑动的光标
embedding-clusters.ts, training-loss.ts, recs-rerank.ts, ab-test.ts中等点聚集成簇,下降的 prism 柱状图,重新排序的槽架,带有赢家的拆分
data-erasure.ts, rag-answer.ts复杂一个请求扇出到六个系统并带有计数器,检索到的卡片飞入模型,答案输入
hero.ts复杂一个提示按 1、5、15 和 40 的层级增长系统:转盘旋转和加权下降,输出从下方滑出,弹出,带有网格填充的池子,无尽请求,静止时朝鼠标倾斜;通过 fill 和使用 <use> 的重复瓦片保持成本低廉;仅使用 SVG text(),因此 exportSvg() 可在其上工作
funnel-live.ts, billing-cycle.ts, parcel-delivery.ts, test-shards.ts中等,复杂layout: 'wide':从左到右阅读的 3:1 条带(逐渐变窄的阶段,无跳跃变化的月份条带,运送盒子的车辆,在车道中竞赛并合并的分片)
kanban-move.ts, stock-sync.ts, diff-merge.ts, refund-flow.ts简单到复杂layout: 'diagonal':沿一条等距轴移动的卡片或令牌,变为绿色的门,以第二种颜色沿着相同路径返回的退款
live-cursors.ts, two-approvals.ts, design-comments.ts, translate-six.ts简单,中等layout: 'panels':彼此面对的直立 panel();输入的光标,解锁锁的两个勾选标记,图钉和线程,六种语言的一条消息,带有泰语和日文字体堆栈
cms-publish.ts, sensor-gateway.ts, smart-home.ts, currency-convert.ts简单,中等layout: 'radial':一个副本到五个通道,读数输入然后地板波输出,设备依次改变状态,一个脉冲沿环线绕行一次
ota-rollout.ts, build-cache.ts, calendar-booking.ts, load-slider.ts简单到复杂layout: 'field':跨设备场的波浪,带有停止和重试,从依赖关系表计算缓存命中,跳过冲突的会议;load-slider 是一个拖动控件(真实的 range 输入,支持鼠标、触摸和按键),在环境流量保持移动的同时寻找循环
video-transcode.ts, image-resize.ts, fraud-gauge.ts, data-lineage.ts, drill-down.ts简单到复杂layout: 'exploded':向上分离并进行编码的演绎版,剥离的大小,超越警戒线的累积风险层,通过层向下照亮的上下游路径;drill-down 通过真实按钮,通过寻找该部分的循环段,将一部分打开为其层
materials-*.ts, material-gallery.ts六个系列(金属、玻璃、天然、科技图案、复古、生成式)的 49 种侧面饰面,作为 (scene) => Material 工厂,每个系列一个无标签的画廊。生成式饰面接受种子。复制一个饰面而不是发明一个

一次性设置:cd <此目录>/scripts && bun install && bunx playwright install webkit(截图使用已安装的 Google Chrome for Chromium)。类型检查:scripts/node_modules/.bin/tsc -p <此目录>。

1. 在绘制前进行故事板

首先打开 recipes.md。它包含所有测量的运动和构图模式(到达、连接、流、比较、选择、空闲、交互、缓动、表面),每个都带有其数字和工具包调用。从这些配方中构建故事,并保持它们的时序。

首先将故事编写为一个时间线表格:观众应该理解什么,展示它的 3 到 6 个节拍,以及对于每个节拍,哪个元素移动、如何移动、何时移动以及移动多长时间。每个节拍一个想法。需要一个段落来解释的节拍是两个节拍或错误的图片。

经过验证的节拍形状,带有测量的时序:

节拍发生了什么时序
到达碎片一个接一个地掉入,稍微过冲,然后稳定dropIn:每个 0.55 秒,83 毫秒交错,46 像素下落
交接瓦片的幽灵轮廓从一个对象滑到另一个对象4 个幽灵,间隔 0.22 秒,每个 1.2 秒,ease.io
旅行一个短光脉冲沿连线运行;目标在到达时闪烁270 像素/秒,脉冲约为连线的 0.12 到 0.16,上升 0.1 秒,淡出 0.6 秒
揭示层一个实心盒子变为透明;内层一次亮一个并带有标签开启 0.23 秒,保持约 1 秒,关闭 0.2 秒,下一个在前一个结束时开始
旋转选择一个堆栈向上移动一个槽位;活动卡片变为强调色每 3 秒移动 0.9 秒,慢速启动,长时间缓出
填充和清空一个网格逐个瓦片填充,保持,然后清空回空7 帧(0.12 秒)间隔进入,2 帧间隔退出
牵引当连线缩短或伸长时,它所连接的盒子会沿连线方向被拉动约 6 像素,保持,然后缓回,使连线看起来有张力拉动 0.23 秒,保持 0.25 秒,返回 0.65 秒,带有长时间缓出
悬停提升瓦片上升 16 像素并变为其品牌颜色,其名称出现slab({ hover }):270 毫秒开启,325 毫秒关闭,ease.out

连接规则:只有当连线的两端都在屏幕上时,连线才显示。连接到在循环中间到达的对象的连线会随其脉冲一起绘制自身(wire({ dash: '1 1' }),然后 at(id, 'strokeDashoffset', t, d, 1, 0, ease.linear)),并随其加入的对象一起离开。永远不要将这样的连线放在介绍动画中。连线永远不会穿过盒子的顶面:在它连接的对象之前添加它(以便它们绘制在它之上),并从盒子的边缘而不是中心开始。

循环规则:最后一帧等于第一帧。任何在循环期间发生变化的事物都必须在循环结束前变回原样。仅介绍动画的运动放在 intro() 中;循环在介绍动画结束时开始,并且介绍动画必须结束在与循环开始的相同画面中。没有介绍动画的元素会在介绍动画期间显示循环的起始值,即其循环段的最后一个 to。

绘制顺序是固定的:稍后添加的内容总是覆盖较早添加的内容。会改变谁覆盖谁的运动(例如,一张卡片从堆栈顶部移动到底部)将绘制为槽位。每个元素是一个移动一步的槽,并像其邻居一样淡出或重新着色,并且每个段都从槽的静止值重新开始,因此所有槽一起跳回,同时画面看起来相同。为每个槽位提供一个选项的标签,并交叉淡入淡出哪一标签显示在活动槽旁边。

2. 选择布局,然后首先为手机进行构图

每个示例在其 layout 中命名其构图:wide(一个从左到右阅读的长条;其宽屏场景约为 3:1,例如 1200x400,断点为 720,展示区将其显示为全宽卡片),diagonal(沿等距轴的长行),panels(彼此面对的直立 panel()),radial(一个核心,周围环绕着环上的物体),field(从高处看到的许多瓦片;稍陡的 ratio 有帮助),exploded(垂直向上分离的层)或 object(用于功能列表或空状态的一两个对象)。在绘制之前选择故事所需的布局;当场景变化时,页面阅读效果更好。

一个观众可以播放的场景(interaction: 'play' | 'drag' | 'explore')在 art() 的 head 中提供一个小型内联模块脚本:label({ html }) 中的真实 <button> 或 `<input type=

相关技能