flotr2轻量级图表库快速上手:从折线图到饼图
2026/9/8 1:51:49 网站建设 项目流程

简介:面向需要在前端网页中绘制交互式图表的前端开发者与数据分析人员,压缩包提供经压缩优化的 Flotr2 核心 JS 库及配套示例。整个包共 3 个文件,含 1 个核心 js 文件、1 个演示用 html 页面和 1 个记录官方 Demo 地址的 txt 文档,体积仅 33KB,轻量易用。示例页展示了引入库、准备数据、调用 Flotr.draw 绘制图表、配置颜色轴与图例等基本流程;配合 txt 中的官方 Demo 链接,还可进一步学习折线图、柱状图、饼图、散点图等多种图表类型,以及事件交互、响应式重绘和性能优化方法。资源已有 352 人学习,适合想快速上手 Flotr2 并搭建数据可视化界面的入门开发者。 最近做内部运营后台的报表页面,需要在不引入重框架的情况下快速画几张趋势图,翻来翻去找到了 flotr2 这个老牌轻量级图表库。单个 min.js 文件压缩后不到 80KB,没有 jQuery 依赖,不挑框架,几十行代码就能出图,非常适合中小型页面上画折线图、柱状图和饼图。这篇文章就把 flotr2.min.js 的包体结构、引入方式和最简单的一批使用例子整理出来,给同样在做轻量前端可视化的朋友一个可以直接抄作业的参考。

1. 先搞清楚 flotr2.min.js 这个包到底装了什么

1.1 单文件架构带来的部署优势

flotr2 是经典绘图库 Flot 的 Canvas 重写版,跟老 Flot 最大的区别就是彻底抛弃了对 jQuery 的依赖。整个库被打包成flotr2.min.js一个文件,里面包含完整的数据处理、坐标轴计算、图形绘制、鼠标跟踪和颜色生成逻辑。部署的时候只需要把这个文件丢到静态资源目录,然后通过普通 script 标签引入即可,不需要 npm install 一系列传递依赖。

项目里如果用的是原生 JavaScript、老旧的 jQuery 页面、或者 Vue 2 的简单 CDN 模式,这个单文件方案都很友好。压缩后的文件体积大概在 77KB 左右,gzip 以后更小,对首屏加载性能几乎没影响。相比 ECharts 动辄几百 KB 的体积,flotr2 在纯图表需求下能省下大量带宽。

1.2 获取渠道和版本确认

在官网 flotr2.org 可以下载到完整包和文档,目前实际稳定的版本号是 1.1.0。国内访问官网偶尔不稳定,所以更推荐通过 cdnjs 或 unpkg 这类公共 CDN 直接引用,例如:

<script src="https://cdnjs.cloudflare.com/ajax/libs/flotr2/1.1.0/flotr2.min.js"></script>

这里建议不要用不带版本号的最新链接,因为公共 CDN 的缓存策略偶尔会更新主版本,带给前端页面不可控的行为变化。固定版本号之后,包体行为可预期。

1.3 全局对象的 API 核心

引入脚本之后,浏览器环境下会暴露一个全局对象Flotr。不要被这个名字误导,它不是一个构造函数,而是一个静态工具集合。最核心的方法是:

Flotr.draw(container, data, options)

三个参数的含义分别是:

  • container:DOM 元素或元素 id,绘图容器,必须已经存在于文档中且具有非零宽高。
  • data:数组,每个元素代表一个数据序列,格式为{ data: [[x1, y1], [x2, y2], ...], label: '系列名' }
  • options:对象,控制坐标轴、网格、图形样式、图例、提示交互等。

还有一个比较常用的小工具方法是Flotr._.color这类内部工具,实际开发中用得不多,通常只需掌握draw方法的配置习惯就足够应付 90% 的场景了。

2. 选型理由:flotr2 跟 ECharts、Chart.js 定位有什么不同

2.1 无依赖和极低上手成本

现在前端做个图表,很多人第一反应就是 ECharts 或 Chart.js。这两个库功能确实全面,但会引入模块化工程、打包配置等一堆工具链成本。flotr2 不需要这些东西,一个 script 标签加上几行 JSON 结构的配置就能画图。

