news 2026/9/13 13:25:57

BokehJS 纯 JavaScript 开发指南:模型、Plotting 与 Charts 接口详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BokehJS 纯 JavaScript 开发指南:模型、Plotting 与 Charts 接口详解

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:

  1. 纯 JavaScript 开发:BokehJS 本身提供了一套完整的 JavaScript API,可以在没有 Python 后端的情况下独立创建交互式图表与应用;
  2. 自定义扩展模型(Extensions):官方文档(user_guide/advanced 目录)中提到的扩展机制通常需要直接操作 BokehJS 模型。

从仓库结构看,BokehJS 的完整源码位于 bokehjs/src/lib 下,公共 API 入口集中在 bokehjs/src/lib/api 目录,包含index.tsmodels.tsplotting.tscharts.tsfigure.tsgridplot.tsio.tslinalg.tspalettes.tsthemes.ts等模块,本文将逐一涉及。

获取与引入 BokehJS

BokehJS 可通过CDNnpm两种途径获取,完整安装说明见 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命名空间导出;
  • 字形属性(如xy)使用{ field: "x" }字段引用语法,表示从数据源的x列取值——这与 Python 侧"x"字符串的语义一致;
  • plot.add_layout(axis, "below"/"left")指定坐标轴位置,plot.add_layout(grid)将网格放在中心区域;
  • 最后通过Bokeh.Plotting.show(plot)渲染到页面。

Interfaces:高层接口

与 Python Bokeh 库类似,BokehJS 也提供了若干高层接口,用于交互式地组织低层模型对象。这些高层接口由Bokeh.PlottingBokeh.Charts组成。

从版本0.12.2起,这些 API 被集中到bokeh-api.js文件中。使用它们时,除了bokeh.js还必须额外引入bokeh-api.js(详见上文 "CDN 方式" 的加载清单)。

从仓库源码看,Bokeh.PlottingBokeh.Charts的导出定义在 bokehjs/src/lib/api/index.ts,其命名空间组织如下:

  • LinAlg—— 线性代数工具;
  • Charts—— 高层图表接口(pie/bar);
  • Plotting—— 绘图接口(figure/show/gridplot/color);
  • Palettes—— 调色板集合;
  • Themes—— 主题;
  • Documentsprintf以及全部模型(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,则先创建新Documentadd_root;默认把内容插入到当前 script 标签的父元素,也支持传入 CSS 选择器字符串或 HTMLElement 作为目标;渲染通过add_document_standalone完成。
  • gridplot(gridplot.ts):把子图排列成网格并合并工具栏,支持toolbar_locationmerge_tools(默认true,将各子图的 Save/Copy 等工具合并到统一工具栏)、sizing_modewidthheight等选项。
  • 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_colorfill_alphaline_color等)。在 figure.ts 的_glyph实现中可以看到:字形属性支持字段引用({field: ...})与常量值({value: ...})两种向量化语法;视觉属性(line_*fill_*hatch_*text_*)会被自动拆分为 selection、nonselection、hover、muted 等不同状态的字形变体(如nonselection_前缀默认alpha: 0.1muted_默认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是包含labelsvalues两个键的 JavaScript 对象,options为可选参数对象,支持以下键:

选项类型说明
widthnumber图表宽度(像素)
heightnumber图表高度(像素)
inner_radiusnumber扇形内半径(像素)
outer_radiusnumber扇形外半径(像素)
start_anglenumber扇形起始角(弧度)
end_anglenumber扇形结束角(弧度)
center[number, number]饼图圆心位置(x, y)(像素)
palettePalette | 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_glyphfill_alpha: 0.8)实现悬停高亮;
  • 使用Text字形在扇形中部标注标签,默认显示labels字段,可通过slice_labels切换为valuespercentages
  • 工具提示内容为<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为可选参数对象,支持以下键:

