You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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>

关键修改说明

  1. 状态数据存储:把每个圆形的位置、颜色、透明度等数据存入数组,不再一次性绘制所有元素,这样才能单独控制每个圆形的动画状态。
  2. 实时重绘机制:编写draw函数,每次GSAP更新透明度后调用该函数重绘Canvas,确保动画帧实时更新。
  3. 滚动触发配置:通过ScrollTrigger设置动画触发时机,toggleActions实现滚动返回时反向播放动画,恢复初始状态。
  4. 交错动画逻辑:第一行保持正序,第二行反转数组顺序后再设置stagger,实现从右到左的交错淡入效果,同时通过时间线偏移让两行动画形成自然衔接。

内容的提问来源于stack exchange,提问作者HandiworkNYC.com

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 02:34:55