news 2026/9/23 9:04:38

Vega 柱状图示例全解析:从数据编码到悬停 Tooltip 的完整规范拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vega 柱状图示例全解析:从数据编码到悬停 Tooltip 的完整规范拆解
  • 数据可视化

【免费下载链接】vega

A visualization grammar.

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

本篇指南围绕 Vega 官方示例 bar-chart.vg.json 展开:一个仅 95 行 JSON 的柱状图规范,却完整覆盖了数据声明、band/linear 双尺度映射、坐标轴、rect 与 text 双重标记、以及基于信号(signal)的悬停高亮与数值 Tooltip 交互。读完本文,你将能够逐行理解 Vega 顶层规范的结构,掌握enter/update/hover编码集、band尺度与生产规则(production rules)的用法,并具备把任意结构相似的数据改造成可交互柱状图的能力。

对应的完整教程位于 docs/tutorials/bar-chart/index.md,本文以其为核心骨架,并结合 packages/vega-scale、packages/vega-parser 等源码做纵深解读。

示例规范一览

先通读完整的 Vega 规范(docs/examples/bar-chart.vg.json):

{ "$schema": "https://vega.github.io/schema/vega/v6.json", "description": "A basic bar chart example, with value labels shown upon pointer hover.", "width": 400, "height": 200, "padding": 5, "data": [ { "name": "table", "values": [ {"category": "A", "amount": 28}, {"category": "B", "amount": 55}, {"category": "C", "amount": 43}, {"category": "D", "amount": 91}, {"category": "E", "amount": 81}, {"category": "F", "amount": 53}, {"category": "G", "amount": 19}, {"category": "H", "amount": 87} ] } ], "signals": [ { "name": "tooltip", "value": {}, "on": [ {"events": "rect:pointerover", "update": "datum"}, {"events": "rect:pointerout", "update": "{}"} ] } ], "scales": [ { "name": "xscale", "type": "band", "domain": {"data": "table", "field": "category"}, "range": "width", "padding": 0.05, "round": true }, { "name": "yscale", "domain": {"data": "table", "field": "amount"}, "nice": true, "range": "height" } ], "axes": [ { "orient": "bottom", "scale": "xscale" }, { "orient": "left", "scale": "yscale" } ], "marks": [ { "type": "rect", "from": {"data":"table"}, "encode": { "enter": { "x": {"scale": "xscale", "field": "category"}, "width": {"scale": "xscale", "band": 1}, "y": {"scale": "yscale", "field": "amount"}, "y2": {"scale": "yscale", "value": 0} }, "update": { "fill": {"value": "steelblue"} }, "hover": { "fill": {"value": "red"} } } }, { "type": "text", "encode": { "enter": { "align": {"value": "center"}, "baseline": {"value": "bottom"}, "fill": {"value": "#333"} }, "update": { "x": {"scale": "xscale", "signal": "tooltip.category", "band": 0.5}, "y": {"scale": "yscale", "signal": "tooltip.amount", "offset": -2}, "text": {"signal": "tooltip.amount"}, "fillOpacity": [ {"test": "datum === tooltip", "value": 0}, {"value": 1} ] } } } ] }

规范由六大顶层组件构成:可视化尺寸、数据、信号、尺度、坐标轴、标记。下面逐一拆解。

可视化尺寸:width、height、padding 与 autosize

"width": 400, "height": 200, "padding": 5, "autosize": "pad"
  • width/height定义的是数据矩形(data rectangle)的大小,即数据绘图区域;坐标轴、图例等额外组件会占用更多空间。
  • padding定义图表内容与视图边界之间的留白。
  • autosize决定最终图表尺寸如何确定:
    • "pad"(默认):额外扩展空间以容纳所有标记(包括坐标轴、图例),数据矩形大小不变。若标记超出数据矩形边界,视图组件可能变得很大。
    • "fit":尝试将整张图(数据矩形、坐标轴、图例,但不含 padding)放入给定的width/height内,Vega 会收缩数据矩形来容纳坐标轴与图例;某些情况下(如图例过高)可能发生裁剪。
    • "none":禁用自动尺寸调整,总尺寸仅由width+height+padding决定。

