IvyLabs Design BostonIvy 爬山虎 v0.10.0

动效

动效只用来说明三件事:东西从哪来、到哪去,以及状态变了。不做纯装饰的循环动画;计时器的呼吸点是唯一的例外,它表示「正在计时」。

时长和曲线按场景取,不在组件里写死数字:

基础 token

Token 值 用在
duration-instant 80ms 像素风格的状态切换、复选框。
duration-fast 120ms 悬停、按下、标签出现。
duration-base 200ms 面板展开、卡片移动。
duration-slow 320ms 模态和侧滑面板进入。
easing-standard cubic-bezier(0.2, 0, 0, 1) 青藤:默认缓动。
easing-enter cubic-bezier(0, 0, 0, 1) 元素进入。
easing-exit cubic-bezier(0.3, 0, 1, 1) 元素离开。
easing-pop cubic-bezier(0.34, 1.56, 0.64, 1) 贴纸:带一点回弹,用于完成勾选和贴纸落下。
easing-linear cubic-bezier(0, 0, 1, 1) 像素:不做缓动,只用 instant 时长。

场景

每格是「时长 曲线,位移,起始缩放」。CSS 变量是 --motion-<场景>(位移、缩放、逐行间隔另有 -shift、-scale、-stagger),Flutter 是 BiTransitions.<风格>.<场景>。

场景 名字 用在 青藤 萌虎 像素 减少动态效果时
悬停和按下 hover 悬停、按下、选中时的颜色、底色和描边变化。 120ms standard 120ms standard 80ms linear 照旧
菜单出现 popover-enter 菜单、日期选择、工具提示出现:淡入,朝打开方向移入 4px,缩放 0.97 → 1。 120ms enter,4px,0.97 120ms enter,4px,0.97 80ms linear 只淡入淡出
菜单消失 popover-exit 菜单、日期选择、工具提示消失:淡出,不位移。 80ms exit 80ms exit 80ms linear 只淡入淡出
对话框出现 dialog-enter 对话框出现:遮罩淡入,面板淡入并从 0.97 放大到 1。 200ms enter,0.97 200ms enter,0.97 80ms linear 只淡入淡出
对话框关闭 dialog-exit 对话框关闭:遮罩和面板一起淡出。 120ms exit 120ms exit 80ms linear 只淡入淡出
侧栏滑入 panel-enter 侧栏和任务详情面板滑入:从所在的一边移入整个宽度。 320ms enter,整宽 320ms enter,整宽 80ms linear 只淡入淡出
侧栏滑出 panel-exit 侧栏和任务详情面板滑出。 200ms exit,整宽 200ms exit,整宽 80ms linear 只淡入淡出
列表新增 list-insert 列表新增一行(新建任务、Agent 建的一批任务到达):高度展开,同时淡入并下移 4px。一批超过 6 行时,第 7 行起不再逐行出现,和第 6 行一起出现。 200ms standard,4px,逐行 24ms 200ms standard,4px,逐行 24ms 80ms linear 只淡入淡出
列表删除 list-remove 列表删除一行:先淡出,再用 list-move 的时长收起高度。 120ms exit 120ms exit 80ms linear 只淡入淡出
换位置 list-move 行或卡片换位置:排序、拖到另一列、完成后移到「已完成」分组。从旧位置移到新位置。 200ms standard 200ms pop 80ms linear 直接切换
展开和收起 expand 展开和收起:Agent 动态的改动列表、计划预览的子任务、评论的回复。高度变化,内容淡入;箭头旋转用 hover 的时长。 200ms standard 200ms standard 80ms linear 直接切换
轻提示出现 toast-enter 轻提示出现:从底部上移 8px 并淡入。 200ms enter,8px 200ms enter,8px 80ms linear 只淡入淡出
轻提示消失 toast-exit 轻提示消失:淡出。 120ms exit 120ms exit 80ms linear 只淡入淡出
完成任务 complete 完成任务:勾选框填色并画勾,标题加删除线;停 200ms 后用 list-move 移到「已完成」。 120ms standard 120ms pop 80ms linear 照旧

进入和退出

.my-popover { animation: bi-appear var(--motion-popover-enter) both;
  --bi-shift: calc(-1 * var(--motion-popover-enter-shift)); --bi-scale: var(--motion-popover-enter-scale); }
final t = BiTransitions.of(context).popoverEnter;
AnimatedSlide(duration: t.duration, curve: t.curve, offset: Offset(0, open ? 0 : -t.shift / height), child: menu);

列表:新增、删除、移动

展开和收起

计划预览的子任务、Agent 动态的改动列表、评论的回复,都用 expand:高度在 200ms 内变化,内容同时淡入;箭头图标的切换用 hover 的时长。Web 的 AgentActivity 和 Flutter 的 BiAgentActivity 已经这样做;客户端给计划预览加折叠时照同样的写法。

轻提示

出现时从底部上移 8px 并淡入(200ms),消失时淡出(120ms)。停留 5 秒,带操作(如「撤销」)时 8 秒;Web 在鼠标停留或获得焦点时暂停计时,Flutter 在读屏开着时不自动关掉带操作的轻提示。同一时间最多两条。

减少动态效果

系统打开「减少动态效果」(Web 读 prefers-reduced-motion,Flutter 读 MediaQuery.disableAnimations,对应 macOS 的「减弱动态效果」和 Android 的「移除动画」)时,每个场景按上表最后一列退化:

另外:计时器圆点不再闪烁,改为常亮;萌虎完成任务时的旋转和放大去掉;拖动卡片不再倾斜。这些都由变量和 BiTransitions.of 处理,组件里不要再自己判断系统设置、另写一套时长。

三种风格

风格 气质 写法
青藤 平稳、几乎察觉不到 按上表的时长和曲线
萌虎 有一点弹性和仪式感 完成任务、卡片落位用带回弹的 easing-pop,其余同青藤
像素 像终端一样干脆 所有场景 80ms 线性,不位移、不缩放、不逐行出现;计时器圆点用 steps(2) 闪烁