You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求实现鼠标拖拽文字跟随效果的代码解决方案

实现鼠标拖拽文字的动态跟随效果

需要实现的效果:鼠标移动时,文字字符依次跟随鼠标,呈现连贯的拖拽牵引感,字符间带有自然延迟,形成类似拖尾的动态效果。我尝试用paper.js实现但未达到预期,请求编写能实现该效果的代码片段,不限使用任何库。

以下是我尝试的代码:

我的尝试代码

HTML

<!DOCTYPE html>
<html>

<head>
  <script src="https://unpkg.com/paper"></script>
</head>

<body>
  <div class="custom-cursor"></div>
  <canvas id="myCanvas"></canvas>
</body>

</html>

CSS

html,
body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

#myCanvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.custom-cursor {
  position: absolute;
  top: -3px;
  left: -3px;
  width: 7px;
  height: 7px;
  border: 2px solid black;
  border-radius: 50%;
  background-color: transparent;
  pointer-events: none;
  z-index: 9999;
}

JavaScript

// Setup Paper.js
paper.setup('myCanvas');

var word = 'Hello, World!';
var characters = [];

var offsetX = 0;
var offsetY = 0;
var characterSpacing = 20;
var chasingSpeed = 0.9;
var delayIncrement = 100;

for (var i = 0; i < word.length; i++) {
  var character = new paper.PointText({
    content: word[i],
    fillColor: 'black',
    fontFamily: 'Arial, sans-serif',
    fontWeight: 'bold',
    fontSize: 24,
    justification: 'center'
  });

  character.targetPosition = new paper.Point(0, 0);
  character.position = new paper.Point(0, 0);
  character.delay = i * delayIncrement;

  characters.push(character);
}

function onMouseMove(event) {
  var mousePosition = event.point;
  var cursor = document.querySelector('.custom-cursor');

  cursor.style.top = mousePosition.y - cursor.offsetHeight / 2 + 'px';
  cursor.style.left = mousePosition.x - cursor.offsetWidth / 2 + 'px';

  // 注释的线性跟随逻辑
  // for (var i = 0; i < characters.length; i++) {
  //     var character = characters[i];
  //     var targetPosition = mousePosition.add(new paper.Point(i * characterSpacing + 20, 0));

  //     setTimeout(function(char, targetPos) {
  //         char.targetPosition = targetPos;
  //     }, character.delay, character, targetPosition);
  // }

  var wordLength = word.length;
  var totalWidth = wordLength * characterSpacing;
  var angleIncrement = Math.PI * 2 / totalWidth;
  var initialAngle = Math.atan2(mousePosition.y, mousePosition.x) - Math.PI / 2;

  for (var i = 0; i < characters.length; i++) {
    var character = characters[i];

    var angle = initialAngle + angleIncrement * i;
    var radius = totalWidth / (2 * Math.PI);
    var targetX = mousePosition.x + Math.cos(angle) * radius;
    var targetY = mousePosition.y + Math.sin(angle) * radius;

    setTimeout(function(char, posX, posY) {
      char.targetPosition = new paper.Point(posX, posY);
    }, character.delay, character, targetX, targetY);
  }
}

function onFrame(event) {
  for (var i = 0; i < characters.length; i++) {
    var character = characters[i];
    var distance = character.targetPosition.subtract(character.position);
    character.position = character.position.add(distance.multiply(chasingSpeed));
  }
}

paper.view.onMouseMove = onMouseMove;
paper.view.onFrame = onFrame;

paper.view.onResize = function(event) {
  paper.view.viewSize = new paper.Size(window.innerWidth, window.innerHeight);
}

paper.view.draw();

修正后的实现代码

以下是能实现目标拖拽效果的代码,核心调整了字符跟随逻辑,优化了缓动参数与延迟机制:

HTML

<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/paper"></script>
</head>
<body>
  <div class="custom-cursor"></div>
  <canvas id="myCanvas"></canvas>
</body>
</html>

CSS

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  cursor: none;
}

#myCanvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.custom-cursor {
  position: absolute;
  width: 8px;
  height: 8px;
  border: 2px solid #000;
  border-radius: 50%;
  background-color: transparent;
  pointer-events: none;
  z-index: 9999;
  transition: transform 0.05s ease;
}

JavaScript

paper.setup('myCanvas');

const text = 'Hello, World!';
const chars = [];
const charSpacing = 24;
const followSpeed = 0.15;
const delayStep = 60;

// 初始化字符
text.split('').forEach((char, index) => {
  const pointText = new paper.PointText({
    content: char,
    fillColor: '#000',
    fontFamily: 'Helvetica Neue, Arial, sans-serif',
    fontWeight: '600',
    fontSize: 28,
    justification: 'center'
  });
  
  pointText.target = new paper.Point(window.innerWidth/2, window.innerHeight/2);
  pointText.position = pointText.target.clone();
  pointText.delay = index * delayStep;
  
  chars.push(pointText);
});

// 处理鼠标移动
let mousePos = new paper.Point(window.innerWidth/2, window.innerHeight/2);
const cursor = document.querySelector('.custom-cursor');

paper.view.onMouseMove = (event) => {
  mousePos = event.point;
  
  // 更新自定义光标位置
  cursor.style.top = `${mousePos.y - cursor.offsetHeight/2}px`;
  cursor.style.left = `${mousePos.x - cursor.offsetWidth/2}px`;
  
  // 为每个字符设置目标位置,带延迟
  chars.forEach((char, index) => {
    const targetX = mousePos.x + (index - chars.length/2) * charSpacing;
    const targetY = mousePos.y;
    
    setTimeout(() => {
      char.target = new paper.Point(targetX, targetY);
    }, char.delay);
  });
};

// 每一帧更新字符位置
paper.view.onFrame = () => {
  chars.forEach(char => {
    const diff = char.target.subtract(char.position);
    char.position = char.position.add(diff.multiply(followSpeed));
  });
};

// 窗口大小调整
paper.view.onResize = (event) => {
  paper.view.viewSize = new paper.Size(event.size.width, event.size.height);
};

paper.view.draw();

关键改动说明

  • 跟随逻辑优化:字符在鼠标水平方向居中排列,跟随鼠标移动时保持整体行状结构
  • 缓动参数调整:降低followSpeed让字符移动更顺滑,避免生硬跳动
  • 延迟机制简化:按字符顺序设置递增延迟,形成自然的拖尾效果
  • 光标体验提升:隐藏默认光标,让自定义光标更突出
  • 初始位置优化:将字符初始位置设置在屏幕中心,提升首次加载体验

内容的提问来源于stack exchange,提问作者Amir Nejatbakhsh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 08:47:01