
【JavaScript】『GSAP × canvas』画像で連番アニメーションをスクロール制御する方法
こんにちは!今回は「スクロールに合わせてcanvas上で連番画像が切り替わるアニメーション」を紹介します!
See the Pen
Untitled by ryotom (@ryotam-smoke)
on CodePen.
• canvas に画像を1枚ずつ描画しながら、スクロール位置に応じて画像を切り替えていく仕組みです。
• 背景が固定されて、ユーザーの操作で映像が進むような体験が作れます!
HTMLとCSS部分のコードのポイント
- <canvas id="image-sequence"></canvas>
- <div class="spacer"></div>
- canvas {
- position: fixed;
- top: 0;
- left: 0;
- width: 100vw;
- height: 100vh;
- display: block;
- }
- .spacer {
- height: 5000px;
- }
• canvas は画面いっぱいに固定表示(position: fixed)。
• .spacer はスクロール領域を作るためのダミー(高さ5000px)。
• body はスクロール可能にするための背景とマージン調整。
JavaScriptとGSAPの処理
仮画像の読み込み
- const currentFrame = index =>
- `https://placehold.co/800x600/222/fff?text=Frame+${index.toString().padStart(2, "0")}`;
placehold.co を使って「Frame 01」「Frame 02」…の画像URLを自動生成します!
画像の事前ロード
- for (let i = 0; i < frameCount; i++) {
- const img = new Image();
- img.src = currentFrame(i);
- images.push(img);
- }
連番画像を全部読み込んで images[] に保存しておきます。
canvas に描画する処理
- function render() {
- const img = images[Math.floor(imageSeq.frame)];
- if (img && img.complete) {
- context.clearRect(0, 0, canvas.width, canvas.height);
- const scale = Math.min(
- canvas.width / img.width,
- canvas.height / img.height
- );
- const x = (canvas.width - img.width * scale) / 2;
- const y = (canvas.height - img.height * scale) / 2;
- context.drawImage(img, x, y, img.width * scale, img.height * scale);
- }
- }
画像をcanvasの中央に等倍縮小して描画する処理です。
スクロールのたびに呼び出されるので毎フレーム描画!
ScrollTriggerで連動させる
- gsap.to(imageSeq, {
- frame: frameCount - 1,
- snap: "frame",
- ease: "none",
- scrollTrigger: {
- scrub: 1,
- pin: true,
- trigger: canvas,
- start: "top top",
- end: "5000",
- },
- onUpdate: render,
- });
プロパティ | 意味・効果 |
scrub: 1 | スクロールにアニメーションを1秒ディレイ付きで滑らかに同期させる。 数字を指定すると、遅延の秒数になる。 |
pin: true | スクロール中、要素(今回はcanvas)をその場に固定(ピン留め)して動かなくする。 |
trigger: canvas | アニメーションの開始トリガーになる要素。ここでは<canvas>が対象。 |
start: “top top” | トリガー要素の上端がビューポートの上端に来たときに開始。 |
end: “5000” | スクロール距離の終点をピクセルで指定。ここでは5000pxスクロールで終了。 |
snap: “frame” | 画像の切り替えを整数(frame単位)でスナップさせ、ガタつきを防止。 |
onUpdate: render | アニメーションの進行ごとにrender()を実行して、canvasを描き直す。 |
本番画像で使うなら?
- const currentFrame = index =>
- `./images/frame_${index.toString().padStart(4, "0")}.jpg`;
結果 → ./images/frame_0000.jpg, frame_0001.jpg, …, frame_0059.jpg
もし画像ファイル名が「frame1.jpg」「frame2.jpg」みたいにゼロ埋めなしであれば、.padStart() を外してもOKです!
まとめ
このテクニックを使えば、以下のような演出が簡単にできます:
• 動画風のスクロールエフェクト
• 製品の分解アニメーション
• グラフィックノベルやインタラクティブアート