如何使用CSS、HTML、jQuery将随机字符排列为指定形状制作加载动画
字母聚形加载器实现方案
技术选型判断
- 不用引入Three.js、Particle.js这类重依赖,纯原生JS+DOM+离屏Canvas就能实现,对你现在的入门阶段来说上手难度最低,性能完全满足加载器的使用场景。
- Masonry是瀑布流排布工具,只能实现规则的错落布局,没法把元素约束在自定义形状轮廓内,你现有代码里的Masonry相关逻辑可以直接删掉,留着没用。
- 不需要用Adobe软件做GIF/视频,直接用代码就能实现完整的动效。
核心实现逻辑
你之前的代码问题在于字母位置是完全随机生成的,没有做形状范围判断。要把字母限制在指定形状里,只要做「形状点位采样+元素位置映射」就行:
- 用离屏Canvas绘制你想要拼成的目标形状(可以是文字、也可以是透明PNG的LOGO)
- 遍历Canvas的像素数据,把所有不透明的点位记为有效坐标,采样步长可以自己调,控制字母的疏密程度
- 生成字母元素的时候,初始给随机的屏幕位置、旋转角度和透明度,用CSS过渡把字母平滑动画到之前采集到的有效坐标上,自动拼成完整形状
- 额外可以加定时随机切换字母内容的逻辑,做出参考图里那种字符跳动的科技感效果
可直接运行的修改后代码
HTML结构
保留你原来的结构即可,不需要额外加标签:
<div class="flyingLetters"></div>
CSS样式
替换掉你原来的样式,删掉冲突的定位、布局规则:
body { background: #010101; min-height: 100vh; overflow: hidden; margin: 0; } .animation-container { position: fixed; inset: 0; z-index: 1; pointer-events: none; } .animation-container span { position: absolute; color: whitesmoke; text-shadow: 0 0 1px #fff; user-select: none; will-change: transform, opacity; transition: all 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
JS逻辑
删掉原来的jQuery、Masonry相关代码,替换成原生逻辑:
const app = { chars: ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9'], // 要拼成的目标文字,换成你自己的内容即可 targetContent: 'LOADING', init() { this.container = document.createElement('div'); this.container.className = 'animation-container'; document.body.appendChild(this.container); // 采集目标形状内的所有有效坐标 const validPoints = this.getShapeValidPoints(); // 为每个有效点生成一个字母元素 validPoints.forEach(point => { const charEl = document.createElement('span'); const randomSize = 10 + Math.floor(Math.random() * 8); charEl.style.fontSize = `${randomSize}px`; charEl.textContent = this.chars[Math.floor(Math.random() * this.chars.length)]; // 初始状态:随机散落在屏幕任意位置,带随机旋转,透明 charEl.style.opacity = '0'; charEl.style.transform = `translate(${Math.random() * window.innerWidth}px, ${Math.random() * window.innerHeight}px) rotate(${Math.random() * 360}deg)`; this.container.appendChild(charEl); // 触发动画,让字母移动到目标位置 requestAnimationFrame(() => { charEl.style.opacity = '1'; charEl.style.transform = `translate(${point.x}px, ${point.y}px) rotate(${Math.random() * 40 - 20}deg)`; }); // 定时随机更换字符,模拟动态跳动效果 setInterval(() => { if (Math.random() > 0.7) { charEl.textContent = this.chars[Math.floor(Math.random() * this.chars.length)]; } }, 150); }); }, // 采集目标形状范围内的有效坐标 getShapeValidPoints() { const points = []; const offscreenCanvas = document.createElement('canvas'); const ctx = offscreenCanvas.getContext('2d'); offscreenCanvas.width = window.innerWidth; offscreenCanvas.height = 300; // 绘制目标形状,这里默认画居中文字,要做LOGO形状就换成drawImage上传你的透明PNG即可 ctx.fillStyle = '#fff'; ctx.font = 'bold 120px system-ui, sans-serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(this.targetContent, offscreenCanvas.width / 2, offscreenCanvas.height / 2); // 遍历像素采样,步长14控制字母密度,数值越小字母越密 const pixelData = ctx.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height).data; const step = 14; for (let y = 0; y < offscreenCanvas.height; y += step) { for (let x = 0; x < offscreenCanvas.width; x += step) { const pixelAlpha = pixelData[(y * offscreenCanvas.width + x) * 4 + 3]; // 像素不透明度大于128判定为形状内的有效点 if (pixelAlpha > 128) { points.push({ x: x + (window.innerWidth - offscreenCanvas.width) / 2, y: y + (window.innerHeight - offscreenCanvas.height) / 2 }); } } } return points; } }; document.addEventListener('DOMContentLoaded', () => app.init());
自定义形状提示:如果要拼成LOGO图形,只需要把
getShapeValidPoints方法里的fillText逻辑换成ctx.drawImage(你的透明PNG图片对象, 绘制x坐标, 绘制y坐标, 绘制宽度, 绘制高度)即可,采样逻辑不需要改动,不需要用设计软件导出动图。
内容的提问来源于stack exchange,提问作者Jagruti Solanki
相关产品推荐
相关产品推荐

