Angular中Canvas仅绘制最后一次迭代:棋盘实现故障求助
解决Angular中Canvas绘制棋盘仅显示最后一个方块的问题
兄弟,我太懂这个坑了!这是前端开发里超常见的异步闭包陷阱,当年我刚摸Canvas的时候也踩过一模一样的雷。问题出在你循环里的img.onload回调上——它是异步执行的,当所有图片的onload触发时,外层循环早就跑完了,posx和posy已经变成最后一轮循环的数值,所以所有drawImage都会把图片画在同一个最终位置,自然就只看到最后一个方块。
给你两种靠谱的解决方案,按需选:
方案一:预加载图片后同步绘制(推荐)
这种方法不仅能解决异步问题,还能避免重复创建Image对象、重复请求资源,性能更优:
createBoard(): void { const row = 8; const col = 8; let x = 1; let y = 'a'; const ctx = this.canvas.nativeElement.getContext('2d'); // 预加载棋盘的两种方块图片 const lightSquare = new Image(); const darkSquare = new Image(); let loadedImageCount = 0; const totalImages = 2; // 所有图片加载完成后再开始绘制棋盘 const startDrawing = () => { loadedImageCount++; if (loadedImageCount === totalImages) { this.drawChessboard(ctx, lightSquare, darkSquare); } }; lightSquare.onload = startDrawing; darkSquare.onload = startDrawing; lightSquare.src = "assets/img/Boardsquare_light_normal.png"; darkSquare.src = "assets/img/Boardsquare_dark_normal.png"; } // 抽离绘制逻辑,代码更清晰 private drawChessboard(ctx: CanvasRenderingContext2D, lightImg: HTMLImageElement, darkImg: HTMLImageElement): void { let posy = 0; let x = 1; let y = 'a'; for(let i = 0; i < 8; i++){ let posx = 0; for(let j = 0; j < 8; j++) { // 根据行列和判断使用亮/暗方块 const currentImg = (i + j) % 2 === 0 ? lightImg : darkImg; // 图片已加载完成,直接同步绘制 ctx.drawImage(currentImg, posx, posy, 75, 75); y = this.nextChar(y); posx += 75; } x += 1; y = 'a'; posy += 75; } }
方案二:捕获循环变量的快照(快速修复)
如果不想大改代码,只需要在循环里把当前的posx和posy存成独立变量,让每个onload回调引用自己的变量快照就行:
createBoard(): void { let row = 8; let col = 8; let x = 1; let y = 'a'; let posx = 0; let posy = 0; let ctx = this.canvas.nativeElement.getContext('2d'); for(let i = 0; i < row; i++){ posx = 0; for(let j = 0; j < col; j++) { // 关键:用const保存当前循环的位置快照 const currentPosX = posx; const currentPosY = posy; let img = new Image(); img.onload = () => { // 这里引用的是当前循环的快照变量,不是外层会变化的posx/posy ctx.drawImage(img, currentPosX, currentPosY, 75, 75); } if ((i + j) % 2 == 0) { img.src = "assets/img/Boardsquare_light_normal.png"; } else { img.src = "assets/img/Boardsquare_dark_normal.png"; } y = this.nextChar(y); posx += 75; } x += 1; y = 'a'; posy += 75; } }
为什么第二种方案有效?
因为const currentPosX和currentPosY是在每次内层循环里创建的新变量,每个img.onload箭头函数会捕获自己所在循环迭代的这两个变量,不会被后续循环的数值覆盖。
内容的提问来源于stack exchange,提问作者senna terpstra
相关产品推荐
相关产品推荐

