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

关于实现特定逻辑滑块控制Vega堆叠条形图的技术问询

可实现!堆叠条形图滑块交互的代码改进方案

你的需求完全可以通过Vega实现,下面是针对现有代码的改进提示和优化后的完整代码:

核心改进点

  • 重构滑块信号逻辑:将滑块信号表示堆叠的累计结束位置(而非直接对应宽度),天然满足堆叠顺序约束,避免滑块交叉。
  • 修正数据转换逻辑:基于滑块累计位置计算每个矩形的实际宽度,通过反向窗口计算实现“右移滑块时从最右侧矩形开始缩减”的规则,同时确保总宽度不超过100。
  • 简化交互状态管理:用单个信号统一管理当前选中的滑块,减少冗余代码,优化交互体验。

优化后的完整代码

{
  "$schema": "https://vega.github.io/schema/vega/v4.json",
  "width": 400,
  "height": 50,
  "padding": 5,
  "data": [
    {
      "name": "base",
      "values": [
        {"y": "range", "class": "class 1", "order": 1},
        {"y": "range", "class": "class 2", "order": 2},
        {"y": "range", "class": "class 3", "order": 3},
        {"y": "range", "class": "class 4", "order": 4}
      ]
    },
    {
      "name": "control",
      "source": "base",
      "transform": [
        {
          "type": "formula",
          "as": "end",
          "expr": "datum.order == 1 ? tri_mark_1_pos : datum.order == 2 ? tri_mark_2_pos : datum.order == 3 ? tri_mark_3_pos : tri_mark_4_pos"
        },
        {
          "type": "formula",
          "as": "start",
          "expr": "datum.order == 1 ? 0 : datum.order == 2 ? tri_mark_1_pos : datum.order == 3 ? tri_mark_2_pos : tri_mark_3_pos"
        },
        {
          "type": "formula",
          "as": "x",
          "expr": "max(0, datum.end - datum.start)"
        },
        {
          "type": "window",
          "sort": {"field": "order", "order": "descending"},
          "ops": ["sum"],
          "fields": ["x"],
          "as": ["total_right"]
        },
        {
          "type": "formula",
          "as": "x",
          "expr": "if(datum.total_right > 100, max(0, datum.x - (datum.total_right - 100)), datum.x)"
        },
        {
          "type": "stack",
          "groupby": ["y"],
          "field": "x"
        }
      ]
    }
  ],
  "signals": [
    {
      "name": "tri_mark_1_pos",
      "value": 25,
      "on": [
        {
          "events": "[@tri_mark_1:mousedown, window:mouseup] > window:mousemove!",
          "update": "clamp(invert('x', x()), 0, tri_mark_2_pos)"
        }
      ]
    },
    {
      "name": "tri_mark_2_pos",
      "value": 50,
      "on": [
        {
          "events": "[@tri_mark_2:mousedown, window:mouseup] > window:mousemove!",
          "update": "clamp(invert('x', x()), tri_mark_1_pos, tri_mark_3_pos)"
        }
      ]
    },
    {
      "name": "tri_mark_3_pos",
      "value": 75,
      "on": [
        {
          "events": "[@tri_mark_3:mousedown, window:mouseup] > window:mousemove!",
          "update": "clamp(invert('x', x()), tri_mark_2_pos, tri_mark_4_pos)"
        }
      ]
    },
    {
      "name": "tri_mark_4_pos",
      "value": 100,
      "on": [
        {
          "events": "[@tri_mark_4:mousedown, window:mouseup] > window:mousemove!",
          "update": "clamp(invert('x', x()), tri_mark_3_pos, 100)"
        }
      ]
    },
    {
      "name": "active_slider",
      "value": null,
      "on": [
        {"events": "@tri_mark_1:mousedown", "update": "'tri_mark_1'"},
        {"events": "@tri_mark_2:mousedown", "update": "'tri_mark_2'"},
        {"events": "@tri_mark_3:mousedown", "update": "'tri_mark_3'"},
        {"events": "@tri_mark_4:mousedown", "update": "'tri_mark_4'"},
        {"events": "window:mouseup", "update": "null"}
      ]
    }
  ],
  "scales": [
    {
      "name": "y",
      "type": "band",
      "range": "height",
      "domain": {"data": "control", "field": "y"}
    },
    {
      "name": "x",
      "type": "linear",
      "range": "width",
      "domain": [0, 100]
    },
    {
      "name": "color",
      "type": "ordinal",
      "range": ["#EBD423", "#5744AB", "#4823EB", "#968D48"],
      "domain": {"data": "control", "field": "class"}
    }
  ],
  "axes": [
    {"orient": "left", "scale": "y", "ticks": false},
    {"orient": "bottom", "scale": "x", "tickCount": 5}
  ],
  "marks": [
    {
      "type": "rect",
      "from": {"data": "control"},
      "encode": {
        "update": {
          "y": {"scale": "y", "field": "y"},
          "height": {"scale": "y", "band": 1, "offset": -1},
          "x": {"scale": "x", "field": "y0"},
          "x2": {"scale": "x", "field": "y1"},
          "fill": {"scale": "color", "field": "class"},
          "fillOpacity": {"value": 1}
        },
        "hover": {"fillOpacity": {"value": 0.5}}
      }
    },
    {
      "name": "tri_mark_1",
      "type": "symbol",
      "encode": {
        "enter": {
          "y": {"scale": "y", "value": 0, "offset": -7},
          "shape": {"value": "triangle-down"},
          "size": {"value": 400},
          "stroke": {"value": "#EBD423"},
          "strokeWidth": {"value": 0.5}
        },
        "update": {
          "x": {"scale": "x", "signal": "tri_mark_1_pos"},
          "fill": {"signal": "active_slider == 'tri_mark_1' ? '#EBD423' : '#fffff0'"},
          "cursor": {"value": "pointer"}
        }
      }
    },
    {
      "name": "tri_mark_2",
      "type": "symbol",
      "encode": {
        "enter": {
          "y": {"scale": "y", "value": 0, "offset": -7},
          "shape": {"value": "triangle-down"},
          "size": {"value": 400},
          "stroke": {"value": "#5744AB"},
          "strokeWidth": {"value": 0.5}
        },
        "update": {
          "x": {"scale": "x", "signal": "tri_mark_2_pos"},
          "fill": {"signal": "active_slider == 'tri_mark_2' ? '#5744AB' : '#fffff0'"},
          "cursor": {"value": "pointer"}
        }
      }
    },
    {
      "name": "tri_mark_3",
      "type": "symbol",
      "encode": {
        "enter": {
          "y": {"scale": "y", "value": 0, "offset": -7},
          "shape": {"value": "triangle-down"},
          "size": {"value": 400},
          "stroke": {"value": "#4823EB"},
          "strokeWidth": {"value": 0.5}
        },
        "update": {
          "x": {"scale": "x", "signal": "tri_mark_3_pos"},
          "fill": {"signal": "active_slider == 'tri_mark_3' ? '#4823EB' : '#fffff0'"},
          "cursor": {"value": "pointer"}
        }
      }
    },
    {
      "name": "tri_mark_4",
      "type": "symbol",
      "encode": {
        "enter": {
          "y": {"scale": "y", "value": 0, "offset": -7},
          "shape": {"value": "triangle-down"},
          "size": {"value": 400},
          "stroke": {"value": "#968D48"},
          "strokeWidth": {"value": 0.5}
        },
        "update": {
          "x": {"scale": "x", "signal": "tri_mark_4_pos"},
          "fill": {"signal": "active_slider == 'tri_mark_4' ? '#968D48' : '#fffff0'"},
          "cursor": {"value": "pointer"}
        }
      }
    }
  ]
}

关键逻辑说明

  1. 滑块位置约束:每个滑块的移动范围被限制在左侧滑块位置和右侧滑块位置之间,确保不会出现交叉,满足“左移至左侧滑块位置时自身宽度为0”的规则。
  2. 右侧矩形优先压缩:通过窗口转换计算当前类别右侧的总宽度,若超过100则从右到左依次缩减宽度,实现你描述的“右移滑块时从最右侧矩形开始压缩”的需求。
  3. 固定x轴范围:将x轴定义域固定为[0,100],避免图表自动缩放,更贴合0-100的范围要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 15:57:32