如何优化约1000个带CSS动画div的生成加载速度?
优化全屏点阵动画加载速度的方案
我正在制作个人作品集网站,为首页添加了全屏点阵动画效果。目前功能正常,但动画加载耗时过长。通过JS生成25x25的div矩阵,并为每个div添加CSS样式与动画。已尝试减小矩阵尺寸,但优化效果有限,进一步缩小则无法覆盖整个页面,求优化加载速度的方案。
现有代码
JavaScript
let holder = document.getElementById("hero"); let gs = 25; window.onload = () => { for (let i = 0; i < gs; i++) { for (let j = 0; j < gs; j++) { let dot = document.createElement("div"); dot.classList.add("dot"); dot.style.animationDelay = `${Math.sin(i * j) / 2}s`; holder.appendChild(dot); } } };
CSS
.hero { /* background-image: url(media/Background.jpg); */ height: 100vh; position: absolute; width: 100%; display: grid; grid-template-columns: repeat(50, 90px); grid-template-rows: repeat(50, 90px); overflow: hidden; text-align: left; } .dot { width: 2px; height: 2px; animation-name: wavyDots; animation-duration: 1s; animation-iteration-count: infinite; animation-direction: alternate-reverse; z-index: -1; } .dot:nth-child(odd) { background-color: #0cf5d5; } .dot:nth-child(even) { background-color: #e0017a; } @keyframes wavyDots { 0% { transform: translate(0, 0); } 100% { transform: translate(30px, 30px); } }
优化方案
1. 用DocumentFragment批量插入DOM,减少重排
每次调用appendChild都会触发浏览器重排,改用DocumentFragment一次性插入所有节点,大幅减少渲染阻塞:
window.onload = () => { let fragment = document.createDocumentFragment(); for (let i = 0; i < gs; i++) { for (let j = 0; j < gs; j++) { let dot = document.createElement("div"); dot.classList.add("dot"); dot.style.animationDelay = `${Math.sin(i * j) / 2}s`; fragment.appendChild(dot); } } holder.appendChild(fragment); // 仅一次DOM插入操作 };
2. 匹配Grid布局与实际点阵数量
当前Grid设置为repeat(50, 90px),但实际只生成25x25个点,布局规则与元素数量不匹配会浪费渲染资源,修改为:
.hero { /* 其他样式保持不变 */ display: grid; grid-template-columns: repeat(25, 1fr); /* 和生成的列数一致 */ grid-template-rows: repeat(25, 1fr); }
3. 开启GPU加速,优化动画性能
给.dot添加will-change属性提示浏览器提前准备动画加速;用translate3d替代translate,强制触发GPU渲染:
.dot { /* 其他样式保持不变 */ will-change: transform; } @keyframes wavyDots { 0% { transform: translate3d(0, 0, 0); } 100% { transform: translate3d(30px, 30px, 0); } }
4. 改用Canvas渲染(性能最优方案)
大量DOM节点的动画性能远不如Canvas,用Canvas绘制点阵并实现动画,能减少90%以上的性能消耗:
window.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const hero = document.getElementById('hero'); // 设置Canvas尺寸为容器大小 canvas.width = hero.offsetWidth; canvas.height = hero.offsetHeight; canvas.style.cssText = 'position:absolute;top:0;left:0;z-index:-1;'; hero.appendChild(canvas); const dotSize = 2; const gridSize = 25; const dotSpacingX = canvas.width / gridSize; const dotSpacingY = canvas.height / gridSize; const dots = []; // 初始化点阵数据 for (let i = 0; i < gridSize; i++) { for (let j = 0; j < gridSize; j++) { const delay = Math.sin(i * j) / 2; dots.push({ x: j * dotSpacingX, y: i * dotSpacingY, color: (i * gridSize + j) % 2 === 0 ? '#e0017a' : '#0cf5d5', delay: delay, startTime: performance.now() + delay * 1000 }); } } // 动画循环 function animate(timestamp) { ctx.clearRect(0, 0, canvas.width, canvas.height); dots.forEach(dot => { if (timestamp < dot.startTime) return; // 计算动画进度,模拟CSS的alternate-reverse效果 const elapsed = (timestamp - dot.startTime) % 1000; const progress = elapsed / 1000; const finalProgress = progress < 0.5 ? progress * 2 : (1 - progress) * 2; const currentX = dot.x + 30 * finalProgress; const currentY = dot.y + 30 * finalProgress; ctx.fillStyle = dot.color; ctx.fillRect(currentX, currentY, dotSize, dotSize); }); requestAnimationFrame(animate); } // 监听窗口 resize,同步Canvas尺寸 window.addEventListener('resize', () => { canvas.width = hero.offsetWidth; canvas.height = hero.offsetHeight; }); requestAnimationFrame(animate); };
使用该方案时,需移除原.hero的Grid样式及所有.dot相关CSS。
补充说明
- Canvas方案性能优势极大,即使将网格扩大到50x50也不会出现明显卡顿;
- 若坚持使用DOM方案,优先采用DocumentFragment批量插入+GPU加速的组合优化;
- Canvas方案需添加窗口resize监听,确保点阵始终覆盖全屏。
内容的提问来源于stack exchange,提问作者Topu Rayhan Rabby Mamud
相关产品推荐
相关产品推荐

