d3.js: lựa chọn đồng bộ trên nhiều biểu đồ

Nov 09 2020

Làm cách nào để xử lý lựa chọn khi tôi muốn lưu trữ lựa chọn đồng bộ trên nhiều biểu đồ? Khi tôi thực hiện một lựa chọn, lựa chọn giống hệt nhau cũng sẽ được hiển thị trên các biểu đồ khác.

Tôi đã thêm cho bạn một ví dụ về jsfiddle được tạo trong dc.js.

Mã của tôi vào lúc này giống như sau:

var signalData = {
      signal1: {
        name: "signal1",
        data: [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 1, 2, 3, 5, 4, 1, 4, 2, 9, 7, 5, 7, 4, 6],
      },
      signal2: {
        name: "signal2",
        data: [6, 4, 8, 5, 4, 8, 4, 3, 5, 4, 5, 8, 7, 2, 9, 5, 4, 1, 2, 6, 0, 5, 7, 1],
      },
      signal3: {
        name: "signal3",
        data: [9, 5, 12, 3, 8, 4, 8, 6, 3, 4, 7, 8, 5, 2, 1, 8, 6, 8, 5, 8, 4, 8, 5, 1],
      },
    }

    var margin = {
      top: 10,
      right: 50,
      bottom: 40,
      left: 50
    };
    var width = window.innerWidth - margin.left - margin.right;
    var height = 200 - margin.top - margin.bottom;

    var xScale = d3
      .scaleLinear()
      .domain([0, d3.max(Object.keys(signalData), d => signalData[d].data.length)]) // input
      .range([0, width]); // output

    var brushXScale = d3
      .scaleLinear()
      .domain([0, d3.max(Object.keys(signalData), d => signalData[d].data.length)]) // input
      .range([0, width]); // output

    var brush = d3.brushX().extent([
      [0, 0],
      [width - margin.right, height],
    ]).on("brush", brushed);

    function brushed(d) {
      var selectionPx = d3.brushSelection(this); // === [lower, upper] in pixels

      // transform from pixels to x-values
      var selectionX = [xScale.invert(selectionPx[0]), xScale.invert(selectionPx[1])];

      // set x scale domain, then redraw the lines
      brushXScale.domain(selectionX);

      for (var signal in signalData) {
        updateChart(
          signalData[signal].data,
          signalData[signal].name
        );
      }
    }

    //Generate the brush focus chart
    generateMinimap(signalData.signal1.data);
    //Generate charts dynamically as often as i have signals
    for (var signal in this.signalData) {
      generateChart(
        signalData[signal].data,
        signalData[signal].name
      );
    }

    // This function is for the one time preparations
    function generateChart(data, name) {
      var svg = d3
        .select("#" + name)
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

      svg
        .append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")

      svg.append("g").attr("class", "y axis");

      svg
        .append("path")
        .attr("class", "line") // Assign a class for styling
        .attr("fill", "none")
        .attr("stroke", "blue");

      updateChart(data, name);
    }

    // This function needs to be called to update the already prepared chart
    function updateChart(data, name) {
      var svg = d3.select("#" + name + " svg");

      var yScale = d3
        .scaleLinear()
        .domain([0, d3.max(data)]) // input
        .range([height, 0]); // output

      var line = d3
        .line()
        .x((d, i) => brushXScale(i))
        .y((d) => yScale(d));
      console.log(line([1, 2, 3, 4]));

      svg.select(".x.axis")
        .call(d3.axisBottom(brushXScale));

      svg.select(".y.axis")
        .call(d3.axisLeft(yScale));

      svg
        .select(".line")
        .datum(data) // 10. Binds data to the line
        .attr("d", line) // 11. Calls the line generator
    }

    function generateMinimap(data) {
      var yScale = d3
        .scaleLinear()
        .domain([0, d3.max(data)]) // input
        .range([height, 0]); // output

      var line = d3
        .line()
        .x((d, i) => xScale(i))
        .y((d) => yScale(d));

      var svg = d3
        .select("#minimap")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

      svg.append("g").call(brush);

      svg
        .append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(xScale)); // Create an axis component with d3.axisBottom

      svg
        .append("path")
        .datum(data) // 10. Binds data to the line
        .attr("class", "line") // Assign a class for styling
        .attr("d", line) // 11. Calls the line generator
        .attr("fill-opacity", "0.17")
        .attr("fill", "blue")
        .attr("stroke", "blue");
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
  <div id="minimap"></div>
  <!-- In the original project these divs are not 
    static and get generated with v-for as many times as 
    i have a signal in signalData -->
  <div id="signal1"></div>
  <div id="signal2"></div>
  <div id="signal3"></div>

Điều quan trọng cần biết là các biểu đồ đang được hiển thị động và số lượng biểu đồ có thể nhiều hơn hoặc ít hơn!

Tôi đã cố gắng lấy dữ liệu ra khỏi lựa chọn và thêm nó vào những dữ liệu khác nhưng tôi không đi đến kết quả.

Trả lời

1 RubenHelsloot Nov 11 2020 at 03:58