从源码看,autosizepaddingwidthheight都属于内建信号(built-in signals)。在 packages/vega-parser/src/parsers/view.js 的collectSignals中,它们被统一收集:优先取顶层 spec 属性值,未定义时才回落到 config 中的对应配置;随后布局阶段由ViewLayout运算符(同文件 L54-L60)消费autosize信号完成最终布局。这意味着autosize等属性既可以直接写死,也可以声明为信号引用,实现动态尺寸。

数据:内联 values、URL 与数据变换

"data": [ { "name": "table", "values": [ {"category": "A", "amount": 28}, {"category": "B", "amount": 55}, {"category": "C", "amount": 43}, {"category": "D", "amount": 91}, {"category": "E", "amount": 81}, {"category": "F", "amount": 53}, {"category": "G", "amount": 19}, {"category": "H", "amount": 87} ] } ]

data是数据定义数组,每个数据集合必须有唯一的name。本示例使用values直接内联定义数据:每条记录包含字符串字段category(类别标签)与数值字段amount(数值)。

Vega 中的数据来源有三种方式(三者只能选其一):

  • values:直接内联;
  • url:从网络加载(支持 JSON、CSV 等格式);
  • source:由先前定义的数据集派生(例如经变换得到的新数据集)。

数据集还可通过transform属性挂接一系列数据变换(过滤、聚合、布局等)。关于数据与变换的完整说明见 docs/docs/data.md 与 docs/docs/transforms.md。

尺度:band 尺度与线性尺度的分工

"scales": [ { "name": "xscale", "type": "band", "domain": {"data": "table", "field": "category"}, "range": "width", "padding": 0.05, "round": true }, { "name": "yscale", "domain": {"data": "table", "field": "amount"}, "nice": true, "range": "height" } ]

尺度(scale)将数据值映射为视觉值(像素位置、颜色等)。本示例定义了两个尺度:

  • xscale(band 尺度)type: "band"显式指定为序数 band 尺度,将有序的类别域(A–H)映射到水平像素范围。range: "width"是 Vega 提供的便捷写法,等价于[0, 400](由画布宽度决定)。padding: 0.05在条带之间加入 5% 的间隔;round: true让柱条对齐到像素边界,避免亚像素模糊。
  • yscale(线性尺度):未显式写type,因为linear是默认类型(见 packages/vega-scale/src/scales/types.js 中Linear = 'linear'的定义)。其域取自amount字段的最小/最大值,range: "height"等价于[200, 0](Y 轴方向反转)。nice: true让域值更“友好”——例如原始域[0, 94.345]会被圆整为[0, 100],刻度更易读。

关于 band 尺度的底层行为,可参考 packages/vega-scale/src/scales/scaleBand.js:它基于 d3 的序数尺度扩展而来,通过paddingInner/paddingOuterpadding会同时设置二者)计算stepbandwidthround为真时对步长、起点与带宽取整(见该文件 L27-L36)。band: 1即取满整个条带宽度;若改用band: 0.5则只取条带的一半。

补充说明:

  • 每个尺度必须有唯一名称(嵌套在group标记内的尺度可以重名覆盖已定义尺度,属高级用法)。
  • domain既可以是静态值数组,也可以像本例一样从数据动态求取。
  • 默认情况下定量尺度会自动包含 0;如需禁用,在尺度定义中加入"zero": false
  • range也可显式定义为数组:二元数值数组用于空间映射,更长的数组(如["#ffa804", ...]十六进制色值)可用于序数尺度的自定义调色板。

坐标轴:最小定义与定制技巧

"axes": [ { "orient": "bottom", "scale": "xscale" }, { "orient": "left", "scale": "yscale" } ]

坐标轴用于将尺度可视化为刻度与标签。一条轴定义最少需要两个属性:orient(方位)与scale(所可视化的尺度)。这里底部为 xscale,左侧为 yscale。

教程中还演示了进一步的定制(对应 docs/tutorials/bar-chart/bar-chart-axes.vg.json):

