You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vega饼图标签重叠问题:如何复刻目标图表并优化标签布局?

Vega饼图标签优化及复刻方案

问题说明

复刻饼图时遇到标签控制问题:初始添加标签后出现重叠,使用label变换后标签变得零散混乱,多次调整仍无法得到整齐的标签布局,需要优化标签并完成图表复刻。

原实现代码

{  
  "$schema": "https://vega.github.io/schema/vega/v5.json",  
  "width": 600,  
  "height": 400,  
  "padding": 10,  
  "autosize": "none",  
  "data": [  
    {  
      "name": "table",  
      "values": [  
        {  
          "category": "A",  
          "amount": 84.64,  
          "amountText": "84.64%",  
          "col": "#00f"  
        },  
        {  
          "category": "B",  
          "amount": 1.5,  
          "amountText": "1.50%",  
          "col": "#0f0"  
        },  
        {  
          "category": "C",  
          "amount": 3.47,  
          "amountText": "3.47%",  
          "col": "#f00"  
        },  
        {  
          "category": "D",  
          "amount": 1.15,  
          "amountText": "1.15%",  
          "col": "#fa0"  
        },  
        {  
          "category": "E",  
          "amount": 3.47,  
          "amountText": "3.47%",  
          "col": "#0ff"  
        },  
        {  
          "category": "F",  
          "amount": 1.15,  
          "amountText": "1.15%",  
          "col": "#f0f"  
        },  
        {  
          "category": "G",  
          "amount": 3.47,  
          "amountText": "3.47%",  
          "col": "#aaa"  
        },  
        {  
          "category": "H",  
          "amount": 1.15,  
          "amountText": "1.15%",  
          "col": "#000"  
        }  
      ],  
      "transform": [  
        {  
          "type": "pie",  
          "field": "amount",  
          "sort": true  
        }  
      ]  
    }  
  ],  
  "marks": [  
    {  
      "name": "pie",  
      "type": "arc",  
      "from": {  
        "data": "table"  
      },  
      "encode": {  
        "enter": {  
          "fill": { "signal": "datum.col" },  
          "x": { "signal": "width / 2" },  
          "y": { "signal": "height / 2" },  
          "startAngle": { "field": "startAngle" },  
          "endAngle": { "field": "endAngle" },  
          "innerRadius": { "value": 0 },  
          "outerRadius": { "value": 75 }  
        }  
      }  
    },  
    {  
      "name":"label",  
      "type": "text",  
      "from": { "data": "pie" },  
      "encode": {  
        "enter": {  
          "x": { "signal": "width / 2" },  
          "y": { "signal": "height / 2" },  
          "radius": { "value": 100 },  
          "theta": { "signal": "(datum.startAngle + datum.endAngle)/2" },  
          "fill": { "value": "#000" },  
          "align": { "value": "center" },  
          "baseline": { "value": "middle" },  
          "text": { "field": "datum.amountText" },  
          "ruleStartX": { "signal": "(width/2)+80*cos((datum.startAngle + datum.endAngle)/2-(PI/2))" },  
          "ruleStartY": { "signal": "(height/2)+80*sin((datum.startAngle + datum.endAngle)/2-(PI/2))" }  
        }  
      },  
      "transform": [  
        {  
          "type": "label",  
          "avoidMarks": [],  
          "avoidBaseMark": false,  
          "size": { "signal": "[width, height]" }  
        },  
        {  
          "type": "formula",  
          "as": "theta",  
          "expr": "datum.theta"  
        }  
      ]  
    },  
    {  
      "name":"rule1",  
      "type": "rule",  
      "from": { "data": "label" },  
      "encode": {  
        "enter":{  
          "x":{"signal":"datum.ruleStartX"},  
          "y":{"signal":"datum.ruleStartY"},  
          "x2":{"signal": "(datum.bounds.x2-datum.bounds.x1)/2+datum.bounds.x1"},  
          "y2":{"signal": "if(datum.bounds.y1>height/2,datum.bounds.y1-5, datum.bounds.y2+5)"},  
          "stroke":{"signal": "if(abs(datum.ruleStartY-datum.bounds.y1)>10,'black','white')"}  
        }  
      }  
    }  
  ]
}

优化后的完整代码