尤其是做内部系统、管理后台的小型统计模块,本身不需要渐变色、3D 效果、大规模数据流这种炫酷能力,只想干净利落地展示趋势,flotr2 的性价比就很高。所有能力都被封装成linesbarspointspie这几个选项,翻文档的成本极低。

2.2 跟 Flot 的关系和取舍差异

Flot 作为老牌图表库也有不少存量用户,但它强依赖 jQuery。在一个已经移除 jQuery 的新项目里为了画个简单图表再装回 jQuery,显然不合理。flotr2 设计上就瞄准了这个痛点,API 保留了 Flot 的数据结构和 options 风格,但底层换成了 Canvas 渲染,不再操作 DOM,动画和重绘效率也更好。

这里做一个直观对比:

对比项flotr2 1.1.0ECharts 5Chart.js 4
依赖外部库
压缩后体积约 77KB约 300KB 起约 200KB 起
渲染方式CanvasCanvas/SVGCanvas
学习成本中高
图表类型丰富度基础类型多全面基础类型多
模块化工程适配需要手动包装优秀优秀

如果你的页面只有几个基础图表,不涉及 Webpack 模块导入、不搞懒加载分包,最省事的路径就是用 flotr2 这种体量的小库。

2.3 什么情况不建议用它

也要说句公道话,flotr2 的维护频率现在不高,很久没有大版本更新了。如果项目需要复杂交互动画、地图下钻、海量数据实时刷新,或者要求 TypeScript 类型定义完善,就建议直接选择 ECharts 或 Chart.js。flotr2 更适合把“快速出图”放在第一优先级、功能需求比较固定的场景。选工具前想清楚自己的边界,才不会后期返工。

3. 最简单的折线图:三行核心代码能跑起来

3.1 数据格式和容器准备

先准备一个带宽高的 div,注意宽度和高度必须显式设置,否则 Canvas 会留下一个默认 300 像素宽、150 像素高的隐形画布,看起来图表就被截断了,这个稍后会在坑点里细说。示例:

<div id="simpleChart" style="width: 100%; height: 350px;"></div>

接着构造数据。flotr2 的数据允许[x, y]点数组,也支持[x, y, marker]形式,第三个字段可以在某些事件里取到。最简单的折线图数据长这样:

var data = [ { data: [[1, 30], [2, 42], [3, 38], [4, 55], [5, 67]], label: '访问量' }, { data: [[1, 20], [2, 25], [3, 31], [4, 36], [5, 48]], label: '新增用户' } ];

3.2 调用 draw 并配置最小选项

绘制只需一行方法调用:

Flotr.draw(document.getElementById('simpleChart'), data, { xaxis: { min: 0, max: 6 }, yaxis: { min: 0 }, lines: { show: true }, points: { show: true }, legend: { position: 'se' } });

这段配置的含义很直白:x 轴范围限定在 0 到 6,y 轴从 0 开始,两个系列都用折线加数据点显示,图例放在东南角也就是右下角。运行后浏览器里就会渲染出两条带网格背景的折线图,效果干净利落。

3.3 坐标轴范围为什么建议手动指定

如果数据点只有几个,flotr2 默认会自动扩展坐标轴范围。但自动扩展偶尔会留下多余的空白区域,或者导致折线贴着画布边缘。手动指定minmax,能控制图形的留白比例。比如 x 轴数据最大是 5,我习惯把 max 设成 6,这样最右侧点和右边框之间有一些呼吸感,视觉上更舒服。同理,y 轴 min 设成 0 比设成自动最小值更符合阅读习惯,大部分业务图表从 0 开始比较直观。

4. 从折线延伸到柱状图、饼图和时间序列

4.1 柱状图的配置和 barWidth 计算

柱状图与折线图共用同一套数据格式,只是在 options 里把lines换成bars

Flotr.draw(container, data, { bars: { show: true, barWidth: 0.5, lineWidth: 1, fill: true, fillOpacity: 0.8 }, grid: { verticalLines: false, horizontalLines: true } });

