mcpField
G
gsap-canvas

gsap-canvas

github.com/iotron设计与 UI938 次安装31 周前更新
代码仓库

关于 gsap-canvas

gsap-canvas 是面向前端开发者的 GSAP Canvas 动画技能,说明如何用普通 JavaScript 对象承载动画状态,再通过 Canvas 2D API 绘制。涵盖粒子动画、控制、缩放处理和性能技巧。

主要功能

  1. 01GSAP 对象动画与 Canvas 绘制
  2. 02粒子系统制作方法
  3. 03播放与暂停控制
  4. 04画布缩放处理
  5. 05渲染性能建议

适用场景

  1. 01制作 Canvas 粒子动画
  2. 02将 GSAP 动画绘制到画布
  3. 03为画布动画添加播放控制
  4. 04优化逐帧绘制流程

安装

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

$npx skills add https://github.com/iotron/gsap-cookbook --skill gsap-canvas

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

附带文件references/canvas-patterns.md

详细介绍

GSAP Canvas — 渲染模式

流程:gsap-setup → gsap-canvas → gsap-optimise → gsap-test

配套内容:如需查阅 GSAP 核心 API 参考,请调用 gsap-core。如需了解时间轴排序,请调用 gsap-timeline。本技能仅涵盖 canvas 渲染方案。需要:greensock/gsap-skills


1. 模式

使用 GSAP 执行 Canvas 动画与 DOM 动画有本质区别。GSAP 不会直接操作 DOM 元素,而是:

  1. 创建带有数值属性的普通 JS 对象(x、y、scale、rotation、alpha)。
  2. 使用 GSAP 时间轴或补间动画这些对象——GSAP 会对这些数值进行插值。
  3. 为时间轴附加 onUpdate 回调,读取对象的当前值,并使用 Canvas 2D API 绘制到 canvas 上。
Plain objects  →  GSAP tweens numbers  →  onUpdate draws to canvas
     ↑                                          ↓
  { x, y, scale }                      ctx.drawImage(img, x, y, w*scale, h*scale)

这意味着 GSAP 负责缓动、交错、重复、往返和时间轴排序,而整个渲染流程完全由你掌控。

// Minimal example: animate a circle across canvas
const dot = { x: 0, y: 250, radius: 20 }

gsap.to(dot, {
  x: 800, duration: 2, ease: 'power2.inOut',
  onUpdate() {
    ctx.clearRect(0, 0, cw, ch)
    ctx.beginPath()
    ctx.arc(dot.x, dot.y, dot.radius, 0, Math.PI * 2)
    ctx.fill()
  },
})

2. 粒子系统方案

让一组带有精灵图的粒子向内绕行。GSAP 负责所有运动;onUpdate 在每一帧重新绘制。

设置粒子数组
const particles = Array.from({ length: 99 }, (_, i) => ({
  x: 0, y: 0, scale: 0, rotate: 0,
  img: Object.assign(new Image(), {
    src: `https://assets.codepen.io/16327/flair-${2 + (i % 21)}.png`,
  }),
}))
构建时间轴
const radius = Math.max(cw, ch)

const tl = gsap.timeline({ onUpdate: draw })
  .fromTo(particles, {
    x: (i) => {
      const angle = (i / particles.length) * Math.PI * 2 - Math.PI / 2
      return Math.cos(angle * 10) * radius
    },
    y: (i) => {
      const angle = (i / particles.length) * Math.PI * 2 - Math.PI / 2
      return Math.sin(angle * 10) * radius
    },
    scale: 1.1,
    rotate: 0,
  }, {
    duration: 5, ease: 'sine',
    x: 0, y: 0, scale: 0, rotate: -3,
    stagger: { each: -0.05, repeat: -1 },
  }, 0)
  .seek(99) // jump ahead so repeating stagger is mid-flow
绘制函数
function draw() {
  particles.sort((a, b) => a.scale - b.scale) // z-sort by scale
  ctx.clearRect(0, 0, cw, ch)
  particles.forEach((p) => {
    ctx.translate(cw / 2, ch / 2)
    ctx.rotate(p.rotate)
    ctx.drawImage(p.img, p.x, p.y, p.img.width * p.scale, p.img.height * p.scale)
    ctx.resetTransform()
  })
}

3. 控制与调整大小

通过 timeScale 切换播放/暂停
canvas.addEventListener('pointerup', () => {
  gsap.to(tl, {
    timeScale: tl.isActive() ? 0 : 1, // smooth ease to pause/play
  })
})
使用 invalidate 的尺寸调整处理程序
window.addEventListener('resize', () => {
  cw = canvas.width = innerWidth
  ch = canvas.height = innerHeight
  radius = Math.max(cw, ch)
  tl.invalidate() // recalculates functional from-values on next render
})

invalidate() 会强制 GSAP 使用更新后的尺寸重新计算基于函数的值(即三角函数计算)。


4. 性能提示

提示原因
使用 gsap.ticker.add(fn) 执行渲染循环与 GSAP 内部的 rAF 同步——共用一个帧预算,避免重复绘制
尽量减少状态更改批量调用 translate/rotate;每个粒子只调用一次 resetTransform()
谨慎排序每帧调用 particles.sort() 的复杂度为 O(n log n)——如果 z 顺序固定,就跳过
在 <canvas> 上使用 will-change: transform提升至 GPU 图层,降低合成开销
优先使用 ctx.resetTransform(),而不是 save/restore速度更快——避免栈推入/弹出的开销
预渲染到离屏 canvas对于静态精灵图,只需绘制一次到 OffscreenCanvas,之后从中调用 drawImage

参考资料

  • references/canvas-patterns.md — 完整实现:粒子绕行系统、canvas 形变(将 MorphSVG 渲染到 canvas)

评论

支持 Markdown