{  
  "$schema": "https://vega.github.io/schema/vega/v5.json",  
  "width": 600,  
  "height": 400,  
  "padding": 20,  
  "autosize": "pad",  
  "data": [  
    {  
      "name": "table",  
      "values": [  
        {  
          "category": "A",  
          "amount": 84.64,  
          "amountText": "84.64%",  
          "col": "#00f"  
        },  
        {  
          "category": "B",  
          "amount": 1.5,  
          "amountText": "1.50%",  
          "col": "#0f0"  
        },  
        {  
          "category": "C",  
          "amount": 3.47,  
          "amountText": "3.47%",  
          "col": "#f00"  
        },  
        {  
          "category": "D",  
          "amount": 1.15,  
          "amountText": "1.15%",  
          "col": "#fa0"  
        },  
        {  
          "category": "E",  
          "amount": 3.47,  
          "amountText": "3.47%",  
          "col": "#0ff"  
        },  
        {  
          "category": "F",  
          "amount": 1.15,  
          "amountText": "1.15%",  
          "col": "#f0f"  
        },  
        {  
          "category": "G",  
          "amount": 3.47,  
          "amountText": "3.47%",  
          "col": "#aaa"  
        },  
        {  
          "category": "H",  
          "amount": 1.15,  
          "amountText": "1.15%",  
          "col": "#000"  
        }  
      ],  
      "transform": [  
        {  
          "type": "pie",  
          "field": "amount",  
          "sort": true  
        },  
        {  
          "type": "formula",  
          "as": "midAngle",  
          "expr": "(datum.startAngle + datum.endAngle) / 2"  
        },  
        {  
          "type": "formula",  
          "as": "isLeft",  
          "expr": "datum.midAngle > PI"  
        }  
      ]  
    }  
  ],  
  "marks": [  
    {  
      "name": "pie",  
      "type": "arc",  
      "from": { "data": "table" },  
      "encode": {  
        "enter": {  
          "fill": { "signal": "datum.col" },  
          "x": { "signal": "width / 2" },  
          "y": { "signal": "height / 2" },  
          "startAngle": { "field": "startAngle" },  
          "endAngle": { "field": "endAngle" },  
          "innerRadius": { "value": 0 },  
          "outerRadius": { "value": 80 }  
        }  
      }  
    },  
    {  
      "name": "label",  
      "type": "text",  
      "from": { "data": "table" },  
      "encode": {  
        "enter": {  
          "fill": { "value": "#000" },  
          "fontSize": { "value": 12 },  
          "text": { "field": "amountText" },  
          "align": { "signal": "datum.isLeft ? 'right' : 'left'" },  
          "baseline": { "value": "middle" }  
        },  
        "update": {  
          "x": { "signal": "width/2 + (95 * cos(datum.midAngle - PI/2))" },  
          "y": { "signal": "height/2 + (95 * sin(datum.midAngle - PI/2))" }  
        }  
      },  
      "transform": [  
        {  
          "type": "label",  
          "avoidMarks": ["pie"],  
          "avoidBaseMark": true,  
          "size": { "signal": "[width, height]" },  
          "offset": 5,  
          "sort": {"field": "midAngle"}  
        }  
      ]  
    },  
    {  
      "name": "guideLine",  
      "type": "rule",  
      "from": { "data": "label" },  
      "encode": {  
        "enter": {  
          "stroke": { "value": "#333" },  
          "strokeWidth": { "value": 1 }  
        },  
        "update": {  
          "x": { "signal": "width/2 + (82 * cos(datum.midAngle - PI/2))" },  
          "y": { "signal": "height/2 + (82 * sin(datum.midAngle - PI/2))" },  
          "x2": { "signal": "datum.bounds[datum.isLeft ? 'x2' : 'x1']" },  
          "y2": { "signal": "(datum.bounds.y1 + datum.bounds.y2)/2" }  
        }  
      }  
    }  
  ]
}

关键优化点说明

  • 标签变换配置修正:在label transform中指定avoidMarks: ["pie"],让标签主动避开饼图区域;开启avoidBaseMark防止标签自身重叠,同时添加sort按角度排序标签,避免小占比标签挤在一起。
  • 标签对齐逻辑优化:根据扇区角度判断左右侧,自动设置标签align属性(左侧扇区右对齐,右侧左对齐),让标签排列更整齐。
  • 引导线逻辑重构:引导线从饼图边缘出发,精准连接到标签的对应侧边,避免原代码中引导线错位问题;使用标签的bounds属性动态计算连接点,适配标签位置变化。
  • 初始布局调整:调整饼图外半径、标签初始偏移距离,给标签留出足够空间;修改autosize为pad,自动适配内容避免边缘标签被截断。
  • 移除冗余变换:删除原代码中重复的theta公式变换,直接使用数据预处理的midAngle,简化逻辑。

内容的提问来源于stack exchange,提问作者Noble

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 16:09:55