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 函数负责动画的绘制和更新。
  • 通过监听鼠标移动和触摸事件来更新线条的位置,并开始动画循环。
相关推荐
还是大剑师兰特5 分钟前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
m0_7482361113 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo61726 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_7482489427 分钟前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_7482356139 分钟前
从零开始学前端之HTML(三)
前端·html
旭久2 小时前
SpringBoot的Thymeleaf做一个可自定义合并td的pdf表格
pdf·html·springboot
一个处女座的程序猿O(∩_∩)O3 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink6 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-8 小时前
验证码机制
前端·后端