电路板背景特效(Canvas 电路走线动画,信号脉冲流动)
电路板背景特效: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.density | 20-70px 格距,越小焊点越密 |
| 脉冲速度 | cfg.speed | 1-10,光点流速 |
| 连边概率 | Math.random() < .55 | 调大走线更密集 |
| 走线颜色 | rgba(52,211,153,...) | 换成青色/金色即变风格 |
| 脉冲数量 | Math.min(6, ...) | 同时流动的光点数 |
| 焊点抖动 | gap * .4 | 焊点随机偏移量,0 即规整网格 |
兼容性与性能
- 全部为 Canvas 2D 基础 API,兼容所有现代浏览器;已适配
devicePixelRatio,高分屏线条锐利。 - 走线与焊点只在 build() 时生成一次,动画循环只做描线与脉冲位置计算,无每帧建图开销。
- 密度 44px 时约 15×8 个焊点、几十条走线,一帧绘制成本极低,低端设备可流畅运行。
- 点「重新生成」会触发全量重建,频率不高时无性能问题;若要做连续变化,可每 5 秒自动重建一次并淡入淡出过渡。
- 建议在页面不可见时暂停 rAF 循环以省电。
常见问题
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(缓动曲线)等属性即可,代码中有注释说明。