settle 是做什么的?
将诸如“一个按起来手感很棒的开关”或“展示待办事项应用的循环动画”这样的请求,转化为一个带有物理质感动效的成品组件。其动效工艺在所有组件上保持一致;而外观则每次根据请求独立创作,或继承自用户的项目。
以下路径均相对于此文件所在的文件夹。请从工作文件夹(项目的根目录,或组件所在的文件夹)运行每个脚本,使用其完整路径,如 node <此文件夹>/scripts/deal.mjs …:脚本会将生成的文件写入您运行命令的位置。
请求处理流程
- 阅读上下文。 确定您是否处于一个项目中:一个包含框架信息的
package.json文件,或一个包含 HTML 页面的文件夹。如果在一个项目中,注意其编写样式的方式、其设计令牌或主题设置的外观,以及它已使用的动效库。然后阅读请求:判断它属于references/kinds/中的哪种结构,是一个演示品还是产品的一部分,以及其模糊的措辞想要什么,这些措辞的含义由references/words.md翻译。 - 创作或继承外观。
- 一个有自己外观的项目会保留其外观:将该外观映射到样式表的变量上,并仅使用
--keep palette,type创作其缺失的部分。 - 否则,创作外观:运行
node scripts/deal.mjs --brief "<一行描述的请求>" --kind <control, data-view, composite 或 flow>,并带上references/words.md根据请求措辞提供的标志,如果处于项目中则加上--project,以便后续组件继承该外观。构建外观 1,即创作好的外观:--pick 1。仅当外观 1 与主题冲突时才选用外观 2 或 3,并说明冲突原因:--pick 2 --because "早餐菜单的深色背景"。references/tokens.md包含了相关规则。 - 创作好的外观有一个方向,它决定了组件的视觉风格;请求则设定其形式。阅读其在
references/directions.md中的章节。每个方向在章节顶部,位于简要描述之前。
- 一个有自己外观的项目会保留其外观:将该外观映射到样式表的变量上,并仅使用
- 撰写简要说明或故事板。
- 对于控件、数据视图或复合组件,用两行陈述简要说明并直接构建:说明它是什么、该方向如何适用于此主题、外观是什么、标志性时刻是什么以及您选择的默认值。“一个学习网站的番茄钟。结构:计时器采用厨房计时器的表盘设计,表圈上刻有分钟刻度;外观 1,浅灰色和黑色;标志性时刻:开始按钮变成倒计时进度条;25 分钟工作,5 分钟休息。”
- 对于随时间变化的流程,或可能有两种截然不同含义的请求,请展示故事板(
references/kinds/flow.md中有格式)并等待确认。 - 在构建之前,设想任何类似请求会得到什么结果。修改简要说明中与设想结果匹配的部分。
- 在组件所在位置进行构建。
- 没有项目时: 基于
assets/page.html的单个 HTML 文件,或者assets/reel.html用于动画短片。它包含样式表、方向的页面规则、完整粘贴到 head 模块脚本中的内核 (assets/kernel.js),以及references/motion.md中该组件所需的配方。 - 在一个项目中: 使用项目框架和样式的组件,内核作为模块复制一份,样式表放入项目的全局样式表中。
references/stacks/为每个技术栈提供一个说明。 - 为已有组件添加动效: 保留其标记和外观,并为其每次状态变化赋予动效语法所命名的运动(
references/craft.md,第 3 节),通过门控实现。 references/kinds/中的结构文件,会根据每个部分变化类型,说明应从哪个配方开始。
- 没有项目时: 基于
- 检查它。 对文件进行 lint 检查,并使用 snap.mjs 按照
references/review.md的描述截取帧,可以针对文件或项目的开发服务器。修复联系表中看起来不对劲的地方,然后再次截取,最多进行三轮。对于表现不佳的检查,例如拖拽无法吸附或服务器无法启动,给予两次尝试机会;然后将其标记为“未检查”后进行移交。 - 移交(见下文)。
需要阅读的内容
| 文件 | 何时阅读 |
|---|---|
references/craft.md | 每次构建:每个运动遵循的规则、禁用列表和注意事项 |
references/tokens.md | 每次构建:外观如何被创作或继承,以及如何使用 |
references/directions.md | 每次构建(无项目):创作方向的对应章节 |
references/motion.md | 每次构建:样式表、基础页面和配方,每种动效一个 |
references/kinds/control.md, data-view.md, composite.md, flow.md | 组件的结构:其基本结构和应起始的配方 |
references/stacks/html.md, react-next.md, vue.md, svelte.md, astro.md | 组件所处的位置:独立文件使用 html.md,否则使用项目技术栈的说明 |
references/words.md | 请求描述了一种感觉,例如“让它更突出” |
references/review.md | 检查步骤 |
硬性规则
- 组件放在用户工作的地方。 在项目中:使用项目的框架和样式,不引入新包,并使用项目自身的动效库(如果有的话),同时应用 settle 的计时。没有项目时:一个自包含的 HTML 文件,内核已粘贴进去,其唯一的外部请求是外观所需的字体链接;并且必须能在系统字体下正常工作。按需获取的照片放在其旁边。
- 请求设定形式。 控件保持其真实尺寸,小部件保持为一个卡片或面板,只有请求明确要求全屏时才会使用全屏。对于请求完整落地页或网站的请求,仅构建其中的动效组件,例如应用的幻灯片,使其可插入页面,然后将页面设计工作移交给页面设计技能。组件周围的背景保持简洁。
- 外观来源于样式表和方向。 每个视觉值都来自样式表的变量或其色调,包含一种背景色、一种文字色和一或两种强调色。创作的方向决定了组件的视觉风格,深色背景用于发生在黑暗中的主题。
- 动效来自内核。 每个响应用户的动作和每个状态变化,都使用
settle.track、settle.stretch或settle.move来执行,并配合外观寄存器中的预设settle.register()。 - 时间来自
settle.now()。 事件处理器设定目标;一个requestAnimationFrame循环根据当前时间绘制所有运动中的样式。snap.mjs 会停止时钟以检查帧。 - 形状延续。 每个新状态都是从屏幕上已有的形状构建的,变化的文本会经历短暂模糊,新容器会从背后已有的容器中打开。
- 手动拖拽优先。 被拖拽的值精确地停在指针下;超出限制后则遵循橡皮筋效果;释放时保留手部的速度。
- 真实内容。 使用合理且具体的示例,在一个流程中动作保持其名称。照片来自用户,或者当用户要求真实照片且没有提供时,从
scripts/photo.mjs获取,并在组件中注明来源;否则仅用代码绘制代码能清晰绘制的内容,绝不绘制风景(references/directions.md,图片部分)。 - 一个标志性时刻,且其周围的一切都保持安静。
- 除非请求指明,否则禁用:
references/craft.md中禁用列表的所有项目,lint 工具会标记它们。 - 为所有人设计。 支持指针、触摸和键盘操作,具有可见的焦点状态和 44 像素的交互目标。适应 360 像素宽的屏幕,最小字号不低于 11 像素;在减少动效模式下,其弹簧效果会立即停止。
- 成本意识。 每次工具调用都会消耗用户的配额。构建一次,最多检查三轮,给予表现不佳的检查两次尝试机会,并且不要调试脚本本身:在移交时说明哪些项目未经检查。
演示品或产品
演示品,例如用于落地页的动画短片,会获得完整的动效。产品内部的控件,随着用户看到它的频率增加,其动效会减少:每天使用上百次的组件没有动效,每天使用几十次的组件使用快速而轻微的效果。references/craft.md 中有相关表格(Emil Kowalski 的频率规则)。
文件存放位置
没有项目时,在工作文件夹下,根据主题使用简短名称(如 tea-timer.html)创建文件,除非用户指定了路径。在项目中,按照技术栈说明中的位置放置,使用项目自身的命名风格,并确保用户可以打开它:如果请求未指定,则放在一个独立的页面或路由上。将组件中可调整的数值集中放在脚本顶部:包括数据,以及寄存器未设定的任何计时。
移交
回复时包含:
- 组件的位置,如何打开它(文件,或路由及启动开发服务器的命令),以及一行说明它是什么及其标志性时刻。
- 两到三个值得尝试的操作:“将旋钮拖过末端”,“按 Tab,然后按方向键”。
- 两个可微调的参数,以脚本顶部的变量名给出。
- 当有代码绘制的占位图时,说明用户照片的放置位置:应将哪个元素替换为用户提供的
<img>。 - 有哪些内容未经检查,例如在真实手机上的 Safari 浏览器中的表现。
- 提供重新创作部分外观的选项(
node scripts/deal.mjs --redeal palette),对于模糊的请求,提供一个带三个变体的切换器。
对于动画短片,提供视频导出选项:node scripts/export.mjs reel.html --out reel.mp4(或 --gif)使用 ffmpeg 渲染它,平均每个帧四个子帧以实现运动模糊。
脚本
| 脚本 | 功能 |
|---|---|
scripts/deal.mjs --brief "…" [--tone …] [--register …] [--dark] [--project] | 创作三个外观;--pick <n> 打印选定的样式表(并使用 --project 将其保存为项目的外观);--redeal <part> 重新创作某一部分;--check 检查库 |
scripts/lint.mjs <file> | 对独立文件进行静态检查;FAIL 时返回退出码 1 |
scripts/snap.mjs <file or url> --out <dir> --sheet [--size WxH] [--times …] [--do "…"] [--calm] | 在停止的时钟上截取帧,带有真实输入和联系表,然后执行其标题中列出的已定稿页面的检查 |
scripts/export.mjs <reel> [--out reel.mp4] [--gif] | 将动画短片导出为 MP4 或 GIF,带有四子帧的运动模糊 |
scripts/photo.mjs "<展示内容>" [--out photo.jpg] | 获取一张开放许可的照片(CC0、公共领域或 CC BY),保存到运行位置,并提供用于组件的署名信息;仅当用户要求真实照片时使用 |
它们需要 Node 22 或更高版本以及一个 Chromium 浏览器:snap.mjs 会在常用位置找到 Chrome、Chromium、Edge 或 Brave,其他浏览器通过 CHROME 环境变量指定。当找不到任何浏览器时,它会用一行说明;请对照 references/review.md 审查代码,并将帧标记为未经检查。导出还需要 ffmpeg,photo.mjs 需要网络连接。无需安装任何其他东西。
无 Shell 环境
当无法运行脚本时,例如在无法使用 shell 的聊天环境中:读取 library/directions.json、library/palettes.json 和 library/type.json,保留其种类和色调与请求匹配的条目,并取请求字符长度在环绕每个列表的位置对应的条目,以便不同的请求落在不同的外观上。使用相同的文件进行构建,对照 references/review.md 中的评估标准审查代码,并在移交时说明帧未被检查。
致谢
- [Emil Kowalski's skills](https://github.com/emil
…