Plotly.js — 一个 JSON 描述任何图表的浏览器全家桶
是什么
Plotly.js 不是给你一堆”画框”的工具,而是一种用纯 JSON 描述图表的标准——你写一个对象,浏览器替你画图、加交互、做导出。日常类比:D3 给你砖头自己砌房子,Plotly 给你装修菜单——勾几个选项,房子直接落成,连开关插座(缩放/悬停/导出)都接好了。
最小代码:
Plotly.newPlot('chart', [{ x: [1, 2, 3, 4], y: [10, 15, 13, 17], type: 'scatter'}], { title: '我的第一张图'})四行 JSON,浏览器里出一张可缩放、可框选、可导出 PNG 的折线图。没写一行 SVG,没绑一个事件。
40+ 种图表类型(散点、柱、饼、箱线、热力、3D 曲面、地图、桑基、旭日……)共用同一个对象骨架:data(数据 trace 数组)+ layout(标题/坐标轴/字体)+ config(工具条行为)。换图表只改一个字段:type: 'scatter' 改成 'bar' 就是柱状图,改成 'box' 就是箱线图。
为什么重要
不理解 Plotly.js,下面这些事都没法解释:
- 为什么 Python 里
fig = px.scatter(...)出来的图,保存成 JSON 拷到前端 Plotly.js 直接渲染 —— 因为 plotly.py / Plotly R / Plotly Julia / plotly.js 共用同一份 schema,4 个宿主语言一份文档 - 为什么 Dash / Streamlit / Gradio 的图表组件几乎都是它 —— 跨语言 + 框架无关 + 交互内置,做仪表盘的甜区
- 为什么 Jupyter 里图能拖能转能看 tooltip —— 那不是图片,是 plotly.js 在浏览器实时渲染的 JSON
- 为什么生物/金融/地理论文图大量用它 —— 3D 曲面 / 地图 / 候选烛 / 等高线这些”专业图”开箱即有,D3 要自己拼
核心要点
Plotly.js 的设计可以拆成 三个支点:
-
声明式 JSON schema:图表 =
{data, layout, config}一个对象。data是 trace 数组(每条曲线一个 trace),layout管全局外观,config管交互行为。学一次结构通吃 40+ 图表。 -
SVG / WebGL 混合渲染:默认走 SVG(清晰、可二次编辑、几千点流畅);点数上去切到 WebGL trace(
scatter→scattergl,仅改一个字符串),百万点仍能拖。底层早期基于 D3 做 SVG,后接入 stack.gl / regl 做 WebGL。 -
跨语言 schema 复用:plotly.py 的
fig.to_json()输出和浏览器JSON.stringify(div.data)字节级一致。Python 后端算图、前端原样渲染,中间不用转换层。
三点合起来:写一份 JSON,跨语言、跨规模、跨交互。
实践案例
案例 1:换图表只改一个字符串
const data = [{ x: ['周一', '周二', '周三'], y: [3, 7, 5], type: 'scatter' // 折线图}]Plotly.newPlot('chart', data)把 type: 'scatter' 改成 'bar' —— 柱状图。改成 'box' —— 箱线图(注意把 y 改成数组的数组)。同一个数据骨架,40+ 种图任选。
案例 2:内置交互一个不漏
Plotly.newPlot('chart', data, { hovermode: 'x unified' // 同 x 值多 trace 一起显示在 tooltip})页面上自动得到:
- 缩放:鼠标拖矩形框选区域;滚轮缩放
- 平移:按住拖
- 悬停 tooltip:跟随鼠标显示数值
- 图例:单击隐藏一条 trace、双击只显示这一条
- modebar:右上角工具条 — 下载 PNG / 重置视图 / 套索选择
零代码。要关掉某个交互就在 config 里改一个布尔。
案例 3:Python 算图 + 前端渲染(schema 共用)
Python 端:
import plotly.express as pxfig = px.scatter(df, x='gdp', y='life')json_str = fig.to_json() # 序列化成 JSON 字符串前端 JS:
const fig = JSON.parse(jsonStr)Plotly.newPlot('chart', fig.data, fig.layout)没有适配层。Python 算完直接拿 JSON 给前端,前端就是把那个 JSON 喂给 Plotly.js。Dash 框架就是把这个流程自动化做成 web app。
踩过的坑
-
全量 bundle ~3MB 压缩后:dist/plotly.min.js 含全部 40+ 图 + 3D + 地图,首屏会拖慢。生产用按需选 partial bundle:
plotly-basic/plotly-cartesian/plotly-geo/plotly-gl3d/plotly-finance—— 只引你要的那一部分。 -
Plotly.newPlotvsPlotly.react:前者每次销毁重建 DOM,数据频繁更新(实时仪表盘)会闪烁掉帧。频繁更新用Plotly.react—— 它做 diff,只改变化的部分,新人常用错。 -
WebGL context 上限:浏览器最多约 16 个 WebGL 上下文,超过会丢弃最早的。一页放 20 个
scatter3d会有图突然空白。解决:合并成一个图多 trace,或者部分 trace 退回 SVG。 -
SVG 文本跨浏览器 1-2px 偏差:出 PDF 报告时不同 Chrome / Safari 渲染的 SVG 文本居中略不同。生产报告流水线必须
Plotly.toImage(div, {format:'png'})转 PNG 再嵌 PDF,避免视觉抖动。
适用 vs 不适用场景
适用:
- 数据探索仪表盘(Dash / Streamlit / Jupyter)—— 交互内置、40 种图够用
- 跨语言协作(Python 同事算数据、前端同事展示)—— schema 一致免对齐
- 科研报告含 3D / 地图 / 统计高级图 —— 不用自己拼 D3
- 中等数据量(万级到百万级)—— WebGL trace 顶得住
不适用:
- 极致定制图形(不在 40 种内的奇异可视化)—— 用 D3 从砖头开始
- 极简首屏 / 移动端 banner —— 3MB bundle 太重,用 Chart.js(八种主流图、~70KB)
- 千万级流式数据 —— 仍然吃力,要 deck.gl / regl 直接吃 GPU
- 想完全 React 范式(组件即图表)—— Recharts / nivo 是 React-only 封装更顺手
历史小故事(可跳过)
- 2013:Plotly Inc. 在蒙特利尔创立,做在线绘图 SaaS
- 2014:内部图表引擎跨 Python / R / MATLAB 成型
- 2015:plotly.js 在 GitHub MIT 开源 —— 这是它进入主流前端的起点
- 2017:Dash 发布 —— 把 plotly.js 推上 Python 仪表盘主流舞台
- 2019:Plotly Express 发布 —— Python 高层 API,写法接近 seaborn
之后六年,plotly.js 成为科研、量化、生物信息、地理可视化默认选择之一。
学到什么
- 声明式 JSON 是跨语言图表的关键 —— 一份 schema 让 Python / R / Julia / JS 共享一份”画图语言”
- SVG 和 WebGL 不是二选一 —— 同一个库按数据规模自动切换渲染后端
- 交互内置是仪表盘场景的甜区 —— 缩放/悬停/导出零代码就能用,省下 80% 模板代码
- 乐高 vs 装修菜单 —— D3 / Plotly / ECharts / Chart.js 是同一光谱不同高度,按”想自定义多少”挑
延伸阅读
- 官方文档:Plotly.js docs(按图表类型组织,每种都有可改的 demo)
- 跨语言对照:Plotly Python 和 plotly.js 文档结构镜像,对着看能立刻理解 schema 共享
- Dash 教程:Dash in 20 minutes(看仪表盘怎么把 plotly.js 推上 web)
- 论文级对比:Bostock D3 vs 高层封装的取舍 —— 见 d3 笔记
- d3 —— Plotly.js 早期 SVG 后端用的就是它
- echarts —— 同光谱另一个声明式高层库,国内仪表盘多
- recharts —— React-only 高层封装,思想接近但范式不同