обновить линию при перетаскивании точки в сгруппированном многострочном графике - PullRequest
0 голосов
/ 02 декабря 2018

Я пытаюсь совершить свой первый набег в D3.js - цель - это сгруппированный многострочный график с перетаскиваемыми точками, в результате перетаскивания точки также обновляется соединительная линия.В конце концов, обновленные данные должны быть переданы обратно в r (через r2d3 ()).До сих пор мне удавалось получить базовый сюжет и сделать точки перетаскиваемыми ... но когда дело доходит до обновления линии (и передачи данных назад), я уже несколько часов бьюсь об стену.Надеетесь, что кто-то может мне помочь?

Я вставляю полный сценарий, потому что я не верю, что нигде не сделал чего-то действительно неожиданного.Код, связанный с перетаскиванием, расположен внизу.В его текущей форме перетаскивание круга приводит к исчезновению первой из линий (светло-синей) независимо от того, какие из кругов перетаскиваются.При перетаскивании линии рисуются снова с использованием стандартного (меньшего) обводки, что, конечно, не так, как это должно работать в конце.В идеале линия движется с движением перетаскивания, хотя я также был бы рад, если бы обновленная линия была отрисована снова только после окончания перетаскивания.

Я думаю, что мне нужно знать, как получить идентификационную информацию из перетаскиваемого круга, использовать ее для обновления соответствующей переменной в данных (данные в широком формате, кстати) и обновления соответствующего пути,

бонусный вопрос: перетаскивание не работает при создании 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 голосов
/ 02 декабря 2018

Обновите точку данных, связанную с кругом, а затем обновите круг и все линии.

Не добавляйте новые строки в dragend()

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

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

  focus.selectAll('path').attr("d", function(d) {
    return connectLine(d.values);
  }); // 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); });
}
...