选项类型说明
widthnumber图表宽度(像素)
heightnumber图表高度(像素)
stackedboolean条形是否堆叠
orientation"horizontal" | "vertical"条形方向
bar_widthnumber每条宽度(像素)
palettePalette | Array<Color>用于给数值着色的命名调色板或颜色列表
axis_number_formatstring坐标轴刻度使用的格式化字符串

默认行为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:&nbsp<b>@values</b></div>,并且会根据方向调整anchorattachment(水平图使用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.createElementaddEventListener)即可把自定义按钮与绘图联动,无需任何框架。

总结

BokehJS 为纯 JavaScript 场景提供了与 Python Bokeh 对齐的低层模型体系,以及Bokeh.PlottingBokeh.Charts两套高层接口:

  • 低层模型:扁平化Bokeh命名空间,模型清单见 bokehjs/src/lib/api/models.ts,属性设置与 Python 完全一致,适合精细控制与自定义扩展开发;
  • Bokeh.Plotting:Pythonbokeh.plotting的 JavaScript 移植,提供figureshowgridplotcolor等便捷函数,支持字段引用与视觉属性的状态化拆分;
  • Bokeh.Charts:BokehJS 独有接口,piebar一行代码即可生成带默认 tooltip/hover 的图表,参数见 charts.ts 中的PieChartOptsBarChartOpts类型定义。

开始前请务必通过 CDN 或 npm 正确引入bokeh.jsbokeh-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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 13:25:47

GitHub项目可视化工具:静态代码分析与架构解构

1. 项目概述&#xff1a;GitHub项目透明化工具的价值与意义在开源社区摸爬滚打多年&#xff0c;我见过太多开发者面对高Star项目时的困惑&#xff1a;代码结构复杂、文档缺失、核心逻辑难以快速把握。最近发现一个名为GitDiagram的工具&#xff08;非官方命名&#xff0c;根据功…

作者头像 李华
网站建设 2026/9/13 13:24:01

MicroPython固件集成TFLM与ulab:ESP32-S3手势识别部署实践

简介&#xff1a;一份为微控制器量身定制的MicroPython固件工程&#xff0c;面向嵌入式开发者与AI边缘计算爱好者&#xff0c;目标是在ESP32等MCU上集成TensorFlow Lite与ulab&#xff0c;让开发者直接用Python开展轻量级神经网络实验。工程基于USER_C_MODULES机制扩展&#xf…

作者头像 李华
网站建设 2026/9/13 13:23:49

Python问卷星自动填写:requests构造HTTP请求实现批量提交

简介&#xff1a;这份基于Python实现的问卷星自动填写工具&#xff0c;主要面向希望学习自动化脚本编写的小白与进阶学习者&#xff0c;可作为毕业设计、课程设计或工程实训项目。资源共7个文件&#xff0c;包含2个Python核心脚本、3个XML配置、1个说明文档及1个工程文件&#…

作者头像 李华
网站建设 2026/9/13 13:22:00

C++与FPGA协同设计:提升嵌入式系统性能的关键

1. 为什么需要C与FPGA协同设计 在嵌入式系统和高性能计算领域&#xff0c;硬件加速已成为突破性能瓶颈的关键手段。FPGA&#xff08;现场可编程门阵列&#xff09;因其并行计算能力和可重构特性&#xff0c;成为许多实时处理系统的核心组件。但纯FPGA开发存在两个致命缺陷&…

作者头像 李华
网站建设 2026/9/13 13:21:19

华为OD机试Python环境适配与IO规范实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 13:17:32

从图层思维到节点思维:Substance Designer节点材质制作核心指南

1. 从图层思维到节点思维&#xff1a;这一步迈过去&#xff0c;SD才算真正上手 我第一次打开Substance Designer的时候&#xff0c;说实话是有点懵的。界面里没有图层&#xff0c;没有画笔工具&#xff0c;只有一个空荡荡的图表面板&#xff0c;左边拖一个节点、右边连一根线&a…

作者头像 李华