Scroll Canvas
A <canvas> is redrawn each scroll frame via onUpdate(self) { draw(self.progress) } in the ScrollTrigger config. self.progress (0→1) is passed to the draw function to render the correct visual state. No playback animation — pure scroll-driven canvas rendering.
onUpdate(self) { draw(self.progress) }를 통해 매 스크롤 프레임마다 <canvas>를 다시 그립니다. self.progress(0→1)가 draw 함수에 전달되어 올바른 시각적 상태를 렌더링합니다. 재생 애니메이션 없이 순수 스크롤 기반 캔버스 렌더링입니다.
Source Code script.js
gsap.registerPlugin(ScrollTrigger);
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const progressText = document.getElementById('progressText');
function resize() {
canvas.width = canvas.offsetWidth * window.devicePixelRatio;
canvas.height = canvas.offsetHeight * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
}
resize();
window.addEventListener('resize', resize);
// Draw function — progress goes 0 → 1
// Morphs a grid of dots: small → large, scattered → grid
function draw(progress) {
const W = canvas.offsetWidth;
const H = canvas.offsetHeight;
ctx.clearRect(0, 0, W, H);
const cols = 12;
const rows = 8;
const cellW = W / cols;
const cellH = H / rows;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const i = r * cols + c;
const total = rows * cols;
const phase = i / total; // each dot has a phase offset
// dot reveals progressively
const dotProgress = Math.max(0, Math.min(1, (progress - phase * 0.5) * 2));
// base position
const bx = cellW * c + cellW / 2;
const by = cellH * r + cellH / 2;
// scatter offset shrinks as progress grows
const scatter = (1 - dotProgress) * 40;
const seed = (c * 13 + r * 7) % 20 - 10;
const x = bx + seed * scatter / 10;
const y = by + ((r * 3 + c * 2) % 20 - 10) * scatter / 10;
const maxR = Math.min(cellW, cellH) * 0.35;
const radius = dotProgress * maxR;
const alpha = dotProgress;
ctx.beginPath();
ctx.arc(x, y, Math.max(0, radius), 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
ctx.fill();
}
}
}
draw(0);
ScrollTrigger.create({
trigger: '.pin-wrap',
start: 'top top',
end: '+=2000',
pin: true,
scrub: true,
onUpdate(self) {
draw(self.progress);
progressText.textContent = Math.round(self.progress * 100) + '%';
},
});