Canvas 动画: atan2 三角函数与鼠标跟随效果

这个案例展示了如何使用HTML5的Canvas和JavaScript实现一个动态效果:在画布上绘制一个箭头,并让它实时跟随鼠标移动。这个小项目不仅有趣,还能帮助你理解编程和基本数学概念的实际应用。

项目需求

我们的目标是在一个画布上绘制一个箭头,并让这个箭头随着鼠标的移动自动旋转,始终指向鼠标的位置。

数学基础知识:atan2函数

在这个项目中,最关键的数学概念是atan2函数,它帮助我们计算箭头应该如何旋转才能指向鼠标的位置。

  • dxdy:这些是鼠标位置和箭头位置之间的水平和垂直距离。dx是x方向上的差值,dy是y方向上的差值。

  • atan2(dy, dx)是什么? :它是一个特殊的数学函数,用来计算给定的dxdy所对应的角度。这个角度表示从箭头到鼠标的方向。我们之所以使用atan2,是因为它能够处理所有可能的方向(上下左右斜角),并且它比普通的atan函数更为精确和稳定。

技术要点

让我们来逐步理解项目中涉及的技术要点。

  1. Canvas绘图
  • Canvas是HTML5提供的一个绘图环境。我们使用<canvas>标签来创建一个画布,之后在这个画布上绘制箭头。canvas.getContext('2d')提供了一个2D绘图上下文,通过这个上下文可以绘制图形、设置颜色、处理旋转等操作。

事件监听

  • 我们使用JavaScript的mousemove事件监听器,实时捕捉鼠标在画布上的位置。每次鼠标移动时,事件监听器都会记录鼠标的x和y坐标,这样我们就知道鼠标在哪里了。

图形旋转

  • 当我们知道鼠标的位置后,接下来要做的就是计算箭头应该朝向哪个方向。通过Math.atan2(dy, dx)计算出箭头的旋转角度,然后使用Canvas的rotate方法,让箭头旋转到正确的角度,指向鼠标。

代码展示

