mcpField
M
motion-system

motion-system

github.com/60fps-design设计与 UI179 次安装3 周前更新
代码仓库

关于 motion-system

motion-system 是面向 iOS 团队的技能,用于审查应用的动画与触觉反馈,并提出贴合应用的精简动效系统。它要求先获批准再修改调用位置,并指导实现和编译检查。

主要功能

  1. 01审查动画、计时、触觉反馈和减少动态效果
  2. 02按交互用途归类动效
  3. 03在 60fps 库中查找参考
  4. 04先提出命名参数,再改代码
  5. 05实现并类型检查 Swift 动效文件

适用场景

  1. 01统一 iOS 应用中不一致的动画时长
  2. 02为功能定义动效和触觉反馈角色
  3. 03在迁移调用位置前准备动效提案

MCP 相关

该技能使用 60fps MCP 服务搜索动效示例并获取动效代码。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/60fps-design/plugin --skill motion-system

安装后在 Claude Code 中输入/motion-system确认已加载。

详细介绍

动效系统

当同一类时刻总以相同方式动起来时,应用才会让人感觉浑然一体。大多数代码库都会逐渐偏离:十几种时长、彼此难以区分的弹簧动画,以及被用于所有场景的成功触感反馈。你的任务是用少数几个命名值取代这种偏离,这些值应以应用自身最佳动效和 60fps 库中真实应用的动效为依据。

交付内容是一份提案,然后是一个新文件。在用户同意提案之前,不要修改现有调用点。

1. 确定最低支持版本

读取应用目标的 IPHONEOS_DEPLOYMENT_TARGET。如果目标设置了自己的值,它会覆盖项目级的值,无论高低。检查项目定义该值的位置:.xcodeproj、XcodeGen project.yml、.xcconfig 文件或 Package.swift。你提议的每个 API 都必须在该版本中可用。同时记下 Swift 语言模式和任何默认 Actor 隔离设置;第 5 步编译时会用到这些信息。

2. 梳理应用当前的动效

