动态生成的多个canvas仅支持单画布绘制的解决方案求助
问题原因
- 你声明的
canvas、ctx以及坐标、绘制状态变量都是全局作用域,循环给多个canvas绑定事件的时候,这些全局变量会被后续循环的值覆盖,最终所有事件触发时用的都是最后一个canvas的上下文和状态,所以只能在一个画布上绘制。
解决方案
核心思路是把每个画布的上下文、绘制状态和事件逻辑独立封装,不要用全局变量共享状态。
修改后的代码示例
HTML部分动态生成canvas的逻辑无需调整,保证id为can_xxx的格式即可,JS代码修改如下:
function init() { // 如果canvas是动态生成的,也可以直接用document.querySelectorAll('canvas')获取所有画布,不用固定循环范围 for(let i=1;i<3;i++){ const canvas = document.getElementById('can_'+i); const ctx = canvas.getContext("2d"); // 每个画布独立维护状态变量,不会互相覆盖 let flag = false, prevX = 0, currX = 0, prevY = 0, currY = 0, dot_flag = false; const strokeColor = "red"; const lineWidth = 2; // 绘制函数封装在循环内,使用当前画布的ctx实例 function draw() { ctx.beginPath(); ctx.moveTo(prevX, prevY); ctx.lineTo(currX, currY); ctx.strokeStyle = strokeColor; ctx.lineWidth = lineWidth; ctx.stroke(); ctx.closePath(); } // 坐标计算逻辑也封装在内,直接使用当前循环的canvas实例 function findxy(res, e) { const rect = canvas.getBoundingClientRect(); if (res == 'down') { prevX = currX; prevY = currY; currX = e.clientX - rect.left; currY = e.clientY - rect.top; flag = true; dot_flag = true; if (dot_flag) { ctx.beginPath(); ctx.fillStyle = strokeColor; ctx.fillRect(currX, currY, 2, 2); ctx.closePath(); dot_flag = false; } } if (res == 'up' || res == "out") { flag = false; } if (res == 'move') { if (flag) { prevX = currX; prevY = currY; currX = e.clientX - rect.left; currY = e.clientY - rect.top; draw(); } } } // 绑定事件使用当前画布对应的findxy逻辑 canvas.addEventListener("mousemove", function (e){ findxy('move', e) }, false); canvas.addEventListener("mousedown", function (e) { findxy('down', e) }, false); canvas.addEventListener("mouseup", function (e) { findxy('up', e) }, false); canvas.addEventListener("mouseout", function (e) { findxy('out', e) }, false); } }
优化说明
- 所有和单个画布相关的状态、方法都封装在循环的块级作用域内,每个画布的状态完全独立,不会互相干扰
- 提前缓存
getBoundingClientRect()的调用结果,避免重复计算提升性能 - 后续如果要新增更多canvas,只需要调整循环逻辑或者直接遍历所有canvas元素即可,无需修改核心绘制逻辑
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