barWidth在这里值得单独说一下,它不指定像素,而是指定柱子在 x 轴坐标系中的宽度。比如 x 轴相邻两个整数的间隔是 1,那么barWidth: 0.5表示柱子宽度占间隔的一半。多个系列并排时,总宽度需要拆分为多个柱子的宽度总和,否则柱子会重叠。我的习惯是两个系列时总宽度 0.8,每个柱子 0.38,中间留一点间隙。

4.2 饼图:依赖数据序列的数值占比

饼图也是很多人常用的,配置比柱状图还简单:

var pieData = [ { data: [[1, 46]], label: '直接访问' }, { data: [[1, 31]], label: '搜索引擎' }, { data: [[1, 23]], label: '外链引流' } ]; Flotr.draw(container, pieData, { pie: { show: true, explode: 4 }, legend: { position: 'ne' } });

这里每个data数组里的 x 值并不影响饼图结果,真正被拿去计算占比的是 y 值。explode: 4控制每一块扇形跟中心点的距离,数值越大,分块之间的缝隙越夸张。饼图做出来以后,建议在相关文档里把百分比计算方式也跟业务方对好,因为 flotr2 的饼图标签默认不会自动打出百分比,需要额外处理。

4.3 时间序列的 x 轴格式化

业务里常见的是按日期展示数据,比如“8月1日到8月7日”的销售额。此时 x 轴如果直接用时间戳传入,横轴刻度会显示成一长串数字,非常难看。可以用tick回调格式化:

Flotr.draw(container, data, { xaxis: { mode: 'time', timeFormat: 'M月d日', tickFormatter: function (value) { var date = new Date(value); return (date.getMonth() + 1) + '月' + date.getDate() + '日'; } }, lines: { show: true }, points: { show: true } });

其实mode: 'time'在 flotr2 里并非像 Flot 那样有一整套封装逻辑,很多时候直接用tickFormatter手动格式化更可控。数据 x 值依然用时间戳毫秒数,格式化函数负责把刻度值转成人类友好文字。用原生 Date 对象处理即可,不需要额外引入日期库,对于内部系统来说这部分代码足够精简。

5. 交互增强:鼠标提示、网格线与图例控制

5.1 鼠标跟踪提示的配置

图表不带任何提示时,用户没法直接知道某个点的精确数值,只能靠肉眼在坐标轴之间估算。flotr2 提供了mouse.track系列选项,打开后会随着鼠标移动显示十字准线和悬浮框:

Flotr.draw(container, data, { mouse: { track: true, lineColor: '#999', position: 's', sensibility: 2, trackFormatter: function (obj) { return obj.series.label + ':' + obj.x + ',' + obj.y; } } });

trackFormatter是高度自定义的,回调参数obj里包含xyseriesindex等字段。实际项目里我通常会返回更丰富的 HTML 字符串,比如带小圆点的彩色图例和多个系列值的汇总,这里就展现出 flotr2 的灵活性了:它不限制返回内容的格式,返回 HTML 字符串就能被浮层渲染出来。

5.2 网格、图例和标题的视觉收敛

大多数内部系统的图表都有固定的 UI 风格,网格线太密会显得杂乱。我一般这样收敛视觉:

grid: { color: '#e6e6e6', verticalLines: false, horizontalLines: true, outlineWidth: 0 }, legend: { position: 'ne', backgroundColor: '#fff', noColumns: 2 }, title: '近七日运营趋势'

outlineWidth: 0会让外边框消失,视觉上更轻盈。noColumns: 2控制图例每行显示 2 个条目,避免系列多的时候图例纵向拉得很高。标题直接通过title参数渲染到画布内,不需要额外在 DOM 里写标题元素,这样画布整体居中时标题也会跟着走。

5.3 自适应重绘策略

页面 resize 时,Canvas 画布不会自动跟着容器改变尺寸,需要监听 resize 事件重新调用 draw。推荐用 requestAnimationFrame 做节流,避免快速拉伸窗口时连续触发重绘导致卡顿:

window.addEventListener('resize', function () { window.requestAnimationFrame(function () { Flotr.draw(container, data, options); }); });

