模拟鼠标移动

Avatar of Louis Hoebregts
Louis Hoebregts

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

如果您曾经需要在现场演讲或课堂上展示交互式动画,那么您可能知道在说话的同时与幻灯片进行交互并不总是那么容易。

当我需要向学生展示这个粒子演示时,我就遇到了这种情况。 我不想一直站在电脑旁边移动鼠标来展示演示。

查看 CodePen 上的
粒子(移动时)
,由 Louis Hoebregts (@Mamboleoo) 创建。
CodePen 上。

如果您不与 iframe 交互,您将只会看到空白。 一旦您开始移动鼠标或手指,您就可以看到动画。

因此,我创建了相同的演示,但我使用了一些额外的代码来模拟有人与演示进行交互。

查看 CodePen 上的
粒子(伪造)
,由 Louis Hoebregts (@Mamboleoo) 创建。
CodePen 上。

单纯形噪声

这里的技巧是使用一种算法来生成“平滑”的随机位置。 如果我们使用经典的随机函数,伪造的鼠标将在每一帧都处于完全随机的位置。 我们想要的是每一帧上的位置都与前一个位置直接相关。 幸运的是,有一种技术可以完全满足我们的需求:单纯形噪声(或更常见的是称为 Perlin 噪声)。

让我们看一下这张图片,其中每一列的高度由顶部的随机值定义,下面是单纯形噪声算法的值。

您可以很快注意到,底部图表看起来更加平滑,因为每一列的高度都与前一列相连。 这些图表仅显示了一个维度(x 轴,从左到右),但使用单纯形噪声,您可以在多个维度中获取值。 在我们的例子中,我们将需要两个维度来表示我们模拟的伪造鼠标的 X 和 Y 坐标。

如果您对单纯形噪声的工作原理更感兴趣,请查看 Daniel Shiffman 制作的视频 “I.5: Perlin 噪声 – 代码的本质”

获取噪声坐标

要使我们的演示工作,首先需要实现一个生成噪声的脚本。 在我的例子中,我使用的是 Seph 编写的 此脚本

加载噪声脚本后,我们就可以在每一帧上使用它来使我们的鼠标移动。

我将在演示中使用鼠标的图像,将其设置为 position: fixed; 并使用类 .mouse,但您可以在自己的项目中为其他任何内容设置动画。

所以,让我们看看代码

// We retrieve the image from the DOM
const el = document.querySelector('.mouse');

// The render function is called on every frame
function render (a) {
  // The a variable is the amount of milliseconds since we started our script
  
  // Get a noise value based on the elapsed time to get a new value on every frame
  // This noise algorithm is returning values between [-1, 1] so we need to map them to [0, 1] by adding one to the value and dividing it by 2
  const noiseX = (noise.simplex2(0, a*0.0005) + 1) / 2;
  // We get another noise value for the y axis but because we don't want the same value than x, we need to use another value for the first parameter
  const noiseY = (noise.simplex2(1, a*0.0005) + 1) / 2;
  
  // Convert the noise values from [0, 1] to the size of the window
  const x = noiseX * window.innerWidth;
  const y = noiseY * window.innerHeight;
  
  // Apply the x & y coordinates on our element
  el.style.transform = `translate(${x}px, ${y}px)`;
  
  // Call the render function once the browser is ready to make it an infinite loop
  requestAnimationFrame(render);
}

// Ask the browser to call render to start our animation
requestAnimationFrame(render);

以下是使用上述脚本获得的结果

查看 CodePen 上的
虚拟用户 1
,由 Louis Hoebregts (@Mamboleoo) 创建。
CodePen 上。

允许交互

使用当前代码,我们不再允许与我们的演示进行交互。 让我们添加一些额外的代码,以便在与演示进行交互时使用我们真实的鼠标位置,并在我们停止时切换回伪造的鼠标。

const el = document.querySelector('.mouse');
let lastMove = 0;

// When the mouse is being moved
function onMouseMove (e) {
  // Get the x and y coordinates
  x = e.clientX;
  y = e.clientY;
  
  // Save the last move time
  lastMove = Date.now();
}

// Update the mouse position based on x & y
function updateMouse (x, y) {
  el.style.transform = `translate(${x}px, ${y}px)`;
}

function render (a) {
  // Check if last move was more than 500ms ago
  if (Date.now() - lastMove > 500) {
    // Generate a fake mouse position
    ...
    updateMouse(x, y);
  }
}

// Listen to mouse events
window.addEventListener('mousemove', onMouseMove);

现在,如果您移动鼠标,伪造的鼠标将跟随您的鼠标。 如果您停止移动 500 毫秒,伪造的鼠标将再次开始移动。

查看 CodePen 上的
虚拟用户 3
,由 Louis Hoebregts (@Mamboleoo) 创建。
CodePen 上。

自定义移动

可以通过更改第三个参数的值来更新鼠标的速度。 到目前为止,我们通过将经过的时间乘以 0.0005 来设置此值,这等于 a/2000

// Define a speed ratio
const speed = a * 0.0005;
// Use the speed
const noiseX = (noise.simplex3(1, 0, speed) + 1) / 2;

我们还可以通过从其位置添加更多噪声来在方向变化中添加更多随机性。

let random = 0;
function render (a) {
  ...
  // Update the random value
  random += 0.1;
  // Compute a x random offset based on the window width
  const randX = noise.simplex3(1, 0, random) * window.innerWidth * 0.1;
  // Compute a y random offset based on the window height
  const randY = noise.simplex3(3, 0, random) * window.innerHeight * 0.1;
  
  // Define the x & y values based on (noise * screen) + randomness
  const x = noiseX * innerWidth + randX;
  const y = noiseY * innerHeight + randY;
  ...
}

使用输入来查看速度和随机计算的值如何影响伪造的鼠标移动。

查看 CodePen 上的
虚拟用户 4
,由 Louis Hoebregts (@Mamboleoo) 创建。
CodePen 上。

更多鼠标

既然我们已经创建了一个伪造的鼠标,为什么不创建 500 个呢?

查看 CodePen 上的
虚拟用户 5
,由 Louis Hoebregts (@Mamboleoo) 创建。
CodePen 上。

我现在几乎在所有演示中都使用这个技巧。 我认为能够在不使用视频或被迫在试图谈论演示时随机移动鼠标的情况下展示项目非常酷。

如果您有任何问题或意见,请在下方留言或在 Twitter 上联系我。