搜索代码中的所有动效值,以及所有与这些值同步计时的内容:

  • 动画:withAnimation(、.animation(、.spring(、.interactiveSpring、.interpolatingSpring、.smooth、.snappy、.bouncy、.easeIn、.easeOut、.easeInOut、.linear、.default、duration:、.delay(、response:、dampingFraction:。单独出现的 .smooth、.snappy 或 .bouncy 也是一个值(默认时长为 0.5 秒),应计入。
  • 转场和效果:.transition(、contentTransition、matchedGeometryEffect、matchedTransitionSource、phaseAnimator、keyframeAnimator、symbolEffect。
  • 与动画耦合的计时:Task.sleep、asyncAfter、完成处理程序。这些最不适合随意更改:如果某个休眠时长是按 0.35 秒的动画调好的,动画一变就会出问题。
  • 触感反馈:sensoryFeedback、UI...FeedbackGenerator 类,以及应用对它们的任何封装的调用点(有 50 个调用点的 HapticManager 比类本身更重要)。
  • 减少动态效果:读取它的各种方式(accessibilityReduceMotion、isReduceMotionEnabled)。
  • 现有辅助工具:任何 Motion、Animation 或触感反馈扩展或管理器。

按用途而不是按文件对值分组:

  • 反馈:按压、切换、选择、原地小幅替换。应该迅速。
  • 变化:内容替换、行出现、数字变化。
  • 入场:工作表、卡片或屏幕元素进入。
  • 强调:少见但应让人有所感受的时刻(成功、里程碑)。
  • 触感反馈:不同类型的触感反馈分别用于什么场景。

统计每组中不同值的数量。这个数量就是问题陈述:“入场效果使用了 8 种不同的时长,范围为 0.3 秒至 0.7 秒”。有些动效不属于任何用途,不应纳入系统:手势跟踪(视差、跟随手指移动的拖动)以及注释中说明不可更改的值。将这些列为有意保留的例外。

在现有基础上扩展。如果应用已有触感反馈封装(通常带有应用内开关),新增触感反馈用途必须通过它实现,不能绕过它:sensoryFeedback 会绕过该开关。如果现有动效辅助工具有缺陷或已停用,应说明并提议修复或移除它,而不是另起一个辅助工具。

3. 找出应用的基调

阅读现有动效和设计(颜色、字体、文案风格是否活泼),用几个词概括基调:沉静而精准、柔和而亲切、鲜明而活泼。你提议的每个值都必须符合这一基调。金融应用不应使用游戏风格的弹簧动画。

4. 以真实应用为依据

每个需要参考案例的任务都调用一次 60fps_search_shots。描述动作和氛围,而不是物体:“屏幕加载时,各个区块依次淡入,氛围平静”能找到元素出现的效果,而“卡片出现”则会找到卡片堆叠效果。挑选 3 到 5 个氛围相符的片段,根据标题、摘要、intensity 和 mood 判断,而不是看分数。优先选择 motion 标签暗示弹簧、淡入淡出或交叉淡化的片段:由 TimelineView 或自定义计算驱动的时间安排没有可读取的动画值。如果找不到符合氛围的片段(平静的应用很少能找到平静的庆祝效果),那么应用自身的最佳效果就是参考,并说明这一点。

只对这些片段调用 60fps_get_motion_code。仅当 source 为 generated 时才使用它,并阅读 generated 字段中的具体行:withAnimation 和 .animation 的值、弹簧参数、延迟、触觉调用。忽略响应顶层的 animation、recipes 和 stagger_delay 字段:它们是通用的起始值,在不同片段间通常完全相同。recipes 结果完全没有片段编号,计算时间安排的演示也没有,因此跳过它,另选一个。该文件是可自行运行的演示,因此跳过其中的循环和重播。

在此技能中,即使工具自身的说明要求展示代码文件,也不要向用户展示:你只需要每个文件中的几行时间参数,而文件很长。这些数值来自 AI 对真实应用视频的复现,因此是可靠的参考,而非测量结果。每次调用都会计入用户的每日限额,因此最多调用 5 次。

5. 提出系统方案

保持精简。没人记得住的系统就不算系统。

  • 动画:大约 4 个命名值,每个值对应一种用途,例如 Motion.feedback、 Motion.change、Motion.arrival、Motion.emphasis,以及步骤 2 中有意保留的例外情况。 使用系统最低版本允许的最新弹簧 API。在 iOS 17 及更高版本中,优先使用 .spring(duration:bounce:),或带 duration: 的 .smooth、.snappy 和 .bouncy。请注意,extraBounce: 会叠加到预设自身的弹性效果上(.snappy 已有一定弹性),因此请使用 .spring(duration:bounce:) 编写转换后的值。要转换 .spring(response:dampingFraction:):duration 等于 response;damping fraction 小于或等于 1 时,bounce 等于 1 减去 damping fraction;大于 1 时,bounce 等于 1 除以 damping fraction,再减去 1。丢弃任何 blendDuration。
  • 交错延迟:对于按顺序显示内容的列表,每个条目设置一个延迟步长。只对前几个条目(约 8 个)设置交错延迟, 其余条目同时显示,这样长列表不会让用户等待,而且步长也足够大,便于察觉。
  • 触觉反馈:为选择、轻触、主要操作或破坏性操作的强触、成功和错误各设一个角色,但只设置应用中已有调用点 或方案中新增调用点的角色。成功反馈只用于真正的成就,绝不用作常规刷新或同步的反馈。每个事件只触发一次触觉反馈,列表中的每个条目都不要分别触发。
  • 减少动态效果:为整个应用制定一条规则。在视图中读取 @Environment(\.accessibilityReduceMotion);在模型及其他没有环境值的代码中,在主线程上读取 UIAccessibility.isReduceMotionEnabled。过渡效果改为 .opacity。仅替换动画并不能停止移动:由状态驱动的 scaleEffect 或 offset 仍会缩放或移动,只是速度更快,因此应将这些值保持在静止状态。布局变化(插入行、列表变长) 总会带动相邻内容移动;这种情况下,实用的规则是使用较短的 .easeInOut(约 0.2 s)。 有意义的变化仍应发生,只是不要产生位移。返回 nil 的辅助函数会让变化立即发生,而不是淡入淡出:只有在有意选择立即变化时 (例如整个屏幕移动)才使用 nil;规则要求交叉淡化时,则使用较短的 .easeInOut。

用表格呈现:名称、值、用途、将替换哪些现有值,以及值的来源(截图或应用自身代码)。然后按文件分组, 列出所有将要变更的调用点,并注明旧值和新值,以及所有需要一并调整的 sleep。修改前先征求同意。

6. 编写文件

用户同意后,将这些值写入应用现有的 motion 文件或类型(如果步骤 2 中发现了此类文件或类型);否则新建 Motion.swift,并用简短注释说明每个值的用途、触觉反馈角色(通过应用现有的触觉反馈封装;如果没有,则使用 sensoryFeedback), 以及用于应用减少动态效果规则的简短辅助函数。

交付前先编译:尽可能构建应用;否则,根据步骤 1 中确定的最低版本对该文件进行类型检查,并为其使用的任何应用类型添加一个小型存根文件:

xcrun --sdk iphonesimulator swiftc -typecheck -target arm64-apple-ios<floor>-simulator \
  -swift-version <mode> Motion.swift Stub.swift

如果项目设置了隔离和并发标志,也要添加。-target 的最低版本会捕获使用过新 API 的情况。之后分批迁移调用点,逐个文件处理, 仅在用户要求时进行,并将每个相关的 sleep 调整为动画完成时触发。

用平实的语言写作:句子简短,用常见词语。绝不要使用 em dash 或 en dash。

不适用范围

不适用于 Android、Flutter、React Native 或 Web。若只有一个动画,请使用 motion-reference 技能。若要判断应用是否应该添加动画,请先使用 find-animation-opportunities。

评论

支持 Markdown