如何在Vega-Lite中创建含自定义文本、图标及页脚注释的KPI可视化
解决Vega-Lite KPI可视化的四个挑战
你的原始代码已经搭好了基础框架,下面针对四个问题逐一调整:
1. 精确定位文本
要在卡片内添加KPI名称、当前值、历史变化等文本,直接在layer里新增text标记,通过x和y的固定值定位,同时绑定对应字段:
{ "mark": { "type": "text", "fontSize": 16, "fontWeight": "bold" }, "encoding": { "x": {"value": 20}, "y": {"value": 40}, "text": {"field": "KPI Name"} } }, { "mark": { "type": "text", "fontSize": 24, "fontWeight": "bold" }, "encoding": { "x": {"value": 20}, "y": {"value": 70}, "text": {"field": "Current Value", "format": ".2s"} // 格式化数值,比如百万显示为M } }, { "mark": { "type": "text", "fontSize": 12, "fill": "#666" }, "encoding": { "x": {"value": 20}, "y": {"value": 95}, "text": {"field": "Historical Text"} } }
注:因为用了facet,每个卡片的文本会自动对应各自的数据,无需额外处理。
2. 添加自定义趋势图标及颜色
两种简单实现方式:
- 用emoji作为图标:新增
text标记,根据Trend字段显示不同emoji,同时绑定颜色:
{ "mark": { "type": "text", "fontSize": 20 }, "encoding": { "x": {"value": 350}, "y": {"value": 50}, "text": {"calculate": "datum.Trend === 'Increase' ? '▲' : '▼'"}, "color": {"calculate": "datum.Trend === 'Increase' ? '#2ecc71' : '#e74c3c'"} } }
- 用自定义形状:如果需要更规范的图标,用
shape标记配合符号配置:
{ "mark": { "type": "shape", "size": 150 }, "encoding": { "x": {"value": 350}, "y": {"value": 50}, "shape": {"field": "Trend", "scale": {"domain": ["Increase", "Decrease"], "range": ["triangle-up", "triangle-down"]}}, "color": {"field": "Trend", "scale": {"domain": ["Increase", "Decrease"], "range": ["#2ecc71", "#e74c3c"]}} } }
3. 添加基线和更新日期的页脚注释
在layer底部添加两个text标记,定位到卡片底部区域:
{ "mark": { "type": "text", "fontSize": 10, "fill": "#666" }, "encoding": { "x": {"value": 20}, "y": {"value": 200}, // 卡片高度219,设置为200靠近底部 "text": {"field": "Base line text"} } }, { "mark": { "type": "text", "fontSize": 10, "fill": "#666" }, "encoding": { "x": {"value": 300}, "y": {"value": 200}, "text": {"field": "Updated Date"} } }
4. 调整条形间距
两种方法按需选择:
- 调整条形高度:修改
bar标记的size属性,数值越大条形越高,间距越小:
"mark": { "type": "bar", "size": 25, // 原19,增大后间距变小 "yOffset": -40 }
- 调整y轴 padding:在y轴编码里添加
padding参数,控制ordinal轴的间距:
"y": { "field": "Measure", "type": "ordinal", "sort": ["Target Value","Current Value"], "title": null, "padding": 0.8 // 数值越大,条形间距越大 }
修改后的完整代码
{ "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "data": { "values": [ {"X":1,"Y":1,"KPI Name": "KPI 1", "Target Value": 50, "Current Value": 30, "Base line": 10, "Base line text": "Baseline Value: 10", "Historical Text": "24% Since Jun", "Updated Date": "Update 23 Jul 2024","Trend": "Increase"}, {"X":2,"Y":1,"KPI Name": "KPI 2", "Target Value": 50, "Current Value": 30, "Base line": 10, "Base line text": "Baseline Value: 10", "Historical Text": "24% Since Jun", "Updated Date": "Update 23 Jul 2024","Trend": "Increase"}, {"X":3,"Y":1,"KPI Name": "KPI 3", "Target Value": 5000000, "Current Value": 3000000, "Base line": 1000000, "Base line text": "Baseline Value: 1000000", "Historical Text": "24% Since Jun", "Updated Date": "Update 23 Jul 2024","Trend": "Increase"}, {"X":1,"Y":2,"KPI Name": "KPI 4", "Target Value": 0.9, "Current Value": 0.6, "Base line": 0.2, "Base line text": "Baseline Value: 10%", "Historical Text": "24% Since Jun", "Updated Date": "Update 23 Jul 2024","Trend": "Increase"} ] }, "transform": [ {"fold": ["Current Value", "Target Value"], "as": ["Measure", "Value"]}, {"calculate": "min(datum['Target Value'],datum['Current Value'],0)", "as": "start_value"}, {"calculate": "max(datum['Target Value'],datum['Current Value'])" , "as": "max_value"}, {"calculate": "datum.max_value+(0.27*datum.max_value)","as": "end_value"} ], "facet": { "row": {"field": "Y", "header":null}, "column": {"field": "X", "header":null} }, "spec": { "width": 393, "height": 219, "encoding": { "x": {"type": "quantitative", "scale": {"nice": false}, "title": null, "axis": null} }, "layer":[ { "mark": { "type": "rect", "width": 393, "height": 219, "fill": "#ffffff", "stroke": "#000000", "strokeWidth": 1, "cornerRadius": 4 }, "encoding": { "x": {"value": 0}, "y": {"value": 0} } }, { "mark": { "type": "bar", "size": 25, "yOffset": -40 }, "encoding": { "y": { "field": "Measure", "type": "ordinal", "sort":["Target Value","Current Value"], "title":null, "padding": 0.5 }, "x": { "field": "Value", "type": "quantitative", "title": null, "axis": null, "scale":{"domain": {"data": "", "fields": ["start_value", "end_value"]}} }, "color": { "field": "Measure", "type": "nominal", "title": null, "legend":null, "scale": {"domain": ["Current Value", "Target Value"], "range": ["#3498db", "#bdc3c7"]} } } }, // KPI名称 { "mark": {"type": "text", "fontSize": 16, "fontWeight": "bold"}, "encoding": { "x": {"value": 20}, "y": {"value": 40}, "text": {"field": "KPI Name"} } }, // 当前值 { "mark": {"type": "text", "fontSize": 24, "fontWeight": "bold"}, "encoding": { "x": {"value": 20}, "y": {"value": 70}, "text": {"field": "Current Value", "format": ".2s"} } }, // 历史变化文本 { "mark": {"type": "text", "fontSize": 12, "fill": "#666"}, "encoding": { "x": {"value": 20}, "y": {"value": 95}, "text": {"field": "Historical Text"} } }, // 趋势图标 { "mark": {"type": "text", "fontSize": 20}, "encoding": { "x": {"value": 350}, "y": {"value": 50}, "text": {"calculate": "datum.Trend === 'Increase' ? '▲' : '▼'"}, "color": {"calculate": "datum.Trend === 'Increase' ? '#2ecc71' : '#e74c3c'"} } }, // 基线注释 { "mark": {"type": "text", "fontSize": 10, "fill": "#666"}, "encoding": { "x": {"value": 20}, "y": {"value": 200}, "text": {"field": "Base line text"} } }, // 更新日期 { "mark": {"type": "text", "fontSize": 10, "fill": "#666"}, "encoding": { "x": {"value": 300}, "y": {"value": 200}, "text": {"field": "Updated Date"} } } ] }, "resolve": { "axis": {"x": "independent", "y": "independent"}, "scale": {"x": "independent"} }, "config": { "facet": {"spacing": 48} } }
内容的提问来源于stack exchange,提问作者Gurava Raju
相关产品推荐
相关产品推荐

