Tips

スクロールに連動して動画(連番ファイル)を再生する方法(GSAP)
Appleのサイトなどでよく見かけるのギミックですが、GSAPで再現してみました。
GSAP(GreenSock Animation Platform)は、高性能かつ制御性の高いJavaScriptアニメーションライブラリで、DOM / SVG / Canvas / WebGL など幅広い描画対象を扱えます。動画を連番の.jpgに落とし込んでスクロール量に比例してシーケンスファイルを順に描画しています
CSS Transitionsなどウェブ標準にも選択肢があるなかで、GSAPのメリットはどのようなところにあるのでしょうか?
- CSS Transitions/AnimationsやWeb Animations APIより、GSAPのほうが制御の自由度が大きい。
- 連続したモーションの管理に、GSAPが役立つ。
- HTMLのDOMだけではなく、WebGL/Canvasの実装でもGSAPが利用できる
CSS Transitionsだけで実現できるような演出ではGSAPを使う必要はありません。また、BootstrapやMaterial DesignなどのCSSフレームワーク・デザインシステムにモーションが組み込まれているケースでも、GSAPは不要です。
「特別なウェブサイトを目指したい」「演出にこだわりたい」「オリジナルの動きを作りたい」といったケースでGSAPが役立ちます。
デモ
HTML
<section>
<h1>Scroll Down</h1>
</section>
<section class="sequence">
<canvas id="sequence-canvas"></canvas>
</section>
<section>
<h1>Next Section</h1>
</section>CSS
body {
margin: 0;
background: #fff;
color: #333;
font-family: sans-serif;
}
section {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.sequence {
position: relative;
height: 100vh;
width: 100vw;
overflow: hidden;
}
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}GSAP
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>Javascript
gsap.registerPlugin(ScrollTrigger);
const frameCount = 100;
const images = [];
const canvas = document.getElementById("sequence-canvas");
const context = canvas.getContext("2d");
// 画像ファイルのパスを指定(例: seq_0001.jpg ~ seq_0100.jpg)
const currentFrame = index => `/images/seq_${String(index + 1).padStart(3, '0')}.jpg`;
// Canvasサイズを画面にフィット
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
render();
}
window.addEventListener("resize", resizeCanvas);
// 画像をプリロード
for (let i = 0; i < frameCount; i++) {
const img = new Image();
img.src = currentFrame(i);
images.push(img);
}
const imageSeq = { frame: 0 };
gsap.to(imageSeq, {
frame: frameCount - 1,
snap: "frame",
ease: "none",
scrollTrigger: {
trigger: ".sequence",
start: "top top",
end: "+=3000", // 再生に必要なスクロール量
scrub: true,
pin: true,
},
onUpdate: render
});
images[0].onload = render;
function render() {
const img = images[imageSeq.frame];
if (!img) return;
const scale = Math.max(canvas.width / img.width, canvas.height / img.height);
const x = (canvas.width / 2) - (img.width / 2) * scale;
const y = (canvas.height / 2) - (img.height / 2) * scale;
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(img, x, y, img.width * scale, img.height * scale);
}
resizeCanvas();
コメントを残す