什么是 mousemove 事件?

mousemove 事件会在用户将鼠标指针移动到一个元素上时持续不断地触发,只要鼠标在元素边界内移动,哪怕只移动一个像素,这个事件就会被触发一次。

js教程 mousemove
(图片来源网络,侵删)

基本语法

主要有两种方式来监听 mousemove 事件:

使用 addEventListener (推荐方式)

这是现代 JavaScript 中最标准、最灵活的方式。

// 获取要监听的元素,通常是 document 或某个具体的 DOM 元素
const element = document.body; // 或者 document.getElementById('myDiv');
// 定义一个事件处理函数(也叫监听器)
function handleMouseMove(event) {
  // event 对象包含了关于事件的详细信息,比如鼠标位置
  console.log("鼠标在移动!");
}
// 添加事件监听器
element.addEventListener('mousemove', handleMouseMove);

使用 onmousemove 属性 (传统方式)

这种方式比较直接,但不如 addEventListener 灵活(无法添加多个监听器)。

const element = document.body;
// 直接将一个函数赋值给 onmousemove 属性
element.onmousemove = function(event) {
  console.log("鼠标在移动!");
};

核心:event 对象

mousemove 事件被触发时,浏览器会传递一个 event 对象给我们的处理函数,这个对象包含了非常有用的信息,其中最重要的是鼠标的坐标。

js教程 mousemove
(图片来源网络,侵删)

获取鼠标位置

event 对象提供了两组坐标,它们的参考点(原点)不同:

  1. clientXclientY

    • 参考点:浏览器视口的左上角。
    • 含义:表示鼠标指针在当前可见窗口中的位置,不考虑页面是否滚动。
    • 这是最常用、最稳定的坐标。
  2. pageXpageY

    • 参考点:整个文档的左上角(包括被滚动隐藏的部分)。
    • 含义:表示鼠标指针在整个HTML文档中的绝对位置。
    • 注意:pageXpageY 在 IE 8 及以下版本中不支持。

示例代码:

js教程 mousemove
(图片来源网络,侵删)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">MouseMove 坐标示例</title>
  <style>
    body {
      font-family: sans-serif;
      padding: 20px;
    }
    #info {
      background-color: #f0f0f0;
      border: 1px solid #ccc;
      padding: 10px;
      margin-top: 20px;
      border-radius: 5px;
    }
  </style>
</head>
<body>
  <h1>移动你的鼠标</h1>
  <p>查看下方坐标的变化。</p>
  <div id="info">
    <p>ClientX: <span id="clientX">0</span></p>
    <p>ClientY: <span id="clientY">0</span></p>
    <p>PageX: <span id="pageX">0</span></p>
    <p>PageY: <span id="pageY">0</span></p>
  </div>
  <script>
    // 获取显示坐标的 span 元素
    const clientXSpan = document.getElementById('clientX');
    const clientYSpan = document.getElementById('clientY');
    const pageXSpan = document.getElementById('pageX');
    const pageYSpan = document.getElementById('pageY');
    // 在整个文档上监听 mousemove 事件
    document.addEventListener('mousemove', function(event) {
      // 更新显示的内容
      clientXSpan.textContent = event.clientX;
      clientYSpan.textContent = event.clientY;
      pageXSpan.textContent = event.pageX;
      pageYSpan.textContent = event.pageY;
    });
  </script>
</body>
</html>

实战应用案例

案例1:创建一个跟随鼠标的元素

这是一个非常酷炫的效果,元素会像“小尾巴”一样跟随着鼠标。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">跟随鼠标的元素</title>
  <style>
    body {
      height: 2000px; /* 让页面可以滚动,演示 pageY 的区别 */
      cursor: none; /* 隐藏默认鼠标指针 */
    }
    #cursor-follower {
      position: fixed; /* 关键:相对于视口定位 */
      width: 20px;
      height: 20px;
      background-color: #007bff;
      border-radius: 50%;
      pointer-events: none; /* 关键:让这个元素不拦截鼠标事件 */
      transform: translate(-50%, -50%); /* 让元素中心对准鼠标位置 */
      transition: transform 0.1s ease-out; /* 添加平滑过渡效果 */
    }
  </style>
</head>
<body>
  <h1>移动鼠标试试看</h1>
  <p>页面很长,可以滚动一下。</p>
  <div id="cursor-follower"></div>
  <script>
    const follower = document.getElementById('cursor-follower');
    document.addEventListener('mousemove', function(event) {
      // 使用 clientX 和 clientY,因为元素是 fixed 定位,相对于视口
      follower.style.left = event.clientX + 'px';
      follower.style.top = event.clientY + 'px';
    });
  </script>
