鼠标交互以在Vega中拖动数据点

时间:2018-12-07 16:04:30

标签: interaction vega

我正在努力更深入地了解Vega图中的交互。当前,我想修改Voronoi example from the documentation,以便您不添加和删除点,而是拖动现有点。我的代码如下:

{
  "$schema": "https://vega.github.io/schema/vega/v4.json",
  "width": 500,
  "height": 200,
  "autosize": "none",

  "signals": [
   {
      "name": "movePoint",
      "on": [
        {
          "events": "[mousedown, mouseup] > mousemove",
          "update": "{u: round(100*invert('xscale', x()))/100, v: round(100*invert('yscale', y()))/100}"
        }
      ]
    }
  ],

  "data": [
    {
      "name": "table",
      "values": [
        {"u": 0.1, "v": 0.1},
        {"u": 0.9, "v": 0.1},
        {"u": 0.1, "v": 0.9},
        {"u": 0.9, "v": 0.9},
        {"u": 0.5, "v": 0.5}
      ],
      "on": [
        {"trigger": "movePoint", "modify": "movePoint", "values": "{u: movePoint.u, v: movePoint.v}"}
      ]
    }
  ],

  "scales": [
    {
      "name": "xscale",
      "domain": [0, 1],
      "range": "width"
    },
    {
      "name": "yscale",
      "domain": [0, 1],
      "range": "height"
    }
  ],

  "marks": [
    {
      "name": "points",
      "type": "symbol",
      "zindex": 1,
      "from": {"data": "table"},
      "interactive": false,
      "encode": {
        "enter": {
          "fill": {"value": "black"},
          "size": {"value": 36},
          "x": {"scale": "xscale", "field": "u"},
          "y": {"scale": "yscale", "field": "v"}
        }
      }
    },
    {
      "type": "path",
      "from": {"data": "points"},
      "encode": {
        "enter": {
          "stroke": {"value": "firebrick"},
          "fill": {"value": "transparent"}
        }
      },
      "transform": [
        {
          "type": "voronoi",
          "x": "datum.x", "y": "datum.y",
          "size": [{"signal": "width"}, {"signal": "height"}]
        }
      ]
    }
  ]
}

似乎信号已经很好。我使用Vega调试视图对其进行了测试,它似乎可以正常运行:

  • 仅当在鼠标按下期间发生鼠标移动时,信号才会改变
  • 信号值在正确的坐标系中,即数据使用的坐标系,即0到1之间的坐标

交互是行不通的:我无法拖动这些点,并且鼠标交互时数据也不会改变。我试图将触发器从数据移到标记,但这也没有用。我必须做些什么才能能够拖动点?

1 个答案:

答案 0 :(得分:0)

我发现了如何为自己做这件事。问题是我使用的信号可以计算正确的新坐标,但是它不包含要拖动的对象的ID。我发现最简单的方法是为此添加一个单独的信号。因此,现在有了一个“ NewPointPosition”(相同的信号,新名称)和“ WhichPoint”,而不是一个“ MovePoint”信号,其中包含要拖动的对象的ID。也许可以将其合并为一个信号,但这样更容易理解。

我的第一个版本中缺少的另一个重要点是,“ points”和“ path”标记需要在“ encode”中使用“ update”子句,而不仅仅是“ enter”子句。否则,数据将发生变化,但是可视化将不会对此做出反应。

工作代码如下:

    {
  "$schema": "https://vega.github.io/schema/vega/v4.json",
  "width": 500,
  "height": 200,
  "autosize": "none",

  "signals": [
   {
      "name": "whichPoint",
      "on": [
        {
          "events": "path:click, path:mousemove[event.buttons]{20}",
          "update": "datum.datum"
        }
      ]
    },
   {
      "name": "newPointPosition",
      "on": [
        {
          "events": "path:click, path:mousemove[event.buttons]{20}",
          "update": "{u: invert('xscale', x()), v: invert('yscale', y())}"
        }
      ]
    }
  ],

  "data": [
    {
      "name": "table",
      "values": [
        {"u": 0.1, "v": 0.1},
        {"u": 0.9, "v": 0.1},
        {"u": 0.1, "v": 0.9},
        {"u": 0.9, "v": 0.9},
        {"u": 0.5, "v": 0.5}
      ],
      "on": [
        {"trigger": "whichPoint", "modify": "whichPoint", "values": "{u: newPointPosition.u, v:newPointPosition.v}"}
      ]
    }
  ],

  "scales": [
    {
      "name": "xscale",
      "domain": [0, 1],
      "range": "width"
    },
    {
      "name": "yscale",
      "domain": [0, 1],
      "range": "height"
    }
  ],

  "marks": [
    {
      "name": "points",
      "type": "symbol",
      "zindex": 1,
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "fill": {"value": "black"},
          "size": {"value": 36},
          "x": {"scale": "xscale", "field": "u"},
          "y": {"scale": "yscale", "field": "v"}
        },
        "update": {
          "fill": {"value": "black"},
          "size": {"value": 36},
          "x": {"scale": "xscale", "field": "u"},
          "y": {"scale": "yscale", "field": "v"}
        }
      }
    },
    {
      "type": "path",
      "from": {"data": "points"},
      "encode": {
        "enter": {
          "stroke": {"value": "firebrick"},
          "fill": {"value": "transparent"}
        },
        "update": {
          "stroke": {"value": "firebrick"},
          "fill": {"value": "transparent"}
        }
      },
      "transform": [
        {
          "type": "voronoi",
          "x": "datum.x", "y": "datum.y",
          "size": [{"signal": "width"}, {"signal": "height"}]
        }
      ]
    }
  ]
}