HTML鼠标移动的波浪线动画——页面将会初始化一个Canvas元素,并使用JavaScript代码在Canvas上绘制响应鼠标移动的波浪线动画

代码如下

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Wave Animation</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        class Wave {
            constructor(e) {
                this.phase = e.phase || 0;
                this.offset = e.offset || 0;
                this.frequency = e.frequency || 0.001;
                this.amplitude = e.amplitude || 1;
            }

            update() {
                this.phase += this.frequency;
                return this.offset + Math.sin(this.phase) * this.amplitude;
            }
        }

        class Node {
            constructor() {
                this.x = 0;
                this.y = 0;
                this.vy = 0;
                this.vx = 0;
            }
        }

        class Line {
            constructor(e, pos) {
                this.spring = e.spring + 0.1 * Math.random() - 0.05;
                this.friction = E.friction + 0.01 * Math.random() - 0.005;
                this.nodes = [];
                this.pos = pos; 

                for (let i = 0; i < E.size; i++) {
                    const t = new Node();
                    t.x = this.pos.x;
                    t.y = this.pos.y;
                    this.nodes.push(t);
                }
            }

            update() {
                let spring = this.spring;
                let node = this.nodes[0];

                node.vx += (this.pos.x - node.x) * spring;
                node.vy += (this.pos.y - node.y) * spring;

                let prevNode;
                for (let i = 0; i < this.nodes.length; i++) {
                    node = this.nodes[i];

                    if (i > 0) {
                        prevNode = this.nodes[i - 1];
                        node.vx += (prevNode.x - node.x) * spring;
                        node.vy += (prevNode.y - node.y) * spring;
                        node.vx += prevNode.vx * E.dampening;
                        node.vy += prevNode.vy * E.dampening;
                    }

                    node.vx *= this.friction;
                    node.vy *= this.friction;
                    node.x += node.vx;
                    node.y += node.vy;
                    spring *= E.tension;
                }
            }

            draw(ctx) {
                let currNode,
                    nextNode,
                    x = this.nodes[0].x,
                    y = this.nodes[0].y;

                ctx.beginPath();
                ctx.moveTo(x, y);

                let i;
                for (i = 1; i < this.nodes.length - 2; i++) {
                    currNode = this.nodes[i];
                    nextNode = this.nodes[i + 1];
                    x = 0.5 * (currNode.x + nextNode.x);
                    y = 0.5 * (currNode.y + nextNode.y);
                    ctx.quadraticCurveTo(currNode.x, currNode.y, x, y);
                }
                currNode = this.nodes[i];
                nextNode = this.nodes[i + 1];
                ctx.quadraticCurveTo(currNode.x, currNode.y, nextNode.x, nextNode.y);

                ctx.stroke();
                ctx.closePath();
            }
        }

        const E = {
            friction: 0.5,
            trails: 20,
            size: 50,
            dampening: 0.25,
            tension: 0.98,
        };

        const renderCanvas = function () {
            const canvas = document.getElementById("canvas");
            const ctx = canvas.getContext("2d");
            let lines = [];
            const pos = { x: 0, y: 0 };
            const wave = new Wave({
                phase: Math.random() * 2 * Math.PI,
                amplitude: 85,
                frequency: 0.0015,
                offset: 285,
            });
            let running = true;
            let frame = 1;

            function resizeCanvas() {
                ctx.canvas.width = window.innerWidth;
                ctx.canvas.height = window.innerHeight;
            }

            resizeCanvas();

            function animate() {
                if (running) {
                    ctx.globalCompositeOperation = "source-over";
                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
                    ctx.globalCompositeOperation = "lighter";
                    ctx.strokeStyle = `hsla(${Math.round(wave.update())}, 90%, 50%, 0.25)`;
                    ctx.lineWidth = 1;

                    for (let i = 0; i < E.trails; i++) {
                        const line = lines[i];
                        line.update();
                        line.draw(ctx);
                    }
                    frame++;
                    window.requestAnimationFrame(animate);
                }
            }

            function bindMouseMove(event) {
                function drawLine() {
                    lines = [];
                    for (let i = 0; i < E.trails; i++)
                        lines.push(new Line({ spring: 0.45 + (i / E.trails) * 0.025 }, pos));
                }
                function move(e) {
                    e.touches
                        ? ((pos.x = e.touches[0].pageX), (pos.y = e.touches[0].pageY))
                        : ((pos.x = e.clientX), (pos.y = e.clientY));
                    e.preventDefault();
                }
                function start(e) {
                    if (e.touches.length === 1) {
                        pos.x = e.touches[0].pageX;
                        pos.y = e.touches[0].pageY;
                    }
                }
                document.removeEventListener("mousemove", bindMouseMove);
                document.removeEventListener("touchstart", bindMouseMove);
                document.addEventListener("mousemove", move);
                document.addEventListener("touchmove", move);
                document.addEventListener("touchstart", start);
                move(event);
                drawLine();
                animate();
            }

            document.addEventListener("mousemove", bindMouseMove);
            document.addEventListener("touchstart", bindMouseMove);
            document.body.addEventListener("orientationchange", resizeCanvas);
            window.addEventListener("resize", resizeCanvas);
            window.addEventListener("focus", () => {
                if (!running) {
                    running = true;
                    animate();
                }
            });
            window.addEventListener("blur", () => {
                running = true;
            });
        };

        renderCanvas();
    </script>
</body>
</html>

#解析HTML代码

HTML结构
  • 页面中包含一个<canvas>元素,用于绘制动画。
  • CSS样式用于隐藏页面的默认边距,并使画布全屏显示。
JavaScript代码
  • 包含了之前定义的所有类和函数。
  • renderCanvas函数被调用以启动动画。
事件监听
  • 添加了鼠标移动和触摸事件监听器,以更新线条的位置。
  • 窗口调整大小事件监听器用于保持画布与窗口大小同步。

将上述HTML代码保存为一个.html文件,并在浏览器中打开,你就可以看到一个随着鼠标移动变化的波浪线动画了。

#解析JS代码

Wave 类
  • 用于描述一个正弦波,包含相位(phase)、偏移(offset)、频率(frequency)和振幅(amplitude)属性。
  • update 方法用于更新波形,每次调用时相位增加一定的频率,并返回当前波形的位置。
Node 类
  • 代表动画中的一个点,拥有位置(x, y)和速度(vx, vy)。
Line 类
  • 描述由多个Node组成的线段。
  • 包含弹簧系数(spring)、摩擦系数(friction)和节点列表(nodes)。
  • update 方法用于更新每个节点的位置,根据相邻节点的位置和速度以及弹簧和摩擦力。
  • draw 方法用于在Canvas上下文上绘制线条。
E 对象
  • 定义了动画的一些常量,如摩擦系数、轨迹数量、节点数量、阻尼系数和张力系数。
renderCanvas 函数
  • 初始化Canvas,并设置其尺寸。
  • 创建一个Wave实例,并定义了一些动画相关的变量。
  • animate 函数负责动画的绘制和更新。
  • 通过监听鼠标移动和触摸事件来更新线条的位置,并开始动画循环。
相关推荐
Pedantic1 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘1 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆1 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师2 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆2 小时前
VSCode自动格式化三要素
前端
爱勇宝3 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen4 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518136 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode6 小时前
Redis 在生产项目的使用
前端·后端