在分组的多折线图中拖动点时更新线

时间:2018-12-02 16:53:51

标签: javascript d3.js draggable

我正在尝试首次进入D3.js的目的-目的是创建具有可拖动点的分组多线图,其中拖动点会导致连接线也被更新。最终,更新的数据应传递回r(通过r2d3())。到目前为止,我设法获得了基准图并使点可拖动...但是当涉及到更新线(并传回数据?)时,我已经碰壁了几个小时。希望有人可以帮助我吗?

我要粘贴完整的脚本,因为我不相信自己在任何地方都没有做过真正意想不到的事情。与拖动有关的代码都位于底部。在当前形式下,拖动圆圈会使第一行(淡蓝色的线条)消失-不管拖动哪个圆圈。在draggend上,将使用默认(较小)笔划再次绘制线条,这当然不是最后的方式。理想情况下,线条会随着拖动运动而移动,但是如果在拖动结束后才再次绘制更新​​的线条,我也很高兴。

我认为我需要知道的是如何从拖动的圆中获取标识信息,并使用它来更新数据中的相应变量(数据是宽格式,顺便说一句),并更新相应的路径。

额外的问题:制作x scaleOrdinal(按预期)时,拖动不起作用。有没有解决方法?

// !preview r2d3 data= data.frame(id = c(1,1,2,2,3,3,4,4,5,5), tt = c(1, 2, 1, 2, 1, 2, 1, 2, 1, 2), val = c(14.4, 19.3, 22.0, 27.0, 20.7, 25.74, 16.9, 21.9, 18.6, 23.6))

var dById = d3.nest()
  .key(function(d) {
    return d.id;
  })
  .entries(data);

var margin = {
    top: 40,
    right: 40,
    bottom: 40,
    left: 40
  },
  width = 450 - margin.left - margin.right,
  height = 300 - margin.top - margin.bottom;

var color = d3.scaleOrdinal()
  .range(["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99"]);

var x = d3.scaleLinear()
  .range([0.25 * width, 0.75 * width])
  .domain([1, 2]);

var y = d3.scaleLinear()
  .rangeRound([height, 0])
  .domain([0, d3.max(data, function(d) {
    return d.val * 1.1;
  })]);

var xAxis = d3.axisBottom(x),
  yAxis = d3.axisLeft(y);

// Define the line by data variables
var connectLine = d3.line()
  .x(function(d) {
    return x(d.tt);
  })
  .y(function(d) {
    return y(d.val);
  });


svg.append('rect')
  .attr('class', 'zoom')
  .attr('cursor', 'move')
  .attr('fill', 'none')
  .attr('pointer-events', 'all')
  .attr('width', width)
  .attr('height', height)
  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

var focus = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

focus.selectAll('lines')
  .data(dById)
  .enter().append("path")
  .attr("class", "line")
  .attr("d", function(d) {
    return connectLine(d.values);
  })
  .attr("stroke", function(d) {
    return color(d.key);
  })
  .attr('stroke-width', 4);

focus.selectAll('circles')
  .data(dById)
  .enter().append("g")
  .attr("class", "dots")
  .selectAll("circle")
  .data(function(d) {
    return d.values;
  })
  .enter().append("circle")
  .attr("cx", function(d) {
    return x(d.tt);
  })
  .attr("cy", function(d) {
    return y(d.val);
  })
  .attr("r", 6)
  .style('cursor', 'pointer')
  .attr("fill", function(d) {
    return color(d.id);
  })
  .attr("stroke", function(d) {
    return color(d.id);
  });

focus.append('g')
  .attr('class', 'axis axis--x')
  .attr('transform', 'translate(0,' + height + ')')
  .call(xAxis);

focus.append('g')
  .attr('class', 'axis axis--y')
  .call(yAxis);


/// drag stuff: 

let drag = d3.drag()
  .on('start', dragstarted)
  .on('drag', dragged)
  .on('end', dragended);

focus.selectAll('circle')
  .call(drag);

// focus.selectAll('line')
//    .call(drag);

function dragstarted(d) {
  d3.select(this).raise().classed('active', true);
  dragID = Math.round(x.invert(d3.event.x)); 
// get x at start in order to force the dragged circle to    stay at this x-value (i.e. allow it to vertically only)
}

function dragged(d) {
  dragNewY = y.invert(d3.event.y);
  d3.select(this)
    .attr('cx', x(dragID))
    .attr('cy', y(dragNewY));

  //  focus.selectAll('path')
  //      .attr("d", function(d) { return connectLine(d); }); // removes all lines (to be redrawn at dragended with a smaller stroke)

  focus.select('path').attr("d", function(d) {
    return connectLine(d);
  }); // removes first lines (to be redrawn at dragended with a smaller stroke)

  // How do I select only the line associated with the dragged circle?
}

function dragended(d) {
  d3.select(this).classed('active', false);

  focus.selectAll('lines')
    .data(dById)
    .enter().append("path")
    .attr("class", "line")
    .attr("d", function(d) {
      return connectLine(d.values);
    })
    .attr("stroke", function(d) {
      return color(d.key);
    });
    }

1 个答案:

答案 0 :(得分:0)

更新与圆相关的数据点,然后更新圆和所有线。

请勿在{{1​​}}

中添加新行
dragend()