Canvas悬停效果需手动实现坐标转换、命中检测、状态管理和平滑重绘四步闭环:先用getBoundingClientRect转换鼠标坐标,再通过isPointInShape判断图形内点击,接着用插值更新缩放/阴影等状态,最后由requestAnimationFrame驱动高性能重绘,并叠加DOM层保障无障碍支持。

Canvas本身不支持:hover伪类,所有悬停效果都得靠手动计算和重绘实现。核心在于坐标转换、命中检测、状态管理与平滑重绘四步闭环。
坐标转换与命中检测
Canvas是位图环境,没有DOM层级概念,必须把鼠标屏幕坐标转为Canvas坐标,再判断是否落在目标图形内:
- 用
canvas.getBoundingClientRect()获取Canvas在视口中的位置 - 减去left/top偏移,得到相对于Canvas左上角的x/y值
- 对每个可交互图形写专用检测逻辑:矩形用
x > left && x top && y ;圆形用距离公式<code>Math.sqrt(dx*dx + dy*dy) - 建议封装成
isPointInShape(x, y, shape)函数,便于复用和测试
状态驱动的动态反馈
悬停不是“开关式”切换,而是持续更新的状态流:
- 维护
hoveredIndex或hoveredItem变量,只在命中时更新,避免频繁赋值 - 缩放用插值过渡:
currentScale += (targetScale - currentScale) * 0.1,比直接赋值更自然 - 发光用
ctx.shadowBlur和ctx.shadowColor控制,悬停时设为20和'rgba(100, 180, 255, 0.8)',离开时回落到基础值 - 每次绘制前调用
ctx.save(),绘制后ctx.restore(),防止状态污染
高性能重绘策略
卡顿往往源于错误的触发时机:
- 不要在
mousemove里直接重绘——每秒可能触发60+次,CPU扛不住 - 只在
mousemove中更新状态(如hoveredIndex),把重绘交给requestAnimationFrame统一调度 - 重绘函数里先
clearRect()清画布,再按需绘制全部图形(含高亮、tooltip等) - tooltip文字建议用
fillText()绘制,位置跟随鼠标偏移10px,避免遮挡目标点
结合DOM增强可用性
纯Canvas交互对键盘用户和屏幕阅读器不友好,建议叠加轻量DOM层:
- 在Canvas同级位置放一个
<div class="tooltip"></div>,用CSS定位并隐藏 - 悬停命中时,用JavaScript更新该DOM的内容和
style.left/style.top - 添加
tabindex="0"和aria-hidden="true",确保焦点与语义同步 - 这样既保留Canvas渲染性能,又满足无障碍要求


















