
Imgcook
Imgcook 动效开发入门:用状态、过渡与 Web Animations 实现交互动画
Imgcook 能从 Sketch、PSD 或静态图片一键生成前端代码,但生成出来的模块往往只是静态的。这篇教程讲清楚如何在 Imgcook 里拆分动画状态、用状态表达式控制尺寸与显隐、用 transition 与 transitionDelay 编排时序,以及在无法直接写 CSS keyframes 时借助 Element.prototype.animate 完成帧动画,并处理 onAppear 重复触发的问题。
Imgcook 是阿里巴巴推出的一款 SaaS 产品,它通过智能化技术,把 Sketch、PSD、静态图片等各类视觉稿一键转换成前端代码。多数人最初用它生成静态模块,但随着电商类设计稿越来越强调氛围滚动、Feed 商品动效切换这类动态表现,只产出静态结构就不够用了。这篇教程面向已经能把设计稿导入 Imgcook、拿到基础代码,但还不清楚如何让生成结果动起来的前端开发者,讲清楚一套可复用的动效实现路径。如果你只是想先了解这个工具的整体定位,可以看 Imgcook 的工具页;下面进入具体做法。
准备工作
在动手写动效之前,需要先确认几件事,否则后面调试会反复卡住。
- 可用的 Imgcook 账号与设计稿来源。Imgcook 支持从 Sketch、PSD 以及静态图片导入视觉稿。导入前尽量把图层结构整理干净,尤其是需要参与动画的元素,不要被无关图层包裹得太深。
- 明确目标运行框架。Imgcook 生成代码时可以面向不同的前端框架。素材中的示例涉及 Rax 生态的讨论,但最终采用的方案是基于 Web 标准 API 的,因此不绑定单一框架。选框架时要注意:某些动效能力依赖特定生态的插件,换框架就不可用,这一点后面会专门说明。
- 理解「状态」这个概念。Imgcook 的动效不是靠手写 CSS 类切换实现的,而是通过模块内部的状态对象驱动。你需要能在生成的代码里找到状态定义的位置,并知道如何用状态表达式去影响节点的样式属性。
- 准备好调试环境。素材中提到,Imgcook 当时并不支持在画布上直接预览动效,所以每次调整都要重新生成代码、在真实运行环境中查看效果。请提前把「改—生成—预览」这条链路跑通,减少单次迭代的摩擦。
- 确认版本与配额。Imgcook 的功能范围、可用框架、生成配额与付费方案会随时间调整,动手前请以官网当前信息为准,不要依赖旧教程里的描述。
操作步骤
第一步:把动画拆成状态
动效开发的第一步不是写代码,而是拆解。以素材中的一个实际模块为例,整个动画可以拆成三个阶段:
- 商品图以左上角为基准缩小,其余状态先隐藏,等待第一阶段结束。
- 「May Like」区域的渐变效果出现。
- 右侧推荐商品向左滑动到位。
拆解完之后,实现本身并不复杂,难点在于用 Imgcook 的表达方式把每个阶段串起来。拆解时有一个关键约束:需要平滑过渡的两张商品图必须放在同一个节点下,否则过渡会断裂,出现跳变。这一点在整理图层结构时就要处理好。
第二步:用状态对象区分不同阶段
在模块里定义一个状态字段来表示当前处于哪个阶段,例如用 state.recommend 承载不同状态值。后续所有样式变化都基于这个状态值来判断,而不是靠外部事件去直接改 DOM。
状态定义好之后,就可以通过 Imgcook 的状态表达式去修改图片的宽高。状态表达式的写法让节点属性跟随状态值变化,状态一变,尺寸随之改变,配合过渡属性就能得到平滑的缩放效果。
第三步:用过渡属性控制动画节奏
尺寸变化本身是瞬时的,要让它看起来像动画,需要设置过渡效果相关属性。Imgcook 允许在节点上配置动效属性,把过渡时长、缓动方式等参数写进去。素材中的示例把第一阶段的过渡时间设为 0.6s,这个数值后面会被第二阶段复用,所以要有意识地统一管理。
对于需要隐藏的组件,不必写复杂的显隐逻辑,直接通过 Imgcook 控制该节点是否渲染即可。不渲染的节点不会占据布局,也不会触发多余的事件,比用透明度隐藏更干净。
第四步:用 transitionDelay 编排时序
第二阶段要等第一阶段完成后再出现,难点在于延迟时间的设置。做法是给「May Like」区域设置 transitionDelay。素材中的示例把渐变动效的过渡时间设为 0.3s、延迟设为 0.6s,延迟值正好等于第一阶段的过渡时长,于是两个阶段自然衔接。
这里的原则是:后一阶段的延迟应当不小于前一阶段的过渡时长。如果前一阶段时长调整了,后一阶段的延迟要同步修改,否则会出现重叠或空档。
第五步:用 onAppear 事件改变透明度
渐变效果需要把组件透明度从 0 变为 1。这一步通过 View 组件的 onAppear 事件完成:在事件处理函数中接收事件参数,从中取出 DOM 元素(event.target),然后直接操作样式:
target.style.opacity = 1;
也可以改用状态更新的写法:
this.set({ recommendOpacity: 1 });
两种写法都能达到目的。直接操作 DOM 元素更简单直接,素材中的实现选择了这一种;用状态更新则更符合数据驱动的思路,便于和其他状态联动。按团队习惯选一种并保持一致即可。
第六步:用 Element.prototype.animate 实现帧动画
最后一个动画是右侧商品向左滑动。这个效果同样可以用过渡实现,但素材借它来演示更复杂的帧动画该怎么做。
这里有一个绕不开的问题:keyframes 需要定义在 CSS 或 style 标签里,而 Imgcook 生成代码的过程中并不能方便地直接注入 CSS。素材中尝试过几种方案:一是使用 Rax 生态的 rax-keyframes-to-animation,但它依赖 Rax 生态,无法迁移到其他框架;二是动态给模块添加 style 标签,虽然可行,但不够优雅。
最终采用的方案是 Element.prototype.animate。这是一个 Web 实验性能力,keyframes 在其中是一组等价的 Web API 配置,可以直接作用在每个 DOM 元素对象上,从而实现与 CSS keyframes 动画等价的效果。
先在 onAppear 事件中拿到右侧元素,然后在函数里调用:
const keyframes = [
{ transform: `translate3d(100%, 0, 0)` }, // from
{ transform: `translate3d(0, 0, 0)` } // to
];
target.animate(keyframes, {
delay: 600, // 延迟 600ms
duration: 1000, // 过渡时长
easing: 'ease', // 缓动函数
fill: 'forwards' // 动画结束后保持最后一帧的样式
});
参数含义逐项说明:delay 控制动画开始前的等待时间,这里设为 600ms,与前面阶段的节奏对齐;duration 是动画本身的持续时间;easing 决定速度曲线;fill: 'forwards' 让动画结束后元素停留在最后一帧,而不是弹回初始状态。
还有一个容易忽略的前提:元素的默认样式必须与 keyframes 的第一帧保持一致。也就是说,元素初始就应该处于 translate3d(100%, 0, 0) 的位置,否则动画开始的一瞬间会出现位置跳变。这一点需要在节点样式配置里提前设好。
第七步:处理 onAppear 的重复触发
用 onAppear 触发动画会带来一个副作用:当元素离开屏幕后再次出现时(例如 Feed 向下滚动再滚回来),onAppear 会再次触发,动画被重复执行。
解决办法是加一个标记位。定义一个 animStarted,初始值为 false 或 undefined,在事件处理函数里先判断:
if (animStarted) {
return;
}
target.animate(keyframes, {
delay: 600,
duration: 1000,
easing: 'ease',
fill: 'forwards'
});
animStarted = true;
如果标记为真,说明动效已经执行过,直接返回;否则调用 animate() 并在执行后把标记置为 true。这样就避免了重复触发。
一个完整示例
把上面的步骤串起来,一个包含三阶段动效的模块大致是这样组织的。以下代码展示结构与关键逻辑,具体节点名与状态字段名以你生成的结果为准。
// 1. 状态定义:用 recommend 表示当前阶段
state = {
recommend: 'stage1',
recommendOpacity: 0
};
// 2. 商品图节点:通过状态表达式控制宽高
// 两张商品图放在同一节点下,保证过渡平滑
// 过渡时长设为 0.6s
// 3. 其余组件:通过是否渲染控制显隐
// 4. May Like 区域:设置 transitionDelay 为 0.6s
// 过渡时长 0.3s,与第一阶段衔接
// 5. 透明度变化:在 View 的 onAppear 中处理
onAppear(event) {
const target = event.target;
target.style.opacity = 1;
}
// 6. 右侧商品滑动:帧动画 + 防重复触发
let animStarted = false;
onAppear(event) {
const target = event.target;
if (animStarted) {
return;
}
const keyframes = [
{ transform: `translate3d(100%, 0, 0)` },
{ transform: `translate3d(0, 0, 0)` }
];
target.animate(keyframes, {
delay: 600,
duration: 1000,
easing: 'ease',
fill: 'forwards'
});
animStarted = true;
}
验证顺序建议如下:先只保留第一阶段,确认商品图缩放平滑、其余组件正确隐藏;再加入 May Like 的延迟渐变,确认两个阶段首尾相接、没有重叠;最后加入右侧滑动,确认默认位置与第一帧一致、滚动回来不会重播。分阶段验证比一次性调完更容易定位问题。
注意事项
- 过渡与延迟的组合适合简单动效。素材的结论是:简单动效用
transition配合transitionDelay就够了;稍复杂的帧动画用animate实现。不要为了复杂而复杂。 - 延迟值依赖前一段时长。后一阶段的
transitionDelay与前一段的过渡时长是耦合的,改一处就要改另一处,建议把时长抽成统一变量或在注释里标明。 - 帧动画的初始样式必须对齐第一帧。否则动画启动瞬间会跳变,这是
animate方案最常见的坑。 fill: 'forwards'不能省。省略后动画结束会回到初始状态,滑动效果看起来像弹回去。onAppear会重复触发。凡是依赖它启动的动画,都要加标记位做幂等处理,否则列表滚动场景下会反复播放。Element.prototype.animate是 Web 实验性能力。素材中明确提到这一点,使用前应确认目标运行环境是否支持,并在不支持的环境里准备降级方案。- 不要依赖特定生态的插件。素材中提到的
rax-keyframes-to-animation依赖 Rax 生态,无法迁移到其他框架,跨框架项目应优先选择 Web 标准 API。 - 动态注入 style 标签虽然可行但不优雅。素材中把它列为备选方案,实际项目中应谨慎使用,避免样式管理失控。
- 画布预览能力有限。素材提到当时不支持在画布上预览动效,每次调整都要重新生成代码后在运行环境中调试,迭代成本较高,建议一次改多个参数再统一验证。
- 事件绑定缺少 once 语义。素材建议平台支持一次性事件绑定,这样开发者就不必额外维护状态来实现「只执行一次」的动作。在平台支持之前,仍需自行用标记位处理。
- 适用范围有边界。素材认为这套方法可以覆盖大多数电商场景的动效,但游戏类效果不在此列。
- 功能与配额以官网为准。Imgcook 支持的框架、生成能力、配额与付费方案可能调整,接入前请查阅官网当前信息。