go 复制代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>跟随鼠标移动的箭头</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
        }
        canvas {
            border: 1px solid #000;
        }
        header {
            margin-bottom: 20px;
        }
        aside {
            margin-top: 10px;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <header>
        Example from <a href="https://qianduandaren.com"><em>前端达人</em></a>
    </header>
    <canvas id="canvas" width="400" height="400"></canvas>
    <aside>请移动鼠标</aside>

    <script>
        class Utils {
            static captureMouse(element) {
                const mouse = { x: 0, y: 0, event: null };
                element.addEventListener('mousemove', event => {
                    const rect = element.getBoundingClientRect();
                    mouse.x = event.clientX - rect.left;
                    mouse.y = event.clientY - rect.top;
                    mouse.event = event;
                });
                return mouse;
            }
        }

        class Arrow {
            constructor() {
                this.x = 0;
                this.y = 0;
                this.color = "#ffff00";
                this.rotation = 0;
            }

            draw(context) {
                context.save();
                context.translate(this.x, this.y);
                context.rotate(this.rotation);
                context.lineWidth = 2;
                context.fillStyle = this.color;
                context.beginPath();
                context.moveTo(-50, -25);
                context.lineTo(0, -25);
                context.lineTo(0, -50);
                context.lineTo(50, 0);
                context.lineTo(0, 50);
                context.lineTo(0, 25);
                context.lineTo(-50, 25);
                context.lineTo(-50, -25);
                context.closePath();
                context.fill();
                context.stroke();
                context.restore();
            }
        }

        window.onload = () => {
            const canvas = document.getElementById('canvas');
            const context = canvas.getContext('2d');
            const mouse = Utils.captureMouse(canvas);
            const arrow = new Arrow();

            arrow.x = canvas.width / 2;
            arrow.y = canvas.height / 2;

            const drawFrame = () => {
                requestAnimationFrame(drawFrame);
                context.clearRect(0, 0, canvas.width, canvas.height);

                const dx = mouse.x - arrow.x;
                const dy = mouse.y - arrow.y;

                arrow.rotation = Math.atan2(dy, dx);
                arrow.draw(context);
            };

            drawFrame();
        };
    </script>
</body>
</html>

分步解释

1. Canvas设置与初始化

go 复制代码
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

获取Canvas元素和上下文:首先,在onload函数中,我们通过getElementById获取HTML中的canvas元素,并通过getContext('2d')获取2D绘图上下文,这个上下文是我们用来绘制图形的基础。

2. 工具类Utils:捕捉鼠标位置

go 复制代码
class Utils {
    static captureMouse(element) {
        const mouse = { x: 0, y: 0, event: null };
        element.addEventListener('mousemove', event => {
            const rect = element.getBoundingClientRect();
            mouse.x = event.clientX - rect.left;
            mouse.y = event.clientY - rect.top;
            mouse.event = event;
        });
        return mouse;
    }
}

Utils类简介:

Utils类提供了一个静态方法captureMouse,这个方法的作用是帮助我们实时捕捉鼠标在画布中的位置。

捕捉鼠标位置:

  • getBoundingClientRect():用于获取画布相对于浏览器窗口的位置和大小。

  • 监听mousemove事件:每次鼠标在画布上移动时,都会触发mousemove事件,这时我们计算鼠标相对于画布的x、y坐标,并存储在mouse对象中。

  • 最后返回这个mouse对象,以便我们在后续的代码中随时获取鼠标的位置。

3、定义箭头类Arrow

go 复制代码
class Arrow {
    constructor() {
        this.x = 0;
        this.y = 0;
        this.color = "#ffff00";
        this.rotation = 0;
    }

    draw(context) {
        context.save();
        context.translate(this.x, this.y);
        context.rotate(this.rotation);
        context.lineWidth = 2;
        context.fillStyle = this.color;
        context.beginPath();
        context.moveTo(-50, -25);
        context.lineTo(0, -25);
        context.lineTo(0, -50);
        context.lineTo(50, 0);
        context.lineTo(0, 50);
        context.lineTo(0, 25);
        context.lineTo(-50, 25);
        context.lineTo(-50, -25);
        context.closePath();
        context.fill();
        context.stroke();
        context.restore();
    }
}
  • Arrow类用于定义箭头的初始属性,包括位置(x、y)、颜色和旋转角度。

  • draw方法:用于在画布上绘制箭头。这个方法使用了Canvas的绘图API,首先保存当前绘图状态(context.save()),然后移动并旋转画布(translate和rotate),根据预定的路径绘制出一个箭头形状,最后填充颜色和描边(fill和stroke),并恢复画布状态(context.restore())。

4.绘制与旋转箭头

go 复制代码
const drawFrame = () => {
       requestAnimationFrame(drawFrame);
       context.clearRect(0, 0, canvas.width, canvas.height);

       const dx = mouse.x - arrow.x;
       const dy = mouse.y - arrow.y;

       arrow.rotation = Math.atan2(dy, dx);
       arrow.draw(context);
   };

   drawFrame();
  • 动画帧更新 :使用requestAnimationFrame(drawFrame)来实现平滑的动画效果。这个方法让浏览器在每次重绘时调用drawFrame,从而以高效的方式不断更新箭头的位置和方向。

  • 清除画布 :每一帧开始时,我们使用context.clearRect(0, 0, canvas.width, canvas.height)清空画布,这样就不会看到之前绘制的内容残留。这样做可以确保每次重绘都是干净的。

  • 计算方向

    • dxdy:计算鼠标相对于箭头的水平和垂直距离。

    • 旋转角度 :通过Math.atan2(dy, dx)计算出箭头需要旋转的角度。atan2函数根据这两个差值返回一个介于π之间的角度值,表示从箭头位置到鼠标位置的方向。

  • 绘制箭头 :在计算完旋转角度后,我们调用arrow.draw(context),根据新的角度在画布上绘制箭头。这使得箭头能够实时指向鼠标的位置。

结束

这个项目演示了如何使用HTML5的Canvas和JavaScript来创建一个动态的跟随鼠标移动的箭头效果。我们通过atan2函数计算出箭头旋转的角度,并使用Canvas的绘图功能将其实时显示在网页上。通过这个案例,您不仅学会了如何使用Canvas绘图和JavaScript事件监听,还掌握了如何将数学函数应用于实际的编程问题中。

希望这个案例能帮助您更好地理解这些技术,并激发您进一步学习和探索的兴趣!

相关推荐
李小白杂货铺6 小时前
显示器最佳分辨率设置
计算机外设·显示器·内置显示器·独立显示器·最佳分辨率
狂睡GG爆14 小时前
开放式耳机伤耳朵吗?分享四款不伤耳的开放式蓝牙耳机
计算机外设
有梦想的鱼21 小时前
杂牌鼠标侧键设置
计算机外设
wow2ok1 天前
天融信把桌面explorer.exe删了,导致开机之后无windows桌面,只能看到鼠标解决方法
windows·计算机外设
哲伦贼稳妥2 天前
一天认识一个硬件之显示器
经验分享·其他·计算机外设
培林将军2 天前
C51单片机-单按键输入识别,键盘消抖
单片机·嵌入式硬件·计算机外设
qq_406144492 天前
wincc利用拓展屏实现多台显示器显示单个项目配置方法详解
计算机外设
小锋学长生活大爆炸3 天前
【踩坑】装了显卡,如何让显示器从主板和显卡HDMI都输出
计算机外设·gpu·显卡·hdmi·外设
20岁30年经验的码农4 天前
java 鼠标点击和移动和键盘事件
java·开发语言·计算机外设
sensor_WU4 天前
【delphi】判断多显示器下,程序在那个显示器中
计算机外设·多显示器·程序在那个显示器上