Canvas粒子文字效果本质是将文字转为像素点,再使每个alpha>128的不透明像素成为可动粒子;关键在用getImageData精准提取高对比文字区域像素,并通过requestAnimationFrame逐帧高效重绘,避免性能瓶颈。

Canvas 粒子文字效果本质是「把文字转成像素点,再让每个不透明像素变成一个可动的粒子」——不是靠 CSS 动画或 SVG 路径,而是真正在 ctx 上逐帧重绘。关键不在“怎么炫”,而在“怎么稳、怎么快、怎么不卡”。
如何用 getImageData 正确提取文字像素
很多人直接在空画布上写文字就调 getImageData,结果拿到全黑或全透明数据。原因:文字颜色和背景色对比度不够,或没等字体加载完就取数据。
- 必须先用
ctx.fillStyle填充一个高对比背景(比如"#000"),再用ctx.fillText写白色文字,确保 alpha > 128 的像素真正代表文字轮廓 - 如果用自定义字体(如
"Ma Shan Zheng"),得监听document.fonts.load或加setTimeout延迟取图,否则getImageData读到的是空白区域 -
getImageData(x, y, width, height)的坐标和尺寸必须严格对齐文字实际渲染区域——建议先用ctx.measureText算宽高,再手动偏移定位,别硬写死0, 0, 200, 50
为什么粒子一多就掉帧?requestAnimationFrame 不够用
每帧遍历几百个粒子做 ctx.fillRect 或 ctx.beginPath + ctx.fill,CPU 和 GPU 都扛不住。真实项目里超过 300 粒子,60fps 就开始崩。
- 粒子绘制优先用
ctx.fillRect(x, y, 2, 2),别用圆形(arc+fill)——少 3 个 API 调用,帧率能稳 10~15% - 禁用
ctx.shadowBlur和ctx.globalAlpha在循环内设值,它们会强制触发合成层切换,开销极大 - 把粒子数组按 Z 轴(比如原始 y 坐标)预排序,用
particleArray.sort((a, b) => a.y - b.y),避免每帧都 sort;或者干脆不排序,靠绘制顺序掩盖层叠问题
Particle 类该存哪些字段?别塞冗余数据
每个粒子对象不是越全越好。Canvas 粒子动画最怕内存暴涨+ GC 频繁,尤其在移动端。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
立即学习“前端免费学习笔记(深入)”;
- 必须存:
x,y,vx,vy,targetX,targetY,color(RGBA 字符串或数组) - 建议缓存:
size(统一用 1.5px,别每个粒子存不同值),life(只在需要淡出/消失时加) - 绝对别存:
ctx,canvas,drawSelf方法体(应抽成独立函数),或任何 DOM 引用——这些会让 V8 无法及时回收对象 - 初始化粒子时,用
new Array(n).fill().map(...)比for循环快,但 n > 1000 时建议用Float32Array存坐标,省 40% 内存
响应式画布下粒子错位?canvas.width/height 和 CSS 尺寸不是一回事
用 canvas.style.width = "100%" 拉伸画布,但没同步改 canvas.width 和 canvas.height 属性,会导致所有像素被拉糊,getImageData 返回的数据坐标完全错乱。
- resize 时必须同时更新两个地方:
canvas.width = canvas.clientWidth和canvas.height = canvas.clientHeight - 文字重绘前,要重新计算文字在新画布中的居中坐标:
textX = (canvas.width - textWidth) / 2,不能复用旧值 - 粒子的
targetX/targetY是基于原始文字位置存的,缩放后需等比映射:比如原画布 400×300,文字在 (100, 150),新画布 800×600,则目标坐标要乘 2
最易被忽略的一点:粒子动画一旦启用了物理模拟(比如加速度、碰撞检测),就必须用时间戳 delta 计算位移,不能只依赖 tickTime = 16。设备卡顿几帧,粒子就会瞬移或堆叠——这问题在低端 Android 机上几乎必现。


















