You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 14:06:03