写真1枚を渡すだけで、スクロールに合わせて被写体がぬるぬる回る。あの表現の作り方を、素材の作り方から実装の考え方まで公開します。同じ仕組みで、プロダクト、美容室、飲食、旅館、ジュエリー、フラワーの6業種ぶんを実際に作りました。素材を差し替えているだけです。
この記事で手に入るもの。素材となるAI動画の作り方。1本あたりの実費まで書きます。動画を連番画像に変換して、スクロールで再生する仕組み。iPhoneで落ちないための設計。ここが一番の山です。そして、失敗した設計とその理由。
全体像はこうです。静止画を1枚用意し、AIで5秒の動画にし、全フレームを画像に切り出し、スクロール量に応じてその画像を順番にcanvasへ描く。それだけです。
動画タグを使わないのが肝です。videoをスクロールで制御すると、シークが追いつかずカクつきます。連番画像なら、スクロール位置と表示フレームが1対1で対応するので、指の動きにぴったり張り付きます。
手順1、素材の静止画を作る。画像生成AIで1枚作ります。i2v、つまり画像から動画にする工程で溶けない画作りには条件があります。背景は暗く被写体は中央。リムライトで輪郭に光を当てる。被写体の輪郭が背景から分離している。この3つを外すと次の動画化で被写体が溶けます。逆に守れば、ほぼ一発で回ります。サイズは1536×1024で作っておくと、後の切り出しで余裕が出ます。
手順2、AIで動画にする。i2vモデルに静止画とプロンプトを渡して5秒の動画を作ります。プロンプトの型は、カメラ固定、被写体のターンテーブル回転、背景照明は固定、カメラの揺れなし、5秒。この4点を英語で明示します。カメラが動くと、スクロールで戻したときに背景がぶれて安っぽくなります。実費は1本あたり0.25ドル前後。数十円でこの素材が手に入ります。
ここに大事な制約がひとつあります。使えるのは一方向の動きだけです。回る、寄る、開く。往復する動き、つまり揺れる、行って戻る、は使えません。スクロールを戻したときに動きの意味が壊れるからです。変える次元も1つに絞ってください。回しながら寄る、は破綻します。
手順3、全フレームを画像に切り出す。ffmpegで input.mp4 から frames の連番pngへ出力します。このとき fps_mode を passthrough に指定してください。24fpsの5秒で121枚になります。この指定を忘れるとフレームが間引かれ、スクロール量と絵がずれます。原因が分かりにくいので、ここは必須です。
手順4、AVIFに変換する。PC用とスマホ用の2セットを作ります。そのままPNGで並べると数十MBになり実用になりません。PC用は1280×720の品質55で121枚、スマホ用は768×432の品質50で61枚。実測でPC用が約960KB、スマホ用が約244KBに収まりました。ヒーロー1本ぶんとしては十分に軽い数字です。
実装、ここからが本番です。骨格は、CSSでステージを画面に固定し、その上をスクロールさせる形です。hero というセクションの高さを420vhにし、中のステージを position sticky、top 0、height 100vh にします。その中にcanvasを1枚置きます。
420vhという数字は、指の移動量と回転量の対比で決めています。短いと一瞬で回りきってしまい、長いとスクロールが重く感じます。画面高さ3枚から5枚ぶんが体感の落としどころです。
スクロールと再生の接続には、GSAPのScrollTriggerを使い、scrubを有効にします。トリガーをheroにし、開始をtop top、終了をbottom bottomにして、onUpdateの中で進捗を総フレーム数に掛けて四捨五入し、前回と違うフレームのときだけ描画します。scrubが肝です。アニメーションを再生するのではなく、スクロール位置に絵を追従させます。
iPhoneで落ちないための設計。ここが最重要です。121枚のImageBitmapを全部メモリに置くと、iOS Safariのメモリ上限、実測で約384MBに当たってページごと落ちます。対策は、圧縮データは全部持ち、展開したものは今見ている周辺だけ持つことです。
具体的には、圧縮blobは配列で全部保持します。展開済みのImageBitmapはMapに入れ、現在フレームの前後12枚、スマホは10枚だけを残します。窓の外に出たものはcloseして捨てます。ただし12枚ごとのキーフレームだけは常駐させます。高速スクロールで窓を飛び越えたときに真っ白にならないためです。近いキーフレームを暫定で描いてから、正しいフレームに差し替えます。
動かない環境への逃げ道も要ります。prefers-reduced-motionがreduce、通信節約モードが有効、createImageBitmapが非対応。このどれかに当たったら、静止画1枚に切り替えます。動きを止めてほしい人、通信を節約したい人、古い端末。この3つに配慮するだけでクレームはほぼ消えます。
やって失敗した設計も書いておきます。ひとつ目、videoタグをスクロールで制御する方式。再生位置をスクロールに合わせて動かします。デスクトップのChromeでは動きますが、iOSではシークが間に合わず、カクついたまま止まります。捨てました。ふたつ目、全フレームを先に展開しておく方式。デスクトップでは快適ですが、iPhoneでページごと落ちます。前述のスライディングウィンドウに作り替えました。みっつ目、往復する動きの素材を使う。戻したときに動きの意味が壊れます。よっつ目、ffmpegのフレーム間引きに気づかない。フレーム数が想定と変わり、スクロール量と絵がずれます。原因が分かりにくい厄介なバグでした。
素材を差し替えて使う手順は簡単です。自分の素材を手順1から4で作り、PC用とスマホ用のフレーム置き場を置き換え、総フレーム数の値を自分の枚数に合わせ、静止画のposterを1枚目の絵に差し替える。これだけで自分の被写体で動きます。
もう一段上げるなら、canvasの2D描画をWebGLに差し替えると、フィルムグレイン、スクロール速度に応じたRGBのずれ、クリックした位置から広がる波紋が乗ります。ただしWebGLは主題に合うときだけにしてください。全部にエフェクトを乗せると、ただの見せびらかしになります。動きは主題を運ぶためのものです。
最後に。この手法の良いところは、素材の実費が数十円で済むことです。撮影も、モデルも、スタジオも要りません。写真1枚から始まります。作ったものはそのまま自分の実績になります。まずは1本、手元の写真で試してみてください。