电路板背景特效(Canvas 电路走线动画,信号脉冲流动)

CANVASJS 许可:自研原创

电路板背景特效:Canvas 自动生成正交电路走线与焊点,光点脉冲沿走线流动,电路密度与脉冲速度实时可调,支持一键重新生成布局,适配高分屏。

效果演示(真实可交互)

Canvas 上自动铺开一张电路板:绿色焊点按网格分布(带随机抖动避免呆板),焊点之间以”先横后竖或先竖后横”的直角折线相连,形成真实的电路走线风格。明亮的信号光点沿走线匀速流动,走到尽头后换一条随机路径继续,营造出数据在芯片间传输的科技感。「电路密度」控制焊点网格的疏密,「脉冲速度」控制光点流速,点「重新生成」按钮会随机生成一张全新的电路布局——每次点击得到的走线都不相同。整张电路由算法现场生成,不需要任何素材图。

点击「重新生成」刷新电路布局

完整代码

<canvas id="circuit" style="width:100%;height:340px;display:block;background:radial-gradient(120% 120% at 50% 0%,#0a1f1a,#041009);"></canvas>
<button id="regen">重新生成</button>
const canvas = document.getElementById('circuit');
const ctx = canvas.getContext('2d');
let W, H, nodes = [], traces = [], pulses = [];
const cfg = { density: 44, speed: 4 };

function resize() {
  const r = canvas.getBoundingClientRect();
  const dpr = window.devicePixelRatio || 1;
  canvas.width = Math.round(r.width * dpr);
  canvas.height = Math.round(r.height * dpr);
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  W = r.width; H = r.height;
  build();
}
function orthogonalPath(a, b) {
  const pts = [a];
  if (Math.random() < .5) {
    const mx = (a.x + b.x) / 2 + (Math.random() - .5) * 40;
    pts.push({ x: mx, y: a.y }, { x: mx, y: b.y });
  } else {
    const my = (a.y + b.y) / 2 + (Math.random() - .5) * 40;
    pts.push({ x: a.x, y: my }, { x: b.x, y: my });
  }
  pts.push(b);
  return pts;
}
function build() {
  const gap = cfg.density;
  const cols = Math.max(1, Math.floor(W / gap));
  const rows = Math.max(1, Math.floor(H / gap));
  nodes = [];
  for (let r = 0; r < rows; r++)
    for (let c = 0; c < cols; c++)
      nodes.push({ x: c * gap + gap / 2 + (Math.random() - .5) * gap * .4,
                   y: r * gap + gap / 2 + (Math.random() - .5) * gap * .4 });
  traces = [];
  for (let i = 0; i < nodes.length; i++) {
    if (i % cols < cols - 1 && Math.random() < .55 && Math.random() < .8)
      traces.push(orthogonalPath(nodes[i], nodes[i + 1]));
    if (i < nodes.length - cols && Math.random() < .55 && Math.random() < .8)
      traces.push(orthogonalPath(nodes[i], nodes[i + cols]));
  }
  pulses = [];
  const n = Math.min(6, traces.length);
  for (let i = 0; i < n; i++)
    pulses.push({ path: traces[Math.floor(Math.random() * traces.length)], t: Math.random() });
}
function pathLength(path) {
  let len = 0;
  for (let i = 1; i < path.length; i++) len += Math.hypot(path[i].x - path[i-1].x, path[i].y - path[i-1].y);
  return len;
}
function pointAt(path, t) {
  let target = t * pathLength(path);
  for (let i = 1; i < path.length; i++) {
    const seg = Math.hypot(path[i].x - path[i-1].x, path[i].y - path[i-1].y);
    if (target <= seg) {
      const f = seg === 0 ? 0 : target / seg;
      return { x: path[i-1].x + (path[i].x - path[i-1].x) * f,
               y: path[i-1].y + (path[i].y - path[i-1].y) * f };
    }
    target -= seg;
  }
  return path[path.length - 1];
}
function step() {
  ctx.clearRect(0, 0, W, H);
  for (const tr of traces) {          // 走线
    ctx.beginPath(); ctx.moveTo(tr[0].x, tr[0].y);
    for (let i = 1; i < tr.length; i++) ctx.lineTo(tr[i].x, tr[i].y);
    ctx.strokeStyle = 'rgba(52,211,153,.12)'; ctx.lineWidth = 1; ctx.stroke();
  }
  for (const n of nodes) {            // 焊点
    ctx.beginPath(); ctx.arc(n.x, n.y, 1.6, 0, Math.PI * 2);
    ctx.fillStyle = 'rgba(52,211,153,.35)'; ctx.fill();
  }
  for (const p of pulses) {           // 脉冲
    p.t += .004 * cfg.speed;
    if (p.t >= 1) { p.t = 0; p.path = traces[Math.floor(Math.random() * traces.length)]; }
    if (!p.path) continue;
    const pos = pointAt(p.path, p.t);
    const g = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, 12);
    g.addColorStop(0, 'rgba(110,231,183,.95)');
    g.addColorStop(1, 'rgba(110,231,183,0)');
    ctx.fillStyle = g;
    ctx.beginPath(); ctx.arc(pos.x, pos.y, 12, 0, Math.PI * 2); ctx.fill();
  }
  requestAnimationFrame(step);
}
resize(); step();
window.addEventListener('resize', resize);
document.getElementById('regen').addEventListener('click', build);

参数说明

参数位置说明
电路密度cfg.density20-70px 格距,越小焊点越密
脉冲速度cfg.speed1-10,光点流速
连边概率Math.random() < .55调大走线更密集
走线颜色rgba(52,211,153,...)换成青色/金色即变风格
脉冲数量Math.min(6, ...)同时流动的光点数
焊点抖动gap * .4焊点随机偏移量,0 即规整网格

兼容性与性能

常见问题

Q: 如何把电路板做成全屏固定背景? 设置 position: fixed; inset: 0; z-index: -1,并用 window.innerWidth / innerHeight 计算画布尺寸,resize 时重新 build 即可。

Q: 走线为什么不直接连直线而是拐直角? 直角走线是 PCB 电路板的典型风格(真实电路为了避让和阻抗控制通常走 45° 或直角),这也是”电路感”的核心来源。想要 45° 斜线可在折点处再插入对角点。

Q: 能做出”脉冲爆炸”或点击交互吗? 可以。在 click 事件里向点击点周围的所有节点注入新脉冲,或让脉冲经过时短暂加粗走线,都能增强互动感。

Q: 布局每次重新生成会不会太随机? 会。若想要稳定的品牌化布局,可固定随机种子(如用带种子的伪随机函数替换 Math.random),这样同一页面每次刷新布局一致。

许可与来源

  • 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
  • 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
  • 首次发布:2026-09-01 · 最近更新:2026-09-01
  • 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
  • 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理

常见问题

这个组件可以商用吗?

可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。

浏览器兼容性如何?

支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。

怎么修改动画参数?

直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。

✍️ 作者与审核