AB
AiBoss站
教程

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 的功能范围、可用框架、生成配额与付费方案会随时间调整,动手前请以官网当前信息为准,不要依赖旧教程里的描述。

操作步骤

第一步:把动画拆成状态

动效开发的第一步不是写代码,而是拆解。以素材中的一个实际模块为例,整个动画可以拆成三个阶段:

  1. 商品图以左上角为基准缩小,其余状态先隐藏,等待第一阶段结束。
  2. 「May Like」区域的渐变效果出现。
  3. 右侧推荐商品向左滑动到位。

拆解完之后,实现本身并不复杂,难点在于用 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 支持的框架、生成能力、配额与付费方案可能调整,接入前请查阅官网当前信息。