如何修改JavaScript自定义Canvas按钮对象的鼠标悬停颜色
问题原因梳理
- HTML元素类型错误:你原本写的
<div id="JScanvas"></div>是div标签,无法获取Canvas绘图上下文,首先要修正为canvas标签。 - 事件监听重复绑定:你把
mousemove监听放在了show()方法里,每次调用show()都会新增一个监听事件,会导致内存泄漏和逻辑异常。 - 缺少自动重绘机制:Canvas是立即绘图模式,修改属性后不会自动更新视图,你修改
btnColor后没有主动清空画布并重新调用show()方法,所以看不到颜色变化。 - 悬停逻辑缺失:你注释掉了鼠标位置和按钮范围的判断代码,不管鼠标位置在哪都会修改按钮颜色,不符合需求。
修复方案
1. 修正HTML代码
<canvas id="JScanvas" width="500" height="300"></canvas>
2. 优化JS逻辑
调整Button类结构,把事件监听移到构造函数中,新增重绘逻辑,完善悬停判断:
let canvas = document.getElementById("JScanvas"); let c = canvas.getContext("2d"); let mousePosition = { x: 0, y: 0 }; // 工具函数保留 function buildRect(fillColor, outlineColor, outlineSize, x, y, w, h) { if (fillColor && outlineColor) { c.beginPath(); c.rect(x, y, w, h); c.fillStyle = fillColor; c.fill(); c.lineWidth = outlineSize; c.strokeStyle = outlineColor; c.stroke(); } else if (fillColor && !outlineColor) { c.beginPath(); c.rect(x, y, w, h); c.fillStyle = fillColor; c.fill(); } else if (!fillColor && outlineColor) { c.beginPath(); c.rect(x, y, w, h); c.lineWidth = outlineSize; c.strokeStyle = outlineColor; c.stroke(); } } class Button { constructor(name, order, btnColor, x, y, w, h, txtColor, txtSize, m, f) { this.name = name; this.order = order; this.originalColor = btnColor[0]; // 保存原始颜色,鼠标离开时恢复 this.btnColor = btnColor; this.x = x; this.y = y; this.w = w; this.h = h; this.txtColor = txtColor; this.txtSize = txtSize; this.m = m; this.f = f; // 绑定悬停监听,只绑定一次 this.bindHoverEvent(); } bindHoverEvent() { this.m.canvs.addEventListener("mousemove", (event) => { // 判断鼠标是否在按钮范围内 const isHover = this.x < this.m.mPosition.x && this.m.mPosition.x < this.x + this.w && this.y < this.m.mPosition.y && this.m.mPosition.y < this.y + this.h; if (isHover) { this.btnColor[0] = "grey"; } else { this.btnColor[0] = this.originalColor; } // 修改属性后主动重绘 this.redraw(); }); } redraw() { // 先清空画布,再重新绘制 c.clearRect(0, 0, canvas.width, canvas.height); this.show(); } show() { if (!this.btnColor[0] && !this.btnColor[1]) { buildRect("transparent", false, 1, this.x, this.y, this.w, this.h); } else if (!this.btnColor[0]) { buildRect(false, this.btnColor[1], 1, this.x, this.y, this.w, this.h); } else if (!this.btnColor[1]) { buildRect(this.btnColor[0], false, 1, this.x, this.y, this.w, this.h); } else { buildRect(this.btnColor[0], this.btnColor[1], 1, this.x, this.y, this.w, this.h); } c.fillStyle = this.txtColor; let theString = String(this.txtSize) + "px Arial"; c.font = theString; let width = Math.round(c.measureText(this.name).width); if (width > this.w) { let newSize = this.w / width * this.txtSize; c.font = String(newSize) + "px Arial"; } c.textAlign = "center"; c.textBaseline = "middle"; c.fillText(this.name, this.x + (this.w / 2), this.y + (this.h / 2)); } clickButton(mouseX, mouseY) { return mouseX >= this.x && mouseX <= this.x + this.w && mouseY >= this.y && mouseY <= this.y + this.h; } runf() { this.f(); } } // 获取鼠标位置 function getMousePos(canvas, evt) { const rect = canvas.getBoundingClientRect(); return { x: evt.clientX - rect.left, y: evt.clientY - rect.top }; } canvas.addEventListener("mousemove", (evt) => { let r = getMousePos(canvas, evt); mousePosition.x = r.x; mousePosition.y = r.y; }); let mouse = { canvs: canvas, mClicked: false, mPosition: mousePosition }; let cookie = new Button("cookie", 1, ["red", false], 50, 100, 150, 50, "black", 30, mouse, () => console.log("点击触发")); cookie.show();
核心改动说明
- 修正了HTML元素类型,补充了canvas宽高属性
- 将事件监听从show方法移到构造函数中,避免重复绑定
- 新增
redraw方法,修改颜色后主动清空画布并重绘 - 补充了悬停范围判断逻辑,新增原始颜色保存,鼠标离开后自动恢复原颜色
- 优化了文字宽度计算逻辑,修复了原代码中多余绘制文字的问题
内容的提问来源于stack exchange,提问作者robo
相关产品推荐
相关产品推荐

