我用纯CSS实现了一个折线图,聊聊背后的思路

阿乐
阿乐 管理员
发布于 2026-09-13 02:09 ·2 浏览 ·0 回复

最近在做一个纯展示型的后台面板,需求很简单:一张静态折线图,数据一个月才更新一次。为了这么个小东西引一个几百 KB 的图表库,总觉得有点杀鸡用牛刀。于是我花了两个晚上,试着用纯 CSS 把它磕了出来。过程比想象中有趣,也比想象中啰嗦,这里把思路和踩到的坑记一记,供同样想「偷懒」的同学参考。

拆解:折线图到底由什么构成

先把图形降维成基础元素看:

- 一个坐标系,通常是背景网格;
- 若干数据点,圆形小点;
- 点与点之间的连线,本质是旋转过的矩形;
- 悬停时的高亮和数值提示。

关键在于第三项:任何一条斜线段,都可以看作一个细长的 div 旋转一定角度。想清楚这一点,整件事就变成了中学几何题——已知两点坐标,求线段长度和倾角。

第一步:用 CSS 变量搭坐标系

我的做法是让每个点自己声明数据坐标,用 CSS 变量传进去,容器负责换算成百分比:

.chart {
  --w: 600;
  --h: 240;
  position: relative;
  background-image: repeating-linear-gradient(...); /* 网格 */
}

.point {
  position: absolute;
  left: calc(var(--x) / var(--w) * 100%);
  bottom: calc(var(--y) / var(--h) * 100%);
  width: 8px;
  height: 8px;
  translate: -50% 50%;
}

好处是 HTML 只关心业务数据,`--x: 3; --y: 87`,不需要在 JS 里做像素换算。网格线直接用 `repeating-linear-gradient` 铺出来,横竖各一层,`background-size` 控制密度,比画一堆 div 干净得多。

第二步:把两个点连成一条线

线段元素的定位点放在起点,宽度由两点距离决定:

.segment {
  position: absolute;
  left: calc(var(--x1) / var(--w) * 100%);
  bottom: calc(var(--y1) / var(--h) * 100%);
  --dx: calc(var(--x2) - var(--x1));
  --dy: calc(var(--y2) - var(--y1));
  width: calc(sqrt(pow(var(--dx), 2) + pow(var(--dy), 2)) / var(--w) * 100%);
  transform: rotate(calc(-1 * atan2(var(--dy), var(--dx))));
  transform-origin: left center;
  height: 2px;
}

这里用到了 CSS 的数学函数:`sqrt()`、`pow()`、`atan2()`。放在两年前这套写法根本不成立,得在构建时把角度算好再注入成变量。现在主流浏览器基本都支持了,能省掉一层预计算,代价是——注意 `atan2` 的 y 轴方向,屏幕里的 y 是向下增长的,而我们的数据是向上增长的,所以 rotate 时要取负号,否则整张图会镜像翻转。这是我调了半小时才发现的。

第三步:那些不那么优雅的地方

真写起来,会遇到几个绕不过去的问题。

一是 DOM 数量。 20 个点就是 19 条线段、20 个圆点,再多就不好看了。所以这套方案的天花板很低,撑死几十个数据点。

二是宽度换算的误差。 用百分比定义线段长度,父容器一旦缩放,角度不会跟着变,线就会「错位」。严谨一点,长度也该用 `calc()` 基于同一个基准算,或者干脆固定容器尺寸、整体用 `zoom` 或 `scale` 缩放。

三是语义化。 满屏的 div 对读屏软件完全不友好,该加的 `aria-label`、数据表格该补还是得补。别因为「纯 CSS」就把无障碍丢了。

交互倒是有惊喜:数据点用 `:hover` 配合 `::after` 显示数值,纯 CSS 就能做出不错的 tooltip,连 JS 都省了。

纯 CSS 图表的边界在哪

折腾完这一轮,我的结论是:纯 CSS 图表适合「静态、少量、一次性」的场景——演示页、邮件模板、博客里的示意图,甚至比引库更快更稳。但一旦涉及实时数据、密集点集、缩放拖拽、图例联动,它的复杂度会指数级上升,这时候老老实实上 ECharts 或者 D3 才是理性选择。

还有个很实际的考虑:可维护性。三个月后接手的人看到一坨 `atan2` 和 `pow`,第一反应大概是「谁写的这玩意」。所以我在代码里留了注释,把每个魔法的来历写清楚了。

写在最后

用 CSS 画图这件事,价值未必在于真的上线,而在于它会逼你重新理解布局、变换和层叠——那些平时被框架封装掉的基础。写完之后我对 `transform-origin` 和坐标变换的理解,确实比之前扎实了不少。

当然,工具没有高下,只有合不合适。想清楚自己的场景,比炫技重要得多。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-282.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~