BokehJS 纯 JavaScript 开发指南:模型、Plotting 与 Charts 接口详解
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
导读
BokehJS 是 Bokeh 的客户端运行时库,负责浏览器端的绘图、渲染与事件处理,让开发者无需编写 HTML/CSS/JavaScript 也能构建交互式可视化应用。本指南以官方用户手册的 BokehJS 章节为主体,结合本仓库(bokeh 源码树)中的 api 模块 与 examples/advanced/bokehjs/simple_line.js 等真实实现,系统讲解如何脱离 Python 直接使用 BokehJS 进行纯 JavaScript 开发。读完本文,你将掌握:BokehJS 的获取与引入方式、低层模型(Low-level Models)的创建与属性操作、Bokeh.Plotting高层接口的用法,以及 BokehJS 独有的Bokeh.Charts图表接口(pie 与 bar)的完整参数体系。
注意:BokehJS 的 API 仍处于开发阶段,后续版本可能发生变更(官方文档明确警告了这一点)。
什么是 BokehJS
BokehJS 是一个纯客户端的 JavaScript 库,它接管了绘图(drawing)、渲染(rendering)与事件处理(event handling)等全部浏览器端工作。Bokeh Python 库(以及 R、Scala、Julia 等其他语言的封装库)是对 BokehJS 的高层封装,目的是让使用者无需关心 JavaScript 与 Web 开发细节。
但对于两类场景,开发者需要直接面对 BokehJS:
- 纯 JavaScript 开发:BokehJS 本身提供了一套完整的 JavaScript API,可以在没有 Python 后端的情况下独立创建交互式图表与应用;
- 自定义扩展模型(Extensions):官方文档(user_guide/advanced 目录)中提到的扩展机制通常需要直接操作 BokehJS 模型。
从仓库结构看,BokehJS 的完整源码位于 bokehjs/src/lib 下,公共 API 入口集中在 bokehjs/src/lib/api 目录,包含index.ts、models.ts、plotting.ts、charts.ts、figure.ts、gridplot.ts、io.ts、linalg.ts、palettes.ts、themes.ts等模块,本文将逐一涉及。
获取与引入 BokehJS
BokehJS 可通过CDN与npm两种途径获取,完整安装说明见 docs/bokeh/source/docs/first_steps/installation.rst 中的 "Installing standalone BokehJS" 小节(即install_bokehjs锚点)。
CDN 方式
Bokeh 的内容分发网络(CDN)使用如下命名方案(x.y.z为版本号):
bokeh-x.y.z.min.js——核心库,始终必需;bokeh-widgets-x.y.z.min.js—— 可选,用于表单控件等 Widgets;bokeh-tables-x.y.z.min.js—— 可选,用于数据表格;bokeh-api-x.y.z.min.js—— 可选,包含高层接口(见下文 "Interfaces" 一节);bokeh-gl-x.y.z.min.js—— 可选,用于启用 WebGL 支持;bokeh-mathjax-x.y.z.min.js—— 可选,用于启用 MathJax 数学文本渲染。
关键加载约定:
- 只有核心库
bokeh-x.y.z.min.js是必需的,其余脚本按需加载; - BokehJS API(
bokeh-api文件)必须加载在核心库之后; - 从 CDN 加载 BokehJS 时,应在 script 标签上设置
crossorigin="anonymous"属性。
npm 方式
BokehJS 也以 npm 包形式发布,可以在 JavaScript 工程中作为依赖引入,适合配合打包工具(如 webpack、esbuild 等)进行模块化开发。仓库内 BokehJS 的构建产物与包配置见 bokehjs/src/lib/package.json。
低层模型(Low-level Models)
模型与 Python 的对应关系
BokehJS 中用于绘图和应用的低层模型(如 guides、glyphs、widgets 等)与 Bokeh Python 模型一一对应。因此,即便官方 reference guide 聚焦于 Python,它仍然是 BokehJS 模型的首要参考文档。
两者在组织方式上有显著差异:
- Python 库按层次结构组织(如
bokeh.models.ranges.Range1d); - JavaScript 模型全部位于扁平化的
Bokeh命名空间中,通常任何 Python 的ClassName在 JavaScript 中都可以写作Bokeh.ClassName。
完整的 JavaScript 模型清单可见 bokehjs/src/lib/api/models.ts,该文件仅一行export * from "../models",实际模型定义位于 bokehjs/src/lib/models 目录下。
创建模型:Python 与 JavaScript 对照
在 JavaScript 中创建模型时,只需把 Python 构造器的关键字参数组织成一个 JavaScript 对象即可。以Range1d为例:
Python:
xdr = Range1d(start=-0.5, end=20.5)JavaScript:
const xdr = new Bokeh.Range1d({ start: -0.5, end: 20.5 });这一模式适用于所有类似场景。模型创建后,两种语言可以用完全相同的方式设置属性,例如xdr.end = 30在 Python 和 JavaScript 中都会把上面Range1d模型的end值设为 30。
从零构建带坐标轴、网格与线图的完整示例
下面是一个从零创建包含坐标轴、网格和 Line 字形(glyph)的绘图示例。它与仓库中的 examples/advanced/bokehjs/simple_line.js 一致,可对照 examples/models 目录下的其他示例观察:在这个层级上,Python 与 JavaScript 的代码几乎完全相同。
// 创建数据与 ColumnDataSource const x = Bokeh.LinAlg.linspace(-0.5, 20.5, 10); const y = x.map(function (v) { return v * 0.5 + 3.0; }); const source = new Bokeh.ColumnDataSource({ data: { x: x, y: y } }); // 创建绘图范围 const xdr = new Bokeh.Range1d({ start: -0.5, end: 20.5 }); const ydr = new Bokeh.Range1d({ start: -0.5, end: 20.5 }); // 创建 Plot const plot = new Bokeh.Plot({ title: "BokehJS Plot", x_range: xdr, y_range: ydr, width: 400, height: 400, background_fill_color: "#F2F2F7" }); // 添加坐标轴 const xaxis = new Bokeh.LinearAxis({ axis_line_color: null }); const yaxis = new Bokeh.LinearAxis({ axis_line_color: null }); plot.add_layout(xaxis, "below"); plot.add_layout(yaxis, "left"); // 添加网格 const xgrid = new Bokeh.Grid({ ticker: xaxis.ticker, dimension: 0 }); const ygrid = new Bokeh.Grid({ ticker: yaxis.ticker, dimension: 1 }); plot.add_layout(xgrid); plot.add_layout(ygrid); // 添加 Line 字形 const line = new Bokeh.Line({ x: { field: "x" }, y: { field: "y" }, line_color: "#666699", line_width: 2 }); plot.add_glyph(line, source); Bokeh.Plotting.show(plot);这段代码展示了低层 API 的几个关键点:
Bokeh.LinAlg.linspace来自 bokehjs/src/lib/api/linalg.ts,由 index.ts 以LinAlg命名空间导出;- 字形属性(如
x、y)使用{ field: "x" }的字段引用语法,表示从数据源的x列取值——这与 Python 侧"x"字符串的语义一致; plot.add_layout(axis, "below"/"left")指定坐标轴位置,plot.add_layout(grid)将网格放在中心区域;- 最后通过
Bokeh.Plotting.show(plot)渲染到页面。
Interfaces:高层接口
与 Python Bokeh 库类似,BokehJS 也提供了若干高层接口,用于交互式地组织低层模型对象。这些高层接口由Bokeh.Plotting与Bokeh.Charts组成。
从版本0.12.2起,这些 API 被集中到
bokeh-api.js文件中。使用它们时,除了bokeh.js,还必须额外引入bokeh-api.js(详见上文 "CDN 方式" 的加载清单)。
从仓库源码看,Bokeh.Plotting与Bokeh.Charts的导出定义在 bokehjs/src/lib/api/index.ts,其命名空间组织如下:
LinAlg—— 线性代数工具;Charts—— 高层图表接口(pie/bar);Plotting—— 绘图接口(figure/show/gridplot/color);Palettes—— 调色板集合;Themes—— 主题;Document、sprintf以及全部模型(export * from "./models")。
Bokeh.Plotting
JavaScript 的Bokeh.Plotting是 Pythonbokeh.plotting接口的移植版本,因此用户手册 ug_basic 基础章节 中的内容对理解它同样有参考价值。
其实现位于 bokehjs/src/lib/api/plotting.ts,仅导出四个函数/类:
export {figure, Figure} from "./figure" export {show} from "./io" export {gridplot} from "./gridplot" export {color2css as color} from "../core/util/color"其中各组成部分的底层实现:
figure/Figure(figure.ts):负责根据x_axis_type/y_axis_type等属性自动创建坐标轴、网格与范围。源码中可见:x_axis_type支持"auto" | "linear" | "datetime" | "timedelta" | "log" | "mercator" | null,默认值为"auto";x_axis_location默认"below"、y_axis_location默认"left";tools既可以是逗号分隔的字符串(如"pan,wheel_zoom,box_zoom,reset,save",源码按逗号切分并 trim),也可以是Tool实例数组;未指定tools时默认工具为["pan", "wheel_zoom", "auto_box_zoom", "save", "reset", "help"](见 figure.ts 中_default_tools)。show(io.ts):将模型或Document渲染到页面。源码逻辑为:若传入的不是Document,则先创建新Document并add_root;默认把内容插入到当前 script 标签的父元素,也支持传入 CSS 选择器字符串或 HTMLElement 作为目标;渲染通过add_document_standalone完成。gridplot(gridplot.ts):把子图排列成网格并合并工具栏,支持toolbar_location、merge_tools(默认true,将各子图的 Save/Copy 等工具合并到统一工具栏)、sizing_mode、width、height等选项。color:即color2css的别名,用于把 RGB 数组等颜色表示转换为 CSS 颜色字符串。
彩色散点图示例
下面的 JavaScript 示例与仓库中的 Python 示例 examples/basic/scatters/color_scatter.py 高度相似:
const plt = Bokeh.Plotting; // 准备数据 const M = 100; const xx = []; const yy = []; const colors = []; const radii = []; for (let y = 0; y <= M; y += 4) { for (let x = 0; x <= M; x += 4) { xx.push(x); yy.push(y); colors.push(plt.color([50+2*x, 30+2*y, 150])); radii.push(Math.random() * 1.5); } } // 创建数据源 const source = new Bokeh.ColumnDataSource({ data: { x: xx, y: yy, radius: radii, colors: colors } }); // 创建绘图并添加工具 const tools = "pan,crosshair,wheel_zoom,box_zoom,reset,save"; const p = plt.figure({ title: "Colorful Scatter", tools: tools }); // 调用 circle 字形方法添加散点 const circles = p.circle({ field: "x" }, { field: "y" }, {field: "radius"}, { source: source, fill_color: { field: "colors" }, fill_alpha: 0.6, line_color: null, }); // 显示绘图 plt.show(p);注意p.circle(...)这类字形方法:位置参数{ field: "x" }、{ field: "y" }、{ field: "radius" }分别对应坐标与半径字段,最后一个对象承载视觉属性(fill_color、fill_alpha、line_color等)。在 figure.ts 的_glyph实现中可以看到:字形属性支持字段引用({field: ...})与常量值({value: ...})两种向量化语法;视觉属性(line_*、fill_*、hatch_*、text_*)会被自动拆分为 selection、nonselection、hover、muted 等不同状态的字形变体(如nonselection_前缀默认alpha: 0.1、muted_默认alpha: 0.2)。这些字形方法由 bokehjs/src/lib/api/glyph_api.ts 提供。
Bokeh.Charts
Bokeh.Charts是 BokehJS独有的高层图表接口(Python 侧没有对应物),目前支持两种高层图表:pie(饼图)与bar(条形图)。其完整实现位于 bokehjs/src/lib/api/charts.ts,默认调色板为"Spectral11"(见resolve_palette函数,也支持传入任意Palette名称或Color[]数组)。
Bokeh.Charts.pie
基本调用形式:
Bokeh.Charts.pie(data, { options })其中data是包含labels与values两个键的 JavaScript 对象,options为可选参数对象,支持以下键:
| 选项 | 类型 | 说明 |
|---|---|---|
width | number | 图表宽度(像素) |
height | number | 图表高度(像素) |
inner_radius | number | 扇形内半径(像素) |
outer_radius | number | 扇形外半径(像素) |
start_angle | number | 扇形起始角(弧度) |
end_angle | number | 扇形结束角(弧度) |
center | [number, number] | 饼图圆心位置(x, y)(像素) |
palette | Palette | Array<Color> | 用于给数值着色的命名调色板或颜色列表 |
slice_labels | "labels" | "values" | "percentages" | 提示工具(tooltip)中显示的内容 |
默认行为:通过Bokeh.Charts.pie创建的图表会自动添加 tooltip 与 hover 策略。从 charts.ts 的pie实现可以看到具体机制:
start_angle默认 0,end_angle默认start_angle + 2π;inner_radius默认 0,outer_radius默认 1;- 值会被归一化(
v / total_value)并累加(cumsum)以计算每个扇区的起止角; - 图表使用
AnnularWedge字形绘制扇区,并额外创建hover_glyph(fill_alpha: 0.8)实现悬停高亮; - 使用
Text字形在扇形中部标注标签,默认显示labels字段,可通过slice_labels切换为values或percentages; - 工具提示内容为
<div>@labels</div><div><b>@values</b> (@percentages)</div>,通过自动添加的HoverTool绑定到扇区 renderer 上。
饼图综合示例
下面示例生成 4 个不同配置的饼图,并用gridplot排布展示(原文使用Bokeh.embed.add_document_standalone渲染到当前 script 的父元素):
const plt = Bokeh.Plotting; const pie_data = { labels: ['Work', 'Eat', 'Commute', 'Sport', 'Watch TV', 'Sleep'], values: [8, 2, 2, 4, 0, 8], }; const p1 = Bokeh.Charts.pie(pie_data); const p2 = Bokeh.Charts.pie(pie_data, { inner_radius: 0.2, start_angle: Math.PI / 2 }); const p3 = Bokeh.Charts.pie(pie_data, { inner_radius: 0.2, start_angle: Math.PI / 6, end_angle: 5 * Math.PI / 6 }); const p4 = Bokeh.Charts.pie(pie_data, { inner_radius: 0.2, palette: "Oranges9", slice_labels: "percentages" }); // 将绘图加入 Document 并显示 const doc = new Bokeh.Document(); doc.add_root(plt.gridplot( [[p1, p2], [p3, p4]], {width: 250, height: 250})); Bokeh.embed.add_document_standalone(doc, document.currentScript.parentElement);Bokeh.Charts.bar
基本调用形式:
Bokeh.Charts.bar(data, { options })其中data是一个数组,每个条目代表数据表的一行,第一行为列头。例如某地区、年份的销售数据:
const data = [ ['Region', 'Year', 'Sales'], ['East', 2015, 23000 ], ['East', 2016, 35000 ], ['West', 2015, 16000 ], ['West', 2016, 34000 ], ['North', 2016, 12000 ], ];与pie类似,options为可选参数对象,支持以下键:
| 选项 | 类型 | 说明 |
|---|---|---|
width | number | 图表宽度(像素) |
height | number | 图表高度(像素) |
stacked | boolean | 条形是否堆叠 |
orientation | "horizontal" | "vertical" | 条形方向 |
bar_width | number | 每条宽度(像素) |
palette | Palette | Array<Color> | 用于给数值着色的命名调色板或颜色列表 |
axis_number_format | string | 坐标轴刻度使用的格式化字符串 |
默认行为:Bokeh.Charts.bar创建的图表同样自动添加 tooltip 与 hover 策略。从 charts.ts 的bar实现可以看到:
- 首行(
data[0])作为列名,其余行按列转置后,第一列作为类别标签(labels),后续各列作为数值列; - 数值轴默认使用
LinearAxis+BasicTickFormatter;指定axis_number_format时使用NumeralTickFormatter(如"0.[00]a"); - 类别轴使用
CategoricalAxis+FactorRange,条块通过Quad字形绘制:堆叠模式下逐列累加left/right(水平)边界,非堆叠模式下按dy = 1/columns.length均分每个类别的条带区间; orientation为"vertical"时,源码会将左右/上下数据互换并交换坐标轴与刻度(见if (orientation == "vertical")分支);- 工具提示内容为
<div>@labels</div><div>@columns: <b>@values</b></div>,并且会根据方向调整anchor与attachment(水平图使用center_right/horizontal,垂直图使用top_center/vertical)。
条形图综合示例
下面的示例同样生成 4 种配置(是否堆叠 × 方向),并以gridplot展示:
const plt = Bokeh.Plotting; const bar_data = [ ['City', '2010 Population', '2000 Population'], ['NYC', 8175000, 8008000], ['LA', 3792000, 3694000], ['Chicago', 2695000, 2896000], ['Houston', 2099000, 1953000], ['Philadelphia', 1526000, 1517000], ]; const p1 = Bokeh.Charts.bar(bar_data, { axis_number_format: "0.[00]a" }); const p2 = Bokeh.Charts.bar(bar_data, { axis_number_format: "0.[00]a", stacked: true }); const p3 = Bokeh.Charts.bar(bar_data, { axis_number_format: "0.[00]a", orientation: "vertical" }); const p4 = Bokeh.Charts.bar(bar_data, { axis_number_format: "0.[00]a", orientation: "vertical", stacked: true }); plt.show(plt.gridplot([[p1, p2], [p3, p4]], {width: 350, height: 350}));Minimal Example:最小可用示例
下面是一个最小示例,演示如何引入库、创建并修改绘图(带完整交互逻辑),可复制到 HTML 页面中运行:
// 创建数据源 const source = new Bokeh.ColumnDataSource({ data: { x: [], y: [] } }); // 创建带工具的绘图 const plot = Bokeh.Plotting.figure({ title: 'Example of random data', tools: "pan,wheel_zoom,box_zoom,reset,save", height: 300, width: 300 }); // 用数据源添加一条线 plot.line({ field: "x" }, { field: "y" }, { source: source, line_width: 2 }); // 显示绘图,追加到当前 script 所在节点末尾 Bokeh.Plotting.show(plot); function addPoint() { // 添加数据 —— 所有字段必须等长 source.data.x.push(Math.random()) source.data.y.push(Math.random()) // 用本地变更更新数据源 source.change.emit() } const addDataButton = document.createElement("Button"); addDataButton.appendChild(document.createTextNode("Some data.")); document.currentScript.parentElement.appendChild(addDataButton); addDataButton.addEventListener("click", addPoint); addPoint(); addPoint();该示例的要点:
figure通过字符串形式的tools快速装配工具条;plot.line({ field: "x" }, { field: "y" }, {...})与p.circle(...)一样属于字形方法;- 动态更新数据的关键是
source.change.emit():修改source.data的数组后必须手动触发 change 信号,绘图才会重新渲染——这正是 BokehJS 事件驱动渲染模型的核心; - 通过原生 DOM API(
document.createElement、addEventListener)即可把自定义按钮与绘图联动,无需任何框架。
总结
BokehJS 为纯 JavaScript 场景提供了与 Python Bokeh 对齐的低层模型体系,以及Bokeh.Plotting、Bokeh.Charts两套高层接口:
- 低层模型:扁平化
Bokeh命名空间,模型清单见 bokehjs/src/lib/api/models.ts,属性设置与 Python 完全一致,适合精细控制与自定义扩展开发; Bokeh.Plotting:Pythonbokeh.plotting的 JavaScript 移植,提供figure、show、gridplot、color等便捷函数,支持字段引用与视觉属性的状态化拆分;Bokeh.Charts:BokehJS 独有接口,pie与bar一行代码即可生成带默认 tooltip/hover 的图表,参数见 charts.ts 中的PieChartOpts与BarChartOpts类型定义。
开始前请务必通过 CDN 或 npm 正确引入bokeh.js与bokeh-api.js(高层接口必需),具体版本与脚本清单参见 installation.rst。更多可运行示例可对照仓库中的 examples/advanced/bokehjs/simple_line.js 与 examples/basic/scatters/color_scatter.py 进一步实践。
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考