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

请求指导:如何实现滚动时图片沿曲线轨迹的动画效果

实现方案

核心思路

  • 监听scroll事件,计算滚动进度(当前滚动距离 / 页面可滚动总距离)
  • 利用数学公式(贝塞尔曲线、三角函数)计算元素在不同进度下的坐标,替代直接操作SVG path的复杂逻辑
  • 通过CSS transform 实现平滑移动,结合opacity控制元素显示/隐藏

示例代码

HTML结构

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>滚动轨迹动画</title>
    <style>
        body {
            margin: 0;
            height: 300vh; /* 足够长的滚动页面 */
            position: relative;
            background: #f0f0f0;
        }

        .curve-img, .circle-img {
            position: fixed;
            width: 80px;
            height: 80px;
            border-radius: 8px;
            transition: opacity 0.3s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 14px;
        }

        .curve-img {
            background: #4285F4;
            opacity: 1;
        }

        .circle-img {
            background: #EA4335;
            opacity: 0; /* 初始隐藏 */
        }
    </style>
</head>
<body>
    <div class="curve-img">曲线移动</div>
    <div class="circle-img">环形移动</div>

    <script>
        const curveImg = document.querySelector('.curve-img');
        const circleImg = document.querySelector('.circle-img');
        const bodyHeight = document.body.scrollHeight - window.innerHeight;

        // 曲线轨迹:二次贝塞尔曲线,可自定义起点、控制点、终点
        function getCurvePosition(progress) {
            const startX = 100;
            const startY = 100;
            const controlX = 500;
            const controlY = 300;
            const endX = 900;
            const endY = 100;

            // 二次贝塞尔公式:B(t) = (1-t)²P0 + 2(1-t)tP1 + t²P2
            const x = Math.pow(1 - progress, 2) * startX + 2 * (1 - progress) * progress * controlX + Math.pow(progress, 2) * endX;
            const y = Math.pow(1 - progress, 2) * startY + 2 * (1 - progress) * progress * controlY + Math.pow(progress, 2) * endY;
            return {x, y};
        }

        // 环形轨迹:自定义圆心、半径,从底部开始顺时针转一圈
        function getCirclePosition(progress) {
            const centerX = 600;
            const centerY = 400;
            const radius = 150;
            // 角度从π(底部)到3π(转一圈回到底部)
            const angle = Math.PI + progress * 2 * Math.PI;
            const x = centerX + radius * Math.cos(angle);
            const y = centerY + radius * Math.sin(angle);
            return {x, y};
        }

        // 滚动监听逻辑,加入性能优化
        let ticking = false;
        window.addEventListener('scroll', () => {
            if (!ticking) {
                requestAnimationFrame(() => {
                    const scrollProgress = window.scrollY / bodyHeight;

                    // 处理曲线移动元素
                    const curvePos = getCurvePosition(scrollProgress);
                    curveImg.style.transform = `translate(${curvePos.x}px, ${curvePos.y}px)`;

                    // 处理环形移动元素:滚动进度超过0.2时显示,0.2-0.8区间完成环形运动
                    if (scrollProgress > 0.2) {
                        circleImg.style.opacity = 1;
                        const circleProgress = Math.min((scrollProgress - 0.2) / 0.6, 1);
                        const circlePos = getCirclePosition(circleProgress);
                        circleImg.style.transform = `translate(${circlePos.x}px, ${circlePos.y}px)`;
                    } else {
                        circleImg.style.opacity = 0;
                    }

                    ticking = false;
                });
                ticking = true;
            }
        });

        // 初始化触发一次,避免初始位置错误
        window.dispatchEvent(new Event('scroll'));
    </script>
</body>
</html>

关键说明

  • 曲线轨迹调整:修改getCurvePosition里的startX/Y、controlX/Y、endX/Y参数,就能自定义曲线的形状和走向
  • 环形轨迹调整:修改getCirclePosition里的centerX/Y(圆心)、radius(半径),或者调整angle的起始值,就能改变环形的位置和转动方向
  • 动画区间控制:如果想让动画在特定滚动段触发,比如只在页面中间1/3部分运动,修改scrollProgress的映射逻辑即可(示例中环形动画绑定在20%-80%的滚动区间)
  • 性能优化:用requestAnimationFrame包裹滚动回调,避免频繁触发重绘,解决滚动卡顿问题

关于SVG Path的补充

如果之前尝试SVG的<animateMotion>失败,核心问题是SVG动画默认是时间驱动,而非滚动驱动。若一定要用SVG实现,需要把滚动进度绑定到动画的progress属性,但逻辑比JS计算坐标更复杂,上面的方案更直观、易调试。

内容的提问来源于stack exchange,提问作者Ariel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:50:20