</body>
</html>

案例2:简单的绘图板

我们可以利用 mousemove 事件,结合 canvas 元素来创建一个简单的绘图工具。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">简单绘图板</title>
  <style>
    canvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="drawingCanvas" width="800" height="400"></canvas>
  <p>按住鼠标左键并拖动来绘画。</p>
  <script>
    const canvas = document.getElementById('drawingCanvas');
    const ctx = canvas.getContext('2d');
    // 设置画笔样式
    ctx.lineWidth = 5;
    ctx.lineCap = 'round';
    ctx.strokeStyle = 'blue';
    let isDrawing = false;
    let lastX = 0;
    let lastY = 0;
    function startDrawing(e) {
      isDrawing = true;
      // 获取鼠标在 canvas 内的坐标
      const rect = canvas.getBoundingClientRect();
      lastX = e.clientX - rect.left;
      lastY = e.clientY - rect.top;
    }
    function draw(e) {
      if (!isDrawing) return;
      const rect = canvas.getBoundingClientRect();
      const currentX = e.clientX - rect.left;
      const currentY = e.clientY - rect.top;
      // 开始一条新路径
      ctx.beginPath();
      // 移动到上一个点
      ctx.moveTo(lastX, lastY);
      // 画线到当前点
      ctx.lineTo(currentX, currentY);
      // 描边
      ctx.stroke();
      // 更新上一个点的坐标
      lastX = currentX;
      lastY = currentY;
    }
    function stopDrawing() {
      isDrawing = false;
    }
    // 添加事件监听器
    canvas.addEventListener('mousedown', startDrawing);
    canvas.addEventListener('mousemove', draw);
    canvas.addEventListener('mouseup', stopDrawing);
    canvas.addEventListener('mouseout', stopDrawing); // 当鼠标离开 canvas 时也停止绘画
  </script>
</body>
</html>

性能优化与最佳实践

mousemove 事件触发非常频繁,如果处理不当,可能会导致严重的性能问题(页面卡顿),必须进行优化。

使用 throttle (节流)

节流函数确保一个函数在指定的时间间隔内最多只执行一次,这对于 mousemove 来说至关重要,因为它可以将事件处理的频率从每秒数百次降低到每秒几十次。

手写一个简单的 throttle 函数:

function throttle(func, limit) {
  let inThrottle;
  return function() {
    const args = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  }
}
// 使用 throttle 包装你的处理函数
const expensiveFunction = function(e) {
  console.log(`处理鼠标位置: ${e.clientX}, ${e.clientY}`);
};
const throttledHandler = throttle(expensiveFunction, 100); // 每 100ms 最多执行一次
document.addEventListener('mousemove', throttledHandler);

使用 requestAnimationFrame

requestAnimationFrame 是浏览器提供的 API,用于在下一次重绘之前调用指定的回调函数,它与浏览器的刷新率同步(通常是 60fps),是做动画和视觉效果的最佳选择。

let isRunning = false;
function handleMouseMove(event) {
  // 如果已经在运行,则不再请求新的动画帧
  if (isRunning) return;
  isRunning = true;
  requestAnimationFrame(function() {
    // 在这里执行你的耗时代码
    console.log(`RAF 处理鼠标位置: ${event.clientX}, ${event.clientY}`);
    // 执行完毕后,重置标志
    isRunning = false;
  });
}
document.addEventListener('mousemove', handleMouseMove);

移除事件监听器

当代码不再需要监听事件时(比如组件卸载、页面切换),一定要记得移除事件监听器,以避免内存泄漏。

function handleMouseMove(event) {
  // ... 处理逻辑
}
// 添加监听
document.addEventListener('mousemove', handleMouseMove);
// ... 在某个时刻,不再需要监听了
// 移除监听 (注意:这里必须是同一个函数引用)
document.removeEventListener('mousemove', handleMouseMove);
特性 描述
触发时机 鼠标在元素上持续移动时。
关键对象 event 对象,包含 clientX, clientY, pageX, pageY 等坐标信息。
核心用途 跟踪鼠标位置、创建交互效果(如悬停提示、跟随动画)、实现绘图功能。
性能关键 必须进行节流 (throttle) 或使用 requestAnimationFrame,否则极易导致性能问题。
最佳实践 优先使用 addEventListener,并在不需要时用 removeEventListener 清理。

掌握 mousemove 事件是前端交互开发的基础,希望这份教程能帮助你理解并熟练使用它!