canvas画动态折线图最轻可控,需设行内宽高、时间转相对偏移、每帧clearrect、超200点降采样;svg适合低频更新,用polyline+title实现悬停;fetch配合requestanimationframe实时刷新并加abortcontroller;移动端须处理devicepixelratio、触摸坐标映射和字体缩放。

用 canvas 画动态折线图,别碰 Chart.js 就想省事
直接上 canvas 是最轻、最可控的方式——尤其当你只需要一个嵌入式趋势面板,不需交互、不需图例、不需导出。用 Chart.js 反而容易卡在主题配置、响应式重绘、数据更新抖动这些地方。真实项目里,一个 20 行核心绘制逻辑 + 10 行数据映射的 canvas 片段,比引入整包更稳。
关键点:
-
canvas的width/height必须设为行内属性(不是 CSS),否则缩放会拉伸坐标系 - 横轴时间戳建议转成相对偏移(如
data[i].time - data[0].time),避免浮点精度导致折线错位 - 每帧重绘前必须调用
ctx.clearRect(0, 0, canvas.width, canvas.height),漏掉就糊成一片 - 如果数据点超 200 个,先做简易降采样(如取极值点),不然
lineTo调用太多会卡顿
SVG 实现带悬停提示的趋势图,适合静态或低频更新
SVG 比 canvas 更易调试(DOM 可 inspect)、天然支持 CSS 动画和 hover 效果,但不适合高频重绘。如果你的趋势图只在页面加载后画一次,或者每分钟才更新一回,SVG 是更干净的选择。
实操注意:
- 用
<polyline></polyline>画折线时,points属性值必须是空格分隔的“x,y x,y”字符串,不能换行或逗号分隔 - 悬停提示建议用
<title></title>标签包裹在<polyline></polyline>内部,原生生效,不用 JS 绑定 - 如果要响应容器宽度,别用
width="100%",改用viewBox+ CSSmax-width控制缩放行为 - IE11 不支持
getScreenCTM()获取鼠标坐标,需 fallback 到clientX/clientY手动减去 SVG 偏移
用 fetch + requestAnimationFrame 实现实时趋势刷新,避开 setInterval
趋势图常要每秒拉新数据,但用 setInterval(() => fetch(...), 1000) 很危险:网络延迟叠加后,请求会堆积,UI 更新不同步,甚至出现倒序点。正确做法是 fetch 完成后再触发下一次,且用 requestAnimationFrame 对齐屏幕刷新节奏。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
示例逻辑:
function updateTrend() {
fetch('/api/metrics')
.then(r => r.json())
.then(data => {
// 更新数据数组,重绘
redraw(data);
// 下一帧再拉新数据
requestAnimationFrame(updateTrend);
});
}
updateTrend();
补充说明:
- 务必加
AbortController防止页面切走后还在发请求 - 服务端返回的数据应带时间戳字段(如
ts),不要依赖客户端Date.now()插值,时钟不同步会导致趋势歪斜 - 如果连续两次 fetch 返回相同数据,跳过重绘,避免无谓的 canvas 清空/重画
移动端适配折线图的三个硬伤点
很多趋势图在 PC 上看着正常,一到手机就压扁、点不准、字体糊——根本不是“加个 meta viewport”能解决的。
必须处理的细节:
-
canvas的设备像素比(window.devicePixelRatio)必须参与缩放计算,否则在 iPhone 上线条细得看不见 - 触摸事件的
touches[0].clientX要除以canvas.style.width对应的缩放系数,才能映射到正确的数据点 - 文字标签(如 Y 轴数值)别用
px单位,改用rem或通过ctx.scale动态调整字号,否则 iOS Safari 会强制放大文本破坏布局 - Android WebView 中
canvas.toDataURL()可能返回空字符串,截图需求得用html2canvas替代
真正难的不是画出一条线,而是让这条线在各种设备、各种网络、各种数据节奏下,始终不跳、不糊、不错位。越想“全平台兼容”,越要从第一行 canvas.getContext('2d') 开始控制变量。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










