gsap-canvas について
gsap-canvas は、GSAP のアニメーションを Canvas に描画するフロントエンド開発者向けスキルです。通常の JavaScript オブジェクトをアニメーションさせ、Canvas 2D API で描画する方法を、粒子、操作、リサイズ、性能の例とともに説明します。
主な機能
- 01GSAP によるオブジェクトアニメーションと描画
- 02粒子システムのレシピ
- 03再生・一時停止の操作
- 04Canvas のリサイズ処理
- 05描画性能のヒント
活用シーン
- 01Canvas の粒子アニメーション作成
- 02GSAP アニメーションの Canvas 描画
- 03Canvas シーンへの再生操作の追加
- 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 要素に直接触れることはありません。代わりに、次のようにします。
- 数値プロパティ(x、y、scale、rotation、alpha)を持つプレーンな JS オブジェクトを作成します。
- GSAP のタイムラインまたはトゥイーンでオブジェクトをアニメーションさせ、GSAP に数値を補間させます。
- タイムラインに
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 レイヤーに昇格させ、合成コストを削減します |
save/restore より ctx.resetTransform() を優先する | スタックへの push/pop のオーバーヘッドがなく、より高速です |
| オフスクリーン canvas に事前描画する | 静的なスプライトの場合は、OffscreenCanvas に一度描画し、その後はそこから drawImage します |
参考資料
references/canvas-patterns.md— 完全な実装:パーティクルの旋回システム、canvas モーフ(MorphSVG を canvas にレンダリング)