news 2026/9/29 5:46:24

使用 @msrvida/vega-deck.gl 构建 Vega 3D 可视化:安装、接入与 WebGL 渲染原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 @msrvida/vega-deck.gl 构建 Vega 3D 可视化:安装、接入与 WebGL 渲染原理
  • 数据可视化
  • 数据分析
  • 前端

【免费下载链接】SandDance

Visually explore, understand, and present your data.

项目地址:https://gitcode.com/gh_mirrors/sa/SandDance
点击查看免费下载

@msrvida/vega-deck.gl是 SandDance 项目中负责把 Vega 可视化规格交给 deck.gl 做 WebGL 渲染的核心视图组件。它让你用 Vega 声明式规格的"表现力",换取 deck.gl 的 GPU 渲染能力,从而直接支持 3 维数据可视化。读完本文,你将掌握两种安装方式(CDN script 标签与 Node.js 打包接入)、use()依赖注入的机制、ViewGl的用法,以及"矩形变立方体"的z/depth编码扩展与底层 Presenter 渲染管线。

说明:本文基于当前仓库packages/vega-deck.gl目录(当前版本 3.3.6)编写,文中所有源码路径均以仓库根目录为起点。

组件定位:Vega 的表达力 + deck.gl 的 WebGL 渲染

该项目在 README 中对自己的定位是"View component for Vega visualizations, using deck.gl for WebGL rendering"。它将两个优秀的可视化库合二为一:一方面完整继承 Vega 规格的声明式表达能力(数据、缩放、坐标、图元都能用 JSON 规格描述),另一方面把最终绘制交给 deck.gl 的 WebGL 管线,因此可以把数据可视化到3 个维度。

在 SandDance 整体架构中,它处于"渲染底座"的位置。从仓库入口文件 packages/vega-deck.gl/src/index.ts 可以看到,该包对外导出以下核心内容:

  • base/use:依赖注入机制(来自 src/base.ts);
  • Presenter:负责用 deck.gl 呈现一帧"舞台"(Stage)数据(src/presenter.ts);
  • ViewGl:Vega.View 的子类,新增.renderer('deck.gl')渲染器(src/vega-classes/viewGl.ts);
  • constants、controls、defaults、types、util等工具模块,以及 deck.gl 的DeckProps、PickInfo、Position、RGBAColor类型再导出。

已知限制:并未完整实现 Vega 全部特性

README 明确声明:本项目没有完整实现 Vega 提供的每一项特性。部分交互特性因为 3D 渲染模型与 2D 渲染平面之间的对应关系被打破而被省略(例如某些依赖 2D 平面坐标系的交互);另一些特性则"尚未开发完成",项目方欢迎以 pull request 的形式贡献。

这是理解该组件能力边界的关键前提:它面向的是"3D 化后的 Vega 规格"这一主场景,而不是一个与 Vega 原生 renderer 完全等价的替代品。

特性扩展:用z/depth编码把矩形变成 3D 立方体

README 中最具实用价值的功能扩展是:矩形(rect)图元可以渲染为 3D 立方体。做法是在通常使用"x"/"width"、"y"/"height"的地方,额外添加"z"/"depth"编码。

底层实现印证了这一设计。在 packages/vega-deck.gl/src/marks/rect.ts 的 mark stager 中,每个 rect 场景项被转换为Cube对象:

  • size: [item.width, item.height, depth]——宽、高、深三个维度;
  • position: [x + (item.x || 0), ty * (y + (item.y || 0)) - item.height, z]——其中ty = -1用于把 SVG 坐标系的 y 轴方向翻转为 WebGL 坐标系(y 向下为正 → y 向上为正);
  • 关键细节:2d视图下z与depth强制取0,因为"对于正交(2d)视图,必须始终使用 0,否则 deck.gl 不会显示它们";3d视图下深度至少叠加min3dDepth(值为0.05,见 src/defaults.ts),保证立方体有最小可见厚度;
  • 颜色取自 Vega 场景项的fill,透明度取自opacity(未定义时默认不透明)。

也就是说,同一份 Vega 规格,只需切换相机视角(2d/3d),矩形图元就能在"平面矩形"与"立体立方体"之间切换,这正是 SandDance 数据探索体验的核心机制。

安装方式一:script 标签(最快接入)

