基于GSAP+ScrollTrigger实现Canvas交替圆形滚动入场动画
实现Canvas圆形滚动交错淡入动画方案
要实现你需要的滚动触发交错淡入效果,核心思路是把每个圆形的状态(比如透明度)单独存储,通过GSAP控制这些状态并实时重绘Canvas,结合ScrollTrigger触发动画。以下是修改后的完整代码和关键说明:
完整代码实现
<!-- 先引入GSAP和ScrollTrigger库 --> <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script> <canvas class="circle-zazz"></canvas> <script> var HW = {}; var $$ = function (selector, parent) { return Array.prototype.slice.call((parent ? parent : document).querySelectorAll(selector)); }; var loop = function (arr, callback, method) { method = method || 'forEach'; return Array.prototype[method].call(arr, callback); }; HW.circMotif = function(canvas) { const ctx = canvas.getContext('2d'); const circleSize = 80; // 圆形尺寸 const padding = 3; // 圆形与方块的间距 const canvasWidth = window.innerWidth + 6; const numRows = 2; // 行数 // 计算方块尺寸 const squareSize = (canvasWidth - 2 * padding) / Math.ceil(canvasWidth / (circleSize + 2 * padding)); // 每行圆形数量 const numCirclesPerRow = Math.floor(canvasWidth / squareSize); const totalWidth = numCirclesPerRow * squareSize; const marginLeft = (canvasWidth - totalWidth) / 2; const canvasHeight = numRows * squareSize; // 设置画布尺寸 canvas.width = canvasWidth; canvas.height = canvasHeight; // 存储所有圆形的状态数据 const circles = []; // 生成每个圆形的基础数据 for (let row = 0; row < numRows; row++) { for (let col = 0; col < numCirclesPerRow; col++) { const x = marginLeft + col * squareSize; const y = row * squareSize; const isBlueCircle = ((row + col) % 2 === 0); circles.push({ squareX: x, squareY: y, squareSize: squareSize, squareColor: isBlueCircle ? 'blue' : 'white', circleX: x + squareSize / 2, circleY: y + squareSize / 2, circleColor: isBlueCircle ? 'white' : 'blue', circleRadius: circleSize / 2, opacity: 0 // 初始透明度为0 }); } } // 绘制函数:根据当前透明度重绘所有元素 const draw = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); circles.forEach(item => { // 绘制背景方块 ctx.fillStyle = item.squareColor; ctx.fillRect(item.squareX, item.squareY, item.squareSize, item.squareSize); // 绘制圆形(应用透明度) ctx.globalAlpha = item.opacity; ctx.beginPath(); ctx.arc(item.circleX, item.circleY, item.circleRadius, 0, 2 * Math.PI); ctx.fillStyle = item.circleColor; ctx.fill(); ctx.globalAlpha = 1; // 重置透明度,避免影响后续绘制 }); }; // 初始绘制一次(透明状态) draw(); // 拆分行数据:第一行正序,第二行倒序实现从右到左动画 const firstRow = circles.slice(0, numCirclesPerRow); const secondRow = circles.slice(numCirclesPerRow).reverse(); // 创建GSAP时间线+滚动触发 gsap.timeline({ scrollTrigger: { trigger: canvas, start: "top 80%", // 画布顶部到达视口80%位置时启动动画 toggleActions: "play none none reverse" // 滚动回去时反向播放动画 } }) // 第一行从左到右交错淡入 .to(firstRow, { opacity: 1, stagger: 0.1, // 每个圆形间隔0.1秒启动动画 onUpdate: draw // 每次状态更新时重绘Canvas }) // 第二行从右到左交错淡入,延迟0.2秒与第一行形成衔接 .to(secondRow, { opacity: 1, stagger: 0.1, onUpdate: draw }, 0.2); // 窗口 resize 时重新计算并初始化 window.addEventListener('resize', () => { // 清理旧的ScrollTrigger实例 ScrollTrigger.getAll().forEach(st => st.kill()); // 重新初始化 HW.circMotif(canvas); }); } HW.doCircMotif = function() { var $circleZazz = $$('.circle-zazz'); loop($circleZazz, function(el){ HW.circMotif(el); }) } HW.doCircMotif(); </script>
关键修改说明
- 状态数据存储:把每个圆形的位置、颜色、透明度等数据存入数组,不再一次性绘制所有元素,这样才能单独控制每个圆形的动画状态。
- 实时重绘机制:编写
draw函数,每次GSAP更新透明度后调用该函数重绘Canvas,确保动画帧实时更新。 - 滚动触发配置:通过
ScrollTrigger设置动画触发时机,toggleActions实现滚动返回时反向播放动画,恢复初始状态。 - 交错动画逻辑:第一行保持正序,第二行反转数组顺序后再设置stagger,实现从右到左的交错淡入效果,同时通过时间线偏移让两行动画形成自然衔接。
内容的提问来源于stack exchange,提问作者HandiworkNYC.com
相关产品推荐
相关产品推荐