Dựa trên ví dụ này , bạn có thể gọi theo brushchương trình .

Bằng cách này, mọi trường hợp đều được đánh dấu giống nhau. Tôi đã tạo một bàn chải mới selectBrushgọi tất cả các hộp chứa bàn chải khác và áp dụng cùng một bàn chải ở đó.

var signalData = {
  signal1: {
    name: "signal1",
    data: [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 1, 2, 3, 5, 4, 1, 4, 2, 9, 7, 5, 7, 4, 6],
  },
  signal2: {
    name: "signal2",
    data: [6, 4, 8, 5, 4, 8, 4, 3, 5, 4, 5, 8, 7, 2, 9, 5, 4, 1, 2, 6, 0, 5, 7, 1],
  },
  signal3: {
    name: "signal3",
    data: [9, 5, 12, 3, 8, 4, 8, 6, 3, 4, 7, 8, 5, 2, 1, 8, 6, 8, 5, 8, 4, 8, 5, 1],
  },
};

var margin = {
  top: 10,
  right: 50,
  bottom: 40,
  left: 50,
};
var width = window.innerWidth - margin.left - margin.right;
var height = 200 - margin.top - margin.bottom;

var xScale = d3
  .scaleLinear()
  .domain([0, d3.max(Object.keys(signalData), d => signalData[d].data.length)]) // input
  .range([0, width]); // output

var brushXScale = d3
  .scaleLinear()
  .domain([0, d3.max(Object.keys(signalData), d => signalData[d].data.length)]) // input
  .range([0, width]); // output

var zoomBrush = d3.brushX()
  .extent([
    [0, 0],
    [width - margin.right, height],
  ])
  .on("brush", zoomBrushed);

var selectBrush = d3.brushX()
  .extent([
    [0, 0],
    [width - margin.right, height],
  ])
  .on("brush", selectBrushed);

function zoomBrushed() {
  var selectionPx = d3.brushSelection(this); // === [lower, upper] in pixels

  // transform from pixels to x-values
  var selectionX = [xScale.invert(selectionPx[0]), xScale.invert(selectionPx[1])];

  // set x scale domain, then redraw the lines
  brushXScale.domain(selectionX);

  for(var signal in signalData) {
    updateChart(
      signalData[signal].data,
      signalData[signal].name,
    );
  }
}

function selectBrushed() {
  var selectionPx = d3.brushSelection(this); // === [lower, upper] in pixels

  selectBrush.on("brush", null);
  // This is the starts-with selector (https://www.w3schools.com/cssref/sel_attr_begin.asp)
  d3.selectAll("div[id^=signal] svg .brushcontainer")
    .call(selectBrush.move, selectionPx);
  selectBrush.on("brush", selectBrushed);
}

//Generate the brush focus chart
generateMinimap(signalData.signal1.data);
//Generate charts dynamically as often as i have signals
for(var signal in this.signalData) {
  generateChart(
    signalData[signal].data,
    signalData[signal].name,
  );
}

// This function is for the one time preparations
function generateChart(data, name) {
  var svg = d3
    .select("#" + name)
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  svg
    .append("g")
    .attr("class", "brushcontainer")
    .call(selectBrush);
  
  svg
    .append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")");

  svg.append("g")
    .attr("class", "y axis");

  svg
    .append("path")
    .attr("class", "line") // Assign a class for styling
    .attr("fill", "none")
    .attr("stroke", "blue");

  updateChart(data, name);
}

// This function needs to be called to update the already prepared chart
function updateChart(data, name) {
  var svg = d3.select("#" + name + " svg");

  var yScale = d3
    .scaleLinear()
    .domain([0, d3.max(data)]) // input
    .range([height, 0]); // output

  var line = d3
    .line()
    .x((d, i) => brushXScale(i))
    .y((d) => yScale(d));

  svg.select(".x.axis")
    .call(d3.axisBottom(brushXScale));

  svg.select(".y.axis")
    .call(d3.axisLeft(yScale));

  svg
    .select(".line")
    .datum(data) // 10. Binds data to the line
    .attr("d", line); // 11. Calls the line generator
}

function generateMinimap(data) {
  var yScale = d3
    .scaleLinear()
    .domain([0, d3.max(data)]) // input
    .range([height, 0]); // output

  var line = d3
    .line()
    .x((d, i) => xScale(i))
    .y((d) => yScale(d));

  var svg = d3
    .select("#minimap")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  svg.append("g")
    .call(zoomBrush);

  svg
    .append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(xScale)); // Create an axis component with d3.axisBottom

  svg
    .append("path")
    .datum(data) // 10. Binds data to the line
    .attr("class", "line") // Assign a class for styling
    .attr("d", line) // 11. Calls the line generator
    .attr("fill-opacity", "0.17")
    .attr("fill", "blue")
    .attr("stroke", "blue");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="minimap"></div>
<!-- In the original project these divs are not 
    static and get generated with v-for as many times as 
    i have a signal in signalData -->
<div id="signal1"></div>
<div id="signal2"></div>
<div id="signal3"></div>