这是最快的接入方式:既可以从 CDN 加载,也可以把脚本托管到自己的网站。在 HTML 中按顺序添加以下三个标签:

<script src="https://unpkg.com/vega@^5.11/build/vega.js" charset="utf-8"></script> <script src="https://unpkg.com/deck.gl@~8.3.7/dist.min.js"></script> <script src="https://unpkg.com/@msrvida/vega-deck.gl@^3/dist/umd/vega-deck.gl.js"></script>

加载后,全局变量VegaDeckGl即可使用。接下来调用use()函数把依赖库传给VegaDeckGl:

VegaDeckGl.use(vega, deck, deck, luma);

注意第二个和第三个参数都是deck(分别为 deck.gl/core 与 deck.gl/layers 的 UMD 全局对象),第四个参数luma是 luma.gl。

安装方式二:Node.js / 打包器

适用于使用脚本打包器(bundler)的场景。在package.json的dependencies中加入:

"@deck.gl/core": "^8.3.7", "@deck.gl/layers": "^8.3.7", "@luma.gl/core": "^8.3.1", "@msrvida/vega-deck.gl": "^3", "vega": "^5.17.0"

然后执行npm install。在 JavaScript 中按如下方式导入并调用use():

import * as deck from '@deck.gl/core'; import * as layers from '@deck.gl/layers'; import * as luma from '@luma.gl/core'; import * as vega from 'vega'; import * as VegaDeckGl from '@msrvida/vega-deck.gl'; VegaDeckGl.use(vega, deck, layers, luma);

当前仓库的 packages/vega-deck.gl/package.json 中,本包自身的运行时依赖还包括@danmarshall/deckgl-typings、@msrvida/chart-types、d3-color、d3-ease、deepmerge、tsx-create-element、vega-typings等;构建脚本由build-typescript(tsc)+bundle(rollup)两步完成。

use() 做了什么:依赖注入源码解析

从 src/base.ts 可以看到use()的实现非常直白:它把传入的四类库引用写入全局base对象:

export const base: Base = { deck, layers, luma, vega }; export function use(vega, deck, layers, luma) { base.deck = deck; base.layers = layers; base.luma = luma; base.vega = vega; }

其中vega需要满足VegaBase接口(包含parse、View、Renderer、renderModule、sceneVisit、scheme、truncate等成员,见 src/base.ts);deck需要包含Deck、Layer、OrbitView、OrbitController、LinearInterpolator、LightingEffect等;layers需要包含IconLayer、LineLayer、PathLayer、PolygonLayer、TextLayer;luma需要包含CubeGeometry、Model、Texture2D(用于立方体着色器)。

这种"把重依赖交给使用方在运行时注入"的设计,是 UMD 与 ES6 两种消费方式能共用的关键:类继承(如ViewGl继承vega.View)发生在执行阶段而非声明阶段,从而兼容两种加载形态。

核心用法:VegaDeckGl.ViewGl

VegaDeckGl.ViewGl使用与 Vega 的 View 相同的 API。唯一新增的能力是:除了'canvas'和'svg',现在可以传入'deck.gl'作为渲染器类型:

var view = new VegaDeckGl.ViewGl(vega.parse(spec)) .renderer('deck.gl') .initialize(document.querySelector('#vis')) .run();

从源码看,这条链路是这样工作的(src/vega-classes/viewGl.ts):

  1. ViewGl是_ViewGl(runtime, config)工厂函数返回的Vega.View动态子类,可通过new关键字实例化;
  2. 第一次调用.renderer('deck.gl')时,会通过base.vega.renderModule('deck.gl', { handler: CanvasHandler, renderer: RendererGl })向 Vega 注册一个名为deck.gl的自定义渲染模块(viewGl.ts);
  3. .initialize(el)会先创建一个Presenter(若未通过presenterConfig传入),并把 Vega 的初始 DOM 挂到 Presenter 内部的"vega controls"元素上(viewGl.ts);
  4. 渲染时,RendererGl._render()把 Vega 场景标记上相机类型(view),再调用presenter.present(scene3d, height, width, presenterConfig)(src/vega-classes/rendererGl.ts)。

因此,你的 Vega 规格、数据变换、信号(signal)逻辑都原样保留,只是"最终画到屏幕"这一步被替换成了 WebGL。

