Vue应用中大量Canvas元素调用fillRect失效问题求助
10000个Canvas批量fillRect失效问题的解决方案
问题根源
同步循环操作大量DOM/Canvas元素时,会阻塞浏览器渲染线程,导致部分绘制操作被延迟或丢弃;同时重复的DOM查询、数组查询也会加剧性能开销,最终引发偶尔失效的情况。
具体解决方案
1. 分批异步处理,释放渲染线程
将10000个元素拆分为小批次,通过requestAnimationFrame分批执行,给浏览器留出渲染时间,避免单次操作阻塞线程:
const batchSize = 100; // 可根据实际性能调整批次大小 let currentKey = 1; function processBatch() { const endKey = Math.min(currentKey + batchSize - 1, 10000); for (let key = currentKey; key <= endKey; key++) { const card = document.getElementById(key); if (!card) continue; if (!this.scratchedSet.has(key)) { const ctx = card.getContext('2d'); ctx.fillStyle = '#b6b6b4'; ctx.fillRect(0, 0, card.width, card.height); card.style.opacity = '1'; } else { card.style.opacity = '1'; card.style.outline = '2px solid #ef7200'; } } currentKey = endKey + 1; if (currentKey <= 10000) { requestAnimationFrame(processBatch.bind(this)); } } // 启动处理 processBatch.call(this);
2. 预缓存DOM元素与Canvas上下文
避免循环内重复执行getElementById和getContext,提前缓存所有需要操作的元素和上下文:
// 在mounted钩子中预缓存 mounted() { // 缓存所有canvas元素 this.cards = Array.from({length:10000}, (_, i) => document.getElementById(i+1)).filter(Boolean); // 缓存所有canvas上下文 this.canvasCtxs = this.cards.map(card => card.getContext('2d')); // 将scratchedValue转为Set,提升查询效率 this.scratchedSet = new Set(this.scratched.scratchedValue); } // 处理逻辑改为使用缓存数据 let currentIndex = 0; function processBatch() { const endIndex = Math.min(currentIndex + batchSize - 1, this.cards.length - 1); for (let i = currentIndex; i <= endIndex; i++) { const card = this.cards[i]; const key = i + 1; if (!this.scratchedSet.has(key)) { const ctx = this.canvasCtxs[i]; ctx.fillStyle = '#b6b6b4'; ctx.fillRect(0, 0, card.width, card.height); card.style.opacity = '1'; } else { card.style.opacity = '1'; card.style.outline = '2px solid #ef7200'; } } currentIndex = endIndex + 1; if (currentIndex < this.cards.length) { requestAnimationFrame(processBatch.bind(this)); } }
3. 优化数组查询性能
将scratchedValue数组转换为Set,把查询时间复杂度从O(n)降为O(1),大幅提升循环内的判断速度:
this.scratchedSet = new Set(this.scratched.scratchedValue); // 后续判断用:this.scratchedSet.has(key)
4. 避免重复绘制
维护一个标记集合,记录已经完成填充的Canvas,避免重复执行fillRect操作:
data() { return { filledCards: new Set() } } // 处理时判断 if (!this.scratchedSet.has(key)) { if (!this.filledCards.has(key)) { const ctx = this.canvasCtxs[i]; ctx.fillStyle = '#b6b6b4'; ctx.fillRect(0, 0, card.width, card.height); this.filledCards.add(key); } card.style.opacity = '1'; }
内容的提问来源于stack exchange,提问作者Sem
相关产品推荐
相关产品推荐

