canvas手写gauge最可控:需用requestanimationframe分帧更新currentvalue向targetvalue渐变,配合linecap:'round'、手动旋转文字、指针2px偏移等细节,避免突变与渲染失真。

用 canvas 手写 gauge 最可控,别碰那些“一行引入”的 UI 库
绝大多数所谓“HTML 仪表盘” demo 都是假的——它们要么依赖庞大框架(如 Vue + ECharts),要么用 SVG 硬套动画,结果一改配色、一调刻度就崩。真正在嵌入式面板、工控屏或低配设备上跑 gauge,canvas 是唯一靠谱路径:轻量、可精确控制弧度/渐变/文字对齐,且不触发重排。
关键不是“怎么画圆”,而是怎么让指针动得自然、刻度不糊、数字不跳:
-
requestAnimationFrame控制动画帧,别用setTimeout或 CSStransition,后者在低端浏览器里指针会卡顿或突变 - 刻度线必须用
lineCap: 'round',否则 1px 线在斜角处被裁成方块,看起来像锯齿 - 文字标签要手动计算旋转角度并
ctx.save()/ctx.restore(),否则叠加多次后 canvas 状态错乱 - 指针尖端坐标不能靠
Math.sin/cos算完直接画线——得预留 2px 偏移,否则和表盘中心点重叠时视觉发虚
gauge.value = 73.5 这种赋值背后,实际要拆三步更新
看似简单的赋值操作,底层必须分阶段同步:当前值 → 动画中间值 → 渲染帧。跳过中间值直接渲染,数字会“啪”一下跳到目标,工业场景里这种突变会被判定为传感器异常。
实操建议:
- 维护一个
currentValue(当前渲染值)和targetValue(目标值),每帧按固定步长(如0.3)向目标靠近 - 当
Math.abs(currentValue - targetValue) 时才真正锁定,避免浮点误差导致永远差 0.000001 - 数值显示区域(比如
<div class="gauge-value">73.5%)必须和 canvas 指针异步更新:先更新 DOM 文字,再 requestAnimationFrame 更新 canvas,否则人眼会感知到文字比指针快半拍<h3>IE11 或 Electron 旧版 Chromium 下 <code>canvas渐变失效?检查addColorStop的 stop 值在老旧内核里,
addColorStop(0, '#f00')和addColorStop(1, '#0f0')看似合理,但一旦 stop 值超出[0, 1]范围(哪怕只是1.0000001),整条渐变就回退成纯色——而且不报错,只静默失败。常见诱因:
anything-to-html下载将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 用百分比算 stop 时写了
value / 100,但 value 可能是105(超量程报警状态) - 动态生成刻度时,把第 11 根线的 stop 设为
i / 10(i=10 → 1.0),但循环多跑了一次变成11/10 === 1.1 - Electron 旧版中
createLinearGradient的坐标若传入NaN(比如除零),也会让后续所有渐变失效
修复很简单:所有 stop 值强制截断,
Math.min(1, Math.max(0, computedStop))。响应式缩放时,
canvas.width/canvas.height必须重设,不能只改 CSS只给
canvas加style="width:100%; height:200px",会导致图像模糊、文字发虚、指针偏移——因为 CSS 缩放的是绘制后的位图,不是重绘逻辑。正确做法是监听容器尺寸变化(推荐用
ResizeObserver),然后:- 先重设
canvas.width和canvas.height为实际像素值(如canvas.width = container.clientWidth * window.devicePixelRatio) - 再调用
ctx.scale(devicePixelRatio, devicePixelRatio)补偿高清屏 - 最后重新绘制全部元素(包括背景弧、刻度、文字、指针),不能只 redraw 指针
漏掉
ctx.scale,在 MacBook 或安卓高刷屏上,指针边缘会明显锯齿;漏掉重绘全部,旧图形残留会和新指针叠出重影。最麻烦的点其实是刻度文字:字号不能写死,得按容器宽度动态算(比如
Math.max(12, container.clientWidth / 30)),否则小屏上“100%”四个字能占满半张表盘。 - 用百分比算 stop 时写了