ViewGl 与 Presenter 的扩展配置

ViewGl除了继承 Vega 的ViewOptions(如background、bind、container、hover、loader、logger、logLevel、renderer、tooltip、locale、expr),还额外支持ViewGlConfig字段(viewGl.ts):

  • presenter:自定义 Presenter 实例;
  • presenterConfig:Presenter 配置(见下);
  • getView:返回当前相机视角类型('2d'/'3d')的函数。

PresenterConfig(定义于 src/interfaces.ts)提供了一系列可与 deck.gl 交互和自定义的钩子:

配置项作用
transitionDurations过渡动画时长,含color、position、size、view四档;默认值分别为 100 / 600 / 600 / 600 毫秒(见 src/defaults.ts)
preStage(stage, deckProps)在提交 deck.gl props 前对 Stage / deckProps 做最后修改
preLayer(stage)在生成 layers 之前回调
onCubeHover/onCubeClick立方体 hover / 点击事件(事件对象 +Cube)
onLayerClick(info, e)透传 deck.gl 的 layer 点击(PickInfo)
onLegendClick图例点击
onTextClick/onTextHover文本图元点击 / hover(hover 返回 boolean)
getTextColor/getTextHighlightColor/getTextHighlightAlphaCutoff自定义文本颜色、高亮色与高亮透明度阈值
onSceneRectAssignCubeOrdinal为场景矩形分配立方体序号(默认自动递增)
onTargetViewState(height, width)调整目标相机视角状态,返回{ height, width, newViewStateTarget? }
shouldViewstateTransition()决定是否触发视角过渡动画
preserveDrawingBuffer为 true 时以preserveDrawingBuffer: true创建 WebGL 上下文,便于截图
zAxisZindexz 轴的层级
redraw强制重绘回调(构造时被绑定到_redraw+run())
onPresent每帧呈现完成后回调

PresenterStyle(src/interfaces.ts)则用于控制外观,包括cssPrefix(默认'vega-deckgl-')、defaultCubeColor(默认[128, 128, 128, 255]灰色)、highlightColor(默认黑色)、fontFamily。

Presenter:从 Vega 场景到 deck.gl 渲染的转换器

Presenter(src/presenter.ts)是本包渲染管线的核心类,职责是"用 deck.gl 呈现一帧 Stage 数据"。它的工作流程可以概括为:

  1. 场景归一化:present(sceneOrStage, height, width, config)判断输入是 Vega 场景(有marktype)还是直接给定的Stage。若是 Vega 场景,则调用sceneToStage(src/stagers.ts)把它转换成统一的Stage结构(包含cubeData、pathData、polygonData、axes、textData、legend、facets、gridLines、view等字段,见 src/interfaces.ts);
  2. 首次创建 Deck 实例:用OrbitView + OrbitController作为视图,LightingEffect提供光照,注册双击重置相机(homeCamera);根据stage.view计算初始viewState——2d视角为rotationX: 90, rotationOrbit: 0, zoom: -0.2,3d视角为rotationX: 30, rotationOrbit: 25, zoom: -0.4(src/viewState.ts);
  3. 立方体数据补齐:当maxOrdinal存在时,用patchCubeArray把cubeData补齐到最大序号,空位用isEmpty: true、颜色透明的占位 Cube 填充(presenter.ts);
  4. 构建 layers 并提交:getLayers把 Stage 中的立方体、线条、多边形、文本等分别映射为CubeLayer、LineLayer/PathLayer、PolygonLayer、TextLayer等 deck.gl layer;若发生了视角/边界变化,则通过LinearInterpolator(插值target、rotationOrbit、rotationX、zoom,见 src/viewState.ts)与 easing 配置过渡动画;随后requestAnimationFrame中deckgl.setProps提交渲染;
  5. 附加能力:rePresent(partialStage)用于只更新局部 Stage(如仅改颜色)的轻量重绘;canvasToDataURL()可把当前帧导出为 PNG(需要preserveDrawingBuffer支持);showGuides()显示渲染区域辅助线;finalize()释放资源。