"axes": [ { "orient": "bottom", "scale": "xscale" }, { "orient": "right", "scale": "yscale", "tickCount": 5, "offset": 6 } ]
  • tickCount: 5:请求大约 5 个刻度(默认约 10 个);
  • orient: "right":将 Y 轴移到图表右侧;
  • offset: 6:沿垂直方向平移轴的位置(本例向右偏移 6 像素)。

更完整的轴配置说明见 docs/docs/axes.md。

标记与编码集:rect 柱条 + hover 高亮

{ "type": "rect", "from": {"data":"table"}, "encode": { "enter": { "x": {"scale": "xscale", "field": "category"}, "width": {"scale": "xscale", "band": 1}, "y": {"scale": "yscale", "field": "amount"}, "y2": {"scale": "yscale", "value": 0} }, "update": { "fill": {"value": "steelblue"} }, "hover": { "fill": {"value": "red"} } } }

标记(mark)是可视化的基本图形元素。每个标记必须有typerectarealinesymbol等)和from(指明数据来源;若不提供from,则创建单个标记实例)。

标记属性的视觉编码通过encode下的命名编码集完成,标准编码集有:

编码集作用
enter标记首次创建时设置的属性
exit标记即将移除时的属性
update数据变化时更新的属性
hover鼠标悬停时设置的属性

执行顺序上,enter先被求值,随后是update,二者共同画出柱状图;鼠标悬停时求值hover将柱条染红;鼠标移出后再次求值update恢复steelblue注意:若省略update集,悬停后柱条会永久保持红色——因为hover属性不会被自动还原。

再细看enter集中的四个定位属性:

"x": {"scale": "xscale", "field": "category"}, "width": {"scale": "xscale", "band": 1}, "y": {"scale": "yscale", "field": "amount"}, "y2": {"scale": "yscale", "value": 0}
  • x:柱条左边缘,由xscale映射category字段得到;
  • width:柱条宽度,取 band 尺度的完整条带(band: 1);
  • y/y2:分别指定柱顶与柱底两个端点,y2用写死的数值 0 经yscale映射,确保柱底始终贴零线。使用双端点而非y+height的好处是不必关心哪个值更大——Vega 会自动正确处理位置;同理也可使用x/x2(横向柱状图、时间线常用)。

除标准图形标记外,Vega 还支持group标记(docs/docs/marks/group.md)用于嵌套标记与小多图(small multiples),group 内部可定义自己的尺度与坐标轴。更完整的标记体系见 docs/docs/marks.md。

信号:用 Tooltip 信号驱动交互

"signals": [ { "name": "tooltip", "value": {}, "on": [ {"events": "rect:pointerover", "update": "datum"}, {"events": "rect:pointerout", "update": "{}"} ] } ]

信号(signal)是 Vega 的动态变量:表达式会在其他信号变化或输入事件发生时被自动重新求值。每个信号必须有唯一name和初始value

本示例的tooltip信号跟踪当前高亮柱条对应的数据对象(datum):

  • rect:pointerover(指针移入矩形标记)时,update求值datum,即该标记的底层数据记录;
  • rect:pointerout(指针移出)时,update求值为空对象{}

事件语法rect:pointerover属于事件流选择器(event stream selector)的简写形式,详见 docs/docs/event-streams.md(其中也包含mouseover等指针事件的说明)。

用信号驱动 text 标记

接着,用一个单实例 text 标记把 Tooltip 数值渲染出来(注意:该标记没有from属性,只创建一个实例):

