如何实现鼓机Canvas网格方块点击切换为crimson颜色?
实现Canvas鼓机方块的点击交互功能
核心问题分析
Canvas是位图绘制,绘制的方块只是像素集合,没有独立的DOM元素,无法直接给单个方块绑定事件。需要通过记录方块的位置数据,在Canvas的全局点击事件中计算点击坐标对应的方块,更新状态后重新绘制画布。
完整解决方案代码
<canvas id="canvas" width="1920" height="1080"></canvas> <script> const drums = ["Crash", "CHiHat", "OHiHat", "Tom3", "Tom2", "Tom1", "Snare", "Kick"]; const rowCount = 8; const colCount = 16; const blockSize = 50; const blockGap = 5; // 对应你代码里55 = 50(方块大小)+5(间距) const startX = 100; // 初始化网格状态:false=默认灰色,true=crimson const grid = Array(rowCount).fill().map(() => Array(colCount).fill(false)); const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // 封装绘制网格的函数,状态变化后调用重绘 function drawGrid() { // 先清空画布,避免旧绘制内容残留 ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < rowCount; i++) { for (let v = 0; v < colCount; v++) { const x = startX + (blockSize + blockGap) * v; const y = (blockSize + blockGap) * i; // 根据状态设置填充色 ctx.fillStyle = grid[i][v] ? 'crimson' : '#ddd'; ctx.fillRect(x, y, blockSize, blockSize); ctx.strokeRect(x, y, blockSize, blockSize); } } } // 给Canvas绑定点击事件 canvas.addEventListener('click', (e) => { // 将鼠标全局坐标转换为Canvas内部相对坐标 const rect = canvas.getBoundingClientRect(); const clickX = e.clientX - rect.left; const clickY = e.clientY - rect.top; // 计算点击对应的行列 const col = Math.floor((clickX - startX) / (blockSize + blockGap)); const row = Math.floor(clickY / (blockSize + blockGap)); // 校验点击位置是否在有效方块范围内 if (col >= 0 && col < colCount && row >= 0 && row < rowCount) { // 切换方块状态:点击后反转颜色 grid[row][col] = !grid[row][col]; // 重绘网格更新视图 drawGrid(); } }); // 页面加载时初始绘制网格 drawGrid(); </script>
关键改进说明
- 状态存储:用二维数组
grid记录每个方块的激活状态,解决Canvas绘制后丢失方块信息的问题。 - 绘制封装:
drawGrid函数统一处理画布清空与方块绘制,状态变化后只需调用该函数即可更新视图。 - 坐标转换:通过
getBoundingClientRect()将鼠标全局坐标转为Canvas内部坐标,确保点击位置计算准确。 - 边界校验:避免点击Canvas空白区域时触发无效的数组操作。
原代码的问题点
- 循环内重复获取
canvas和context,冗余且低效。 - 未存储方块的位置与状态,无法在点击时定位到具体方块。
- 没有重绘逻辑,绘制完成后无法更新方块颜色。
内容的提问来源于stack exchange,提问作者user20482298
相关产品推荐
相关产品推荐