CubeLayer(src/cube-layer/cube-layer.ts)是自定义的 deck.gl Layer:它基于 luma.glModel+ GLSL 着色器(cube-layer-fragment.glsl.ts/cube-layer-vertex.glsl.ts)绘制立方体,属性包括lightingMix(光照混合系数,默认 0.5,2d时为 0、3d时为 1)、getPosition/getSize/getColor访问器,以及material: { ambient: 0.5, diffuse: 1 }材质参数,并启用project32、gouraudLighting、picking三个 shader module。

版本演进与破坏性变更(v3)

README 记录了该组件的版本节奏:

  • 3.3.0:显示 z 轴刻度(Show z-axis scale);
  • 3.2.0:修复动画缓动(animation easing);文本字符集支持全部 Unicode;
  • 3.1.0:新增 line(线)与 area(区域)图元支持。

v3 相对早期版本的破坏性变更包括:

  • Stage.TickText现在为VegaTextLayerDatum类型(该类型定义见 src/interfaces.ts,含color、text、position、size、angle、textAnchor、alignmentBaseline、metaData);
  • View类型被删除;
  • util.isColor函数被删除。

如果你从更早的 2.x 版本迁移,需要按上述清单调整代码。

在 SandDance 仓库中继续探索

如果你想看这个组件被真实消费的样子,可以参考以下仓库内的线索:

  • 打包与部署脚本:packages/vega-deck.gl/scripts/deploy.js;
  • UMD 打包配置:packages/vega-deck.gl/rollup.config.mjs;
  • 外部依赖别名(deck / luma / react 的外部化):packages/vega-deck.gl/alias/deck-external.js 等;
  • 基于它之上的测试页面:如 packages/vega-morphcharts/test/vegaspec/vega-deck.gl.test.html,以及 docs/tests/v4/umd/vega-morphcharts.test.html;
  • 在 SandDance 渲染栈中的下一层:vega-morphcharts(packages/vega-morphcharts)与vega-deck.gl一脉相承,后者也在其源码中大量复用本包的 Presenter 与 Stage 数据结构。

小结

@msrvida/vega-deck.gl通过"依赖注入 + 自定义 Vega 渲染模块 + Presenter/Stage 管线"三个层次,把 Vega 的声明式规格无缝衔接到了 deck.gl 的 WebGL 渲染上,并额外提供了z/depth编码让矩形直接升维成立方体。无论你是通过 CDN 快速原型,还是在 bundler 工程中深度集成,只要记住两条主线——先use()注入 vega/deck/layers/luma,再用ViewGl以'deck.gl'渲染器运行规格——就能快速上手。

  • 数据可视化
  • 数据分析
  • 前端

【免费下载链接】SandDance

Visually explore, understand, and present your data.

项目地址:https://gitcode.com/gh_mirrors/sa/SandDance
点击查看免费下载
上一篇:CodeXGLUE代码搜索系统构建:从自然语言查询到精准代码匹配
下一篇:EloquentFilter入门指南:如何在5分钟内简化Laravel模型过滤

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

superpowers:让Codex CLI从会写代码到会做工程的技能库

从第一次在终端里敲下codex那条命令开始&#xff0c;我一直觉得这类 AI 编程助手有种"聪明但不太会用"的感觉&#xff1a;你问它一句&#xff0c;它能答得像模像样&#xff1b;但真让它独立把一个功能从规划到落地做完&#xff0c;它经常会走一步看一步&#xff0c;甚…

作者头像 李华
网站建设 2026/9/29 5:43:28

解决 Django 与 Jinja2 的兼容性问题

在 Django 项目中&#xff0c;尝试整合 Jinja2 作为模板引擎时遇到了兼容性问题。settings.py 文件中已经正确配置了 Jinja2 并保留了 Django 默认的模板设置&#xff0c;但系统报错提示未指定模板。如果移除 Django 的默认模板配置&#xff0c;错误信息变为未配置 Django 模板…

作者头像 李华
网站建设 2026/9/29 5:42:47

STM32上电到第一个任务:复位向量、启动流程与uC/OS-II调度机制

1. 上电那一瞬间&#xff0c;芯片里到底发生了什么很多人做 STM32 开发&#xff0c;习惯性地在main()函数第一行打断点&#xff0c;然后点下载、复位、运行&#xff0c;看着程序停在main入口&#xff0c;就觉得"启动流程"这件事已经理解了。但如果你真的追问一句&…

作者头像 李华