为什么d3.event在拖动回调中与数据坐标相关

时间:2019-06-21 13:13:05

标签: d3.js drag

我正在玩d3.js,我注意到拖动时出现了怪异的行为。 如果我使用data和scaleX / scaleY添加一个圆,则将其拖动时,d3.event.y值相对于“数据”数组中的xy坐标,而不是左上角...

这是代码和jsFiddle:如果打开控制台,则如果拖动第一个圆圈,则参考是左上角,如果拖动第二个圆圈,则参考是与{x: 3,y:2}

var circle2 = svgGraph
  .selectAll('.circle2')
  .data([{ x: 3, y: 2 }])
  .enter()
  .append('circle') // Uses the enter().append() method
  .attr('cx', scaleX(100))
  .attr('cy', scaleY(100))
  .attr('r', 15)
  .call(d3.drag()
    .on('start', (d) => { })
    .on('drag', onHandlerDrag())
    .on('end', (d) => { }));

function onHandlerDrag() {
  return (d) => {
    console.log(d3.event.y);
  }
}

这是故意的行为吗?您对此有什么参考吗?

谢谢

1 个答案:

答案 0 :(得分:1)

是的,这是预期的行为。实际上,我们通常在拖动事件中使用这些xy属性,以避免容器周围元素 jumping (当人们足够懒惰以指定正确的主题时)

自从您寻求参考以来,请查看drag.subject

  

拖动手势的主题表示被拖动的物体。在拖动手势开始前立即收到启动输入事件(例如,鼠标按下或触摸启动)时进行计算。

然后,在下一段中,对您来说最重要的信息:

  

默认主题是接收到初始输入事件的原始选择中元素的数据(请参见拖动);如果未定义此基准,则创建一个表示指针坐标的对象。在SVG中拖动圆元素时,默认主题就是要拖动的圆的原点。 (强调我的)

最后:

  

返回的主题应该是暴露xy属性的对象,以便在拖动手势期间可以保留主题和指针的相对位置。

因此,如果您不希望x使用基准中的ydrag,只需在主题中设置另一个对象:

.subject(function() {
    return {
        foo: d3.event.x,
        bar: d3.event.y
    };
})

这是您所做的更改的代码:

function onHandlerDrag() {
  return (d) => {
    console.log(d3.event.y);
  }
}

var scaleX = d3
  .scaleLinear()
  .domain([0, 500])
  .range([0, 500]);

var scaleY = d3
  .scaleLinear()
  .domain([0, 500])
  .range([0, 500]);

var svgGraph = d3
  .select('.area')
  .attr('width', 500)
  .attr('height', 500);

var circle1 = svgGraph
  .append('circle')
  .attr('cx', scaleX(20))
  .attr('cy', scaleY(20))
  .attr('r', 15)
  .call(d3.drag()
    .on('start', (d) => {})
    .on('drag', onHandlerDrag())
    .on('end', (d) => {}));

var circle2 = svgGraph
  .selectAll('.circle2')
  .data([{
    x: 3,
    y: 2
  }])
  .enter()
  .append('circle') // Uses the enter().append() method
  .attr('cx', scaleX(100))
  .attr('cy', scaleY(100))
  .attr('r', 15)
  .call(d3.drag()
    .subject(function() {
      return {
        foo: d3.event.x,
        bar: d3.event.y
      };
    })
    .on('start', (d) => {})
    .on('drag', onHandlerDrag())
    .on('end', (d) => {}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg class='area'></svg>