这里还有一个隐含的细节:重新调用 draw 之前最好把容器内部的 canvas 节点清理掉。虽然 flotr2 的 draw 方法内部会尽量复用画布,但在某些极端情况下会叠加多个 canvas,导致图表图层错乱。稳妥做法是直接清空容器:

container.innerHTML = ''; Flotr.draw(container, data, options);

6. 实际项目中容易踩的坑和对应的处理方式

6.1 容器宽高没有提前占位导致图表变形

这个问题排在我踩坑榜的第一位。如果 div 在页面一开始没有高度,或者通过display:none隐藏着,等用户点击某个 Tab 再切回来时,flotr2 在初始化时拿到的宽高是 0,画出来就是一块 300 乘 150 的默认画布。解决办法是在 Tab 切换完成后,或者容器已经显示时再调用 draw。如果无法保证时序,可以在 draw 之前先读取container.clientWidthclientHeight,发现有零值就延迟重试。

6.2 数据量太大时线条卡顿

flotr2 定位轻量级,不适合一次性塞几万个点。我实测在渲染超过 5000 个点时,重绘交互能明显感受到延迟。如果数据源确实有大量采样点,前端可以在喂给图表之前做抽稀,比如均匀取每 50 个点保留 1 个,或者用桶聚合的方式把一段时间内的值做平均。趋势图形状几乎不受影响,性能却能提升一个量级。

6.3 坐标轴刻度文字互相挤压

当时间跨度大或者刻度点较多时,x 轴刻度文字水平排列容易重叠。处理办法是设置labelsAngle,让文字倾斜显示:

xaxis: { min: 0, max: 30, tick: 3, labelsAngle: 45 }

tick控制每隔多少单位显示一根刻度线,labelsAngle控制文字旋转角度。这两个参数搭配起来,可以在不加大画布高度的情况下容纳更多刻度信息。

6.4 工具提示浮层的样式定制

默认的悬浮提示样式比较朴素,白底黑字,和当前设计稿可能不搭。flotr2 的浮层渲染逻辑是基于返回字符串自动创建一个 div,可以借用自定义 class 来实现样式覆盖:

trackFormatter: function (obj) { return '<div class="my-tip">' + obj.series.label + '<br/>' + obj.y + '</div>'; }

然后在页面样式表里写.my-tip的边框、背景、圆角、阴影。要做到这一点,前提是确认页面中其他地方没有全局的.flotr-popup样式覆盖。内部系统里多人协作时偶尔会有人把全局样式写得很宽,影响浮层布局,这时候建议给浮层加上一个足够独特的父级 class,让优先级可控。

6.5 多个图表实例的内存管理

页面上同时存在多个 flotr2 图表时,除了每次重绘前清理容器,最好在页面卸载或模块销毁时主动把不再使用的容器清空。虽然现在的浏览器大部分情况会回收内部资源,但长时间单页应用运行时,频繁创建和销毁图表而忘记清理,最终还是会看到内存小幅爬升。养成container.innerHTML = ''的习惯成本很低,能避免后续排查内存问题时的一堆麻烦。

7. 一个小技巧:用自定义 HTML 做成可复用组件

最后再分享一个在实际工作中很实用的封装方式。虽然 flotr2 本身没有组件化的东西,但在 Vue 或 React 项目中可以把它包成一个极简组件。以 Vue 2 为例,核心思路是在mounted钩子里调用 draw,在watch里监听数据变化触发重绘:

mounted: function () { Flotr.draw(this.$el.querySelector('.chart'), this.chartData, this.chartOptions); }, watch: { chartData: function () { this.$el.querySelector('.chart').innerHTML = ''; Flotr.draw(this.$el.querySelector('.chart'), this.chartData, this.chartOptions); } }

这样业务页面只需要维护chartDatachartOptions两个对象,底层绘图逻辑被收敛在一个文件里。遇到需要调整图表样式的情况,改一个地方就够,不需要每张页面各自为战。这个小封装在我做过的三四个运营后台项目里一直在用,稳定性和维护成本表现都不错。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询