{ "type": "text", "encode": { "enter": { "align": {"value": "center"}, "baseline": {"value": "bottom"}, "fill": {"value": "#333"} }, "update": { "x": {"scale": "xscale", "signal": "tooltip.category", "band": 0.5}, "y": {"scale": "yscale", "signal": "tooltip.amount", "offset": -2}, "text": {"signal": "tooltip.amount"}, "fillOpacity": [ {"test": "datum === tooltip", "value": 0}, {"value": 1} ] } } }
  • x/y:直接从tooltip信号取值,经对应尺度映射定位:band: 0.5将文本置于条带中心,offset: -2让文本略微上移;
  • text:显示tooltip.amount数值;
  • fillOpacity:使用生产规则(production rules)——一组 if-then-else 视觉编码规则链。当tooltip为空对象时,datum === tooltipfalse(空对象不等于任何数据记录),走第二个规则取不透明度 1……实际上这里更常见的写法是判断tooltip是否有值:教程早期版本使用isNaN(tooltip.amount)判断,即指针不在柱条上时tooltip为空对象、tooltip.amountNaN,此时文本完全透明(不透明度 0);否则不透明度为 1,文本可见。

信号可被用于规范的任意位置:数据变换、尺度、标记编码等,详见 docs/docs/signals.md。

延伸练习与下一步

在 Vega Editor 中粘贴完整规范即可实时编辑预览(对应源文件 docs/examples/bar-chart.vg.json)。建议尝试以下练习:

  • 调整尺度与坐标轴:改tickCountorientpaddingnice等参数观察变化;
  • 将竖向柱状图改为横向柱状图(提示:用x/x2双端点配合band尺度在 Y 轴方向排布,轴方位相应调整);
  • 用结构相似的新数据集(字段名为categoryamount)替换values,观察图表是否自动适配;
  • hover高亮改成update中基于条件测试的填充色,体验生产规则与编码集之间的取舍。

更进一步的参考:数据与变换见 docs/docs/data.md 与 docs/docs/transforms.md;完整顶层规范说明见 docs/docs/specification.md;尺度、坐标轴、标记的详细文档分别在 docs/docs/scales.md、docs/docs/axes.md、docs/docs/marks.md。

  • 数据可视化

【免费下载链接】vega

A visualization grammar.

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

相关推荐

上一篇:【亲测免费】 Carto: 一个开源的地图可视化库
下一篇:推荐一个强大的Python表单处理库:New Forms

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

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

MTK6575 USB驱动实操:Host+OTG双模从加载失败到稳定枚举

简介:本资源为MTK6575平台USB驱动的完整源码包,面向嵌入式Linux驱动开发工程师、Android底层开发者及芯片级固件研究者,聚焦USB协议栈在MediaTek单核移动处理器上的具体实现与调试。资源包含42个文件,其中20个C文件实现主机/设备模…

作者头像 李华
网站建设 2026/9/23 8:59:34

day03学习校准法:用认知验证替代时间打卡

1. 这不是日程表,而是一套可验证的学习操作系统“day03-学习计划和进度”——看到这个标题,很多人第一反应是:又一个打卡模板?又一份Excel表格?又一段“今天学了2小时Python”的流水账?但在我带过87个自学转…

作者头像 李华
网站建设 2026/9/23 8:58:43

GTA6实体盒不含光盘?标准版与豪华版预购选择全解析

标准版和豪华版都摆在预购页上了,很多人却在“实体盒里没光盘”这句话上卡住了:盒子到底盒子里装什么?我买它图个啥?这个版本和纯数字版有什么区别?如果你正在纠结这两个版本怎么选,这篇文章就是把这笔账给…

作者头像 李华
网站建设 2026/9/23 8:56:10

AI代理上岗:本地模型如何成为数字隐私守门人

AI代理这个词最近出镜率实在太高了,高到快被说烂了。什么"AI代理将取代程序员"、"AI代理改变工作流",听着确实提气,但很多人没意识到,比"干掉某个岗位"更早发生、影响也更深远的一件事是&#xff1…

作者头像 李华
网站建设 2026/9/23 8:56:09

家长最怕的那几件事,湘楚有才单招是怎么回应的

怕孩子管不住自己这是家长最普遍的焦虑。孩子在家备考,手机不离手,短视频一刷就是两小时。书桌前一坐,发呆的时间比看书还长。说多了嫌烦,说少了没用。家长急得不行,孩子却像没事人一样。更让家长无奈的是,这种状态不是一天两天了,从初中到高中,反复说过、吵过、甚至没收过手机…

作者头像 李华