你现在盯着看的这片背景——漂浮的星点、若隐若现的连线、被鼠标推开的粒子——全部由不到 100 行 JavaScript 画成,没有任何第三方库。这篇文章就把它拆开揉碎讲给你。

原理:三件小事

整个"星河"其实只做三件事:

  1. 在画布上撒一堆随机位置的粒子,让它们缓慢漂移;
  2. 距离小于阈值的两个粒子之间画一条渐隐的线;
  3. 鼠标靠近时轻轻推开粒子,制造互动感。

先拿到画布并把它的尺寸铺满视口:

javascript
const cv  = document.querySelector('#particle-canvas');
const ctx = cv.getContext('2d');

let W, H;

function resize() {
  W = cv.width  = innerWidth;
  H = cv.height = innerHeight;
}

resize();
addEventListener('resize', resize);

第一步:撒星星

粒子的数量按屏幕面积算,大屏多点、小屏少点,避免手机上卡成 PPT。每个粒子记录位置、速度、半径和颜色:

javascript
const COLORS = ['0,229,255', '168,85,247', '255,61,129'];

function spawn() {
  const n = Math.min(110, Math.round(W * H / 16000));
  pts = new Array(n).fill(0).map(() => ({
    x: Math.random() * W,
    y: Math.random() * H,
    vx: (Math.random() - .5) * .35,   // 慢速漂移
    vy: (Math.random() - .5) * .35,
    r: Math.random() * 1.4 + .5,
    c: COLORS[Math.random() * COLORS.length | 0]
  }));
}

每一帧让粒子按速度移动,飘出边界就绕回另一侧(wrap around),星河就"活"了。

第二步:连线成网

这是灵魂所在:两两计算距离,小于 LINK(比如 130px)就连线,透明度随距离衰减。距离越近,线越亮:

javascript
for (let i = 0; i < pts.length; i++) {
  for (let j = i + 1; j < pts.length; j++) {
    const d = Math.hypot(a.x - b.x, a.y - b.y);
    if (d < LINK) {
      ctx.strokeStyle = `rgba(77,124,255,${1 - d / LINK})`;
      ctx.beginPath();
      ctx.moveTo(a.x, a.y);
      ctx.lineTo(b.x, b.y);
      ctx.stroke();
    }
  }
}

鼠标引力

监听 pointermove 记录鼠标位置,粒子在半径 110px 内被轻轻推开——推力与距离成反比。就这一行物理学,互动感直接拉满:

javascript
const dx = p.x - mouse.x, dy = p.y - mouse.y;
const d = Math.hypot(dx, dy);
if (d < 110 && d > .01) {
  p.x += dx / d * .55;  // 单位向量 × 推力
  p.y += dy / d * .55;
}

第三步:性能小贴士

最好的特效不是让人惊叹"这用了什么库",而是让人根本意识不到它是特效。

完整的实现就嵌在这个页面的源码里(main.js),右键查看源码即可自取。给你的博客也点一片星河吧。✨