如何为具有多个y轴的多折线图中的每条线分配特定的id [英] How to assign a specific id to each line in a multi-line chart with multiple y-axes

查看:99
本文介绍了如何为具有多个y轴的多折线图中的每条线分配特定的id的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图为具有多个y轴的多折线图中的每条线分配一个特定的id,以便我可以创建一个交互式图例,单击图例时可以打开和关闭这些线.这是链接到我的小提琴.

I am trying to assign a specific id to each line in a multi-line chart with multiple y-axes so that I can create an interactive legend which toggles the lines on and off when the legend is clicked. Here is the link to my fiddle.

    var xValueArray = [0, 10, 20, 30, 40];
    var arr = [[0, 10, 20, 30, 40], [0, 200, 300, 400, 500]];
    //data array is obtained after structuring arr array
    var data = [[{x: 0, y: 0}, {x: 10, y: 10}, {x: 20, y: 20}, {x: 30, y: 30}, {x: 40, y: 40}], [{x: 0, y: 0}, {x: 10, y: 200}, {x: 20, y: 300}, {x: 30, y: 400}, {x: 40, y: 500}]];

    const margin = {
      left: 20,
      right: 20,
      top: 20,
      bottom: 80
    };

    const svg = d3.select('svg');
    svg.selectAll("*").remove();

    const width = 200 - margin.left - margin.right;
    const height = 200 - margin.top - margin.bottom;

    //const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
    const g = svg.append('g').attr('transform', `translate(${80},${margin.top})`);

    //************* Axes and Gridlines ***************

    const xAxisG = g.append('g');
    const yAxisG = g.append('g');

    xAxisG.append('text')
      .attr('class','axis-label' )
      .attr('x', width / 3)
      .attr('y', -10)
      .style('fill', 'black')
      .text(function(d) {
      return "X Axis";
    });

    yAxisG.append('text')
      .attr('class','axis-label' )
      .attr('id', 'primaryYLabel')
      .attr('x', -height / 2 )
      .attr('y', -15)
      .attr('transform', `rotate(-90)`)
      .style('text-anchor', 'middle')
      .style('fill', 'black')
      .text(function(d) {
      return "Y Axis 1";
    });

    // interpolator for X axis -- inner plot region
    var x = d3.scaleLinear()
    .domain([0, d3.max(xValueArray)])
    .range([0, width])
    .nice();

    var yScale = new Array();
    for (var i = 0; i < 2; i++){
      // interpolator for Y axis -- inner plot region
      var y = d3.scaleLinear()
      .domain([0, d3.max(arr[i])])
      .range([0,height])
      .nice();
      yScale.push(y);
    }

    const xAxis = d3.axisTop()
    .scale(x)
    .ticks(5)
    .tickPadding(2)
    .tickSize(-height)

    const yAxis = d3.axisLeft()
    .scale(yScale[0])
    .ticks(5)
    .tickPadding(2)
    .tickSize(-width);

    yAxisArray = new Array();
    yAxisArray.push(yAxis);
    for (var i = 1; i < 2; i++){
      var yAxisSecondary = d3.axisLeft()
      .scale(yScale[i])
      .ticks(5)
      yAxisArray.push(yAxisSecondary);
    }

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", `translate(80,${height-80})`)
      .call(xAxis);

    svg.append("g")
      .attr("class", "y axis")
      .attr("transform", "translate(80,20)")
      .call(yAxis);

    var colors = ["blue", "red"];

    //plot lines
    for (var i = 0; i < 2; i++){
      var lineFunction = d3.line()
      .x(function(d) {return x(d.x); })
      .y(function(d) {return yScale[i](d.y); })
      .curve(d3.curveLinear);

      //plot lines
      var paths = g.append("path")
      .attr("class", "path1")
      .attr("id", "blueLine")
      .attr("d", lineFunction(data[i]))
      .attr("stroke", colors[i])
      .attr("stroke-width", 2)
      .attr("fill", "none")
      .attr("clip-path", "url(#clip)")

      //plot a circle at each data point
      g.selectAll(".dot")
        .data(data[i])
        .enter().append("circle")
        .attr("cx", function(d) { return x(d.x)} )
        .attr("cy", function(d) { return yScale[i](d.y); } )
        .attr("r", 3)
        .attr("class", "blackDot")
        .attr("clip-path", "url(#clip)")
    }

    var translation = 50;
    var textTranslation = 0;
    var yLabelArray = ["Y Axis 1", "Y Axis 2"];

    //loop starts from 1 as primary y axis is already plotted
    for (var i = 1; i < 2; i++){
      svg.append("g")
        .attr("transform", "translate(" + translation + "," + 20 + ")")
        .call(yAxisArray[i]);

      yAxisG.append('text')
        .attr('x', -height / 2 )
        .attr('y', -60)
        .attr('transform', `rotate(-90)`)
        .style('text-anchor', 'middle')
        .style('fill', 'black')
        .text(yLabelArray[i]);

      translation -= 40;
      textTranslation += 40;
    }

    //************* Legend ***************
    var legend = svg.selectAll(".legend")
    .data(data)
    .enter().append("g")

    legend.append("rect")
      .attr("x", width + 65)
      .attr("y", 30)
      .attr("width", 18)
      .attr("height", 4)
      .style("fill", "blue")

    legend.append("text")
      .attr("x", width + 60)
      .attr("y", 30)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .on("click", function(){
      // Determine if current line is visible
      var active   = blueLine.active ? false : true,
          newOpacity = active ? 0 : 1;
      // Hide or show the elements
      d3.select("#blueLine").style("opacity", newOpacity);
      // Update whether or not the elements are active
      blueLine.active = active;
    })
      .text(function(d) {
      return "Value1";
    });

    var legend1 = svg.selectAll(".legend")
    .data(data)
    .enter().append("g")

    legend1.append("rect")
      .attr("x", width + 65)
      .attr("y", 50)
      .attr("width", 18)
      .attr("height", 4)
      .style("fill", "red")

    legend1.append("text")
      .attr("x", width + 60)
      .attr("y", 50)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .on("click", function(){
      // Determine if current line is visible
      var active   = blueLine.active ? false : true,
          newOpacity = active ? 0 : 1;
      // Hide or show the elements
      d3.select("#blueLine").style("opacity", newOpacity);
      // Update whether or not the elements are active
      blueLine.active = active;
    })
      .text(function(d) {
      return "Value2";
    });

    var pointLegend = svg.selectAll(".pointLegend")
    .data(data)
    .enter().append("g")

    pointLegend.append("circle")
      .attr("r", 3)
      .attr("cx", width + 70)
      .attr("cy", 70)

    pointLegend.append("text")
      .attr("x", width + 60)
      .attr("y", 70)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .on("click", function(d){
      // Determine if dots are visible
      var active   = d.active ? false : true,
          newOpacity = active ? 0 : 1;
      // Hide or show the elements
      d3.selectAll(".blackDot").style("opacity", newOpacity);
      // Update whether or not the elements are active
      d.active = active;
    })
      .text(function(d) {
      return "Data";
    });

可以绘制图表的事实意味着for循环可以绘制图表.我正在使用for循环,因为我希望根据用户的输入来绘制图表,在我的实际代码中有一个名为fieldCount的参数来跟踪用户输入中的序列数,因此我在for循环中使用"2"因为我的数据数组中只有2个数组以便简化.

The fact that the chart can be plotted means the for loops are viable to draw the chart. I am using the for loops as I want my chart to be plotted given the user's input where I have a parameter named fieldCount in my actual code to track the number of series in my user's input thus I am using "2" in the for loop as I only have 2 arrays in my data array for simplification.

从小提琴中,我只能使用可单击的图例切换蓝线,而不能切换红线,因为我为所有行都分配了相同的"blueLine" ID.如何为特定的行分配特定的id,以便可以使用图例来切换行,是否可以对图例进行编码,从而不必声明那么多的图例变量?任何帮助,我们将不胜感激!

From the fiddle, I am only able to toggle the blue line using my clickable legend and not the red line as I am assigning same "blueLine" id to all my lines. How can I assign specific ids to specific lines so that I can toggle the lines using my legend and is there a way to code the legend so that I don't have to declare so many legend variables? Any help is greatly appreciated!

推荐答案

绘制线条时,可以将for循环的索引i传递为pathid作为

When you plot the lines you can pass the index i of the for loop into the id for the path as

//plot lines
var paths = g.append("path")
  .attr("class", "path1")
  .attr("id", "line" + i)

然后,当您执行点击功能时,您可以通过以下方式检查不透明度:

Then when you perform the on click function you can check the opacity as:

      .on("click", function(d, i) {
        // Determine if current line is visible
        let opacity = d3.select("#line" + i).style("opacity");
        let newOpacity;
        if (opacity == 0) {
            newOpacity = 1;
        }else {
            newOpacity = 0
        }
        d3.select("#line" + i).style("opacity", newOpacity);
      });

在您的代码中,您也不必要绘制两个图例.我也解决了这个问题.

Also in your code you are drawing two legends unnecessarily. I have fixed that issue as well.

以下是该解决方案的有效提要: https://jsfiddle.net/7dgek9wq/1/

Here is a working fiddle with the solution: https://jsfiddle.net/7dgek9wq/1/

下面的完整示例:

        var xValueArray = [0, 10, 20, 30, 40];
        var arr = [
          [0, 10, 20, 30, 40],
          [0, 200, 300, 400, 500]
        ];
        //data array is obtained after structuring arr array
        var data = [
          [{
            x: 0,
            y: 0
          }, {
            x: 10,
            y: 10
          }, {
            x: 20,
            y: 20
          }, {
            x: 30,
            y: 30
          }, {
            x: 40,
            y: 40
          }],
          [{
            x: 0,
            y: 0
          }, {
            x: 10,
            y: 200
          }, {
            x: 20,
            y: 300
          }, {
            x: 30,
            y: 400
          }, {
            x: 40,
            y: 500
          }]
        ];

        const margin = {
          left: 20,
          right: 20,
          top: 20,
          bottom: 80
        };

        const svg = d3.select('svg');
        svg.selectAll("*").remove();

        const width = 200 - margin.left - margin.right;
        const height = 200 - margin.top - margin.bottom;

        //const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
        const g = svg.append('g').attr('transform', `translate(${80},${margin.top})`);

        //************* Axes and Gridlines ***************

        const xAxisG = g.append('g');
        const yAxisG = g.append('g');

        xAxisG.append('text')
          .attr('class', 'axis-label')
          .attr('x', width / 3)
          .attr('y', -10)
          .style('fill', 'black')
          .text(function(d) {
            return "X Axis";
          });

        yAxisG.append('text')
          .attr('class', 'axis-label')
          .attr('id', 'primaryYLabel')
          .attr('x', -height / 2)
          .attr('y', -15)
          .attr('transform', `rotate(-90)`)
          .style('text-anchor', 'middle')
          .style('fill', 'black')
          .text(function(d) {
            return "Y Axis 1";
          });

        // interpolator for X axis -- inner plot region
        var x = d3.scaleLinear()
          .domain([0, d3.max(xValueArray)])
          .range([0, width])
          .nice();

        var yScale = new Array();
        for (var i = 0; i < 2; i++) {
          // interpolator for Y axis -- inner plot region
          var y = d3.scaleLinear()
            .domain([0, d3.max(arr[i])])
            .range([0, height])
            .nice();
          yScale.push(y);
        }

        const xAxis = d3.axisTop()
          .scale(x)
          .ticks(5)
          .tickPadding(2)
          .tickSize(-height)

        const yAxis = d3.axisLeft()
          .scale(yScale[0])
          .ticks(5)
          .tickPadding(2)
          .tickSize(-width);

        yAxisArray = new Array();
        yAxisArray.push(yAxis);
        for (var i = 1; i < 2; i++) {
          var yAxisSecondary = d3.axisLeft()
            .scale(yScale[i])
            .ticks(5)
          yAxisArray.push(yAxisSecondary);
        }

        svg.append("g")
          .attr("class", "x axis")
          .attr("transform", `translate(80,${height-80})`)
          .call(xAxis);

        svg.append("g")
          .attr("class", "y axis")
          .attr("transform", "translate(80,20)")
          .call(yAxis);

        var colors = ["blue", "red"];

        //plot lines
        for (var i = 0; i < 2; i++) {
          var lineFunction = d3.line()
            .x(function(d) {
              return x(d.x);
            })
            .y(function(d) {
              return yScale[i](d.y);
            })
            .curve(d3.curveLinear);

          //plot lines
          var paths = g.append("path")
            .attr("class", "path1")
            .attr("id", "line" + i)
            .attr("d", lineFunction(data[i]))
            .attr("stroke", colors[i])
            .attr("stroke-width", 2)
            .attr("fill", "none")
            .attr("clip-path", "url(#clip)")

          //plot a circle at each data point
          g.selectAll(".dot")
            .data(data[i])
            .enter().append("circle")
            .attr("cx", function(d) {
              return x(d.x)
            })
            .attr("cy", function(d) {
              return yScale[i](d.y);
            })
            .attr("r", 3)
            .attr("class", "blackDot")
            .attr("clip-path", "url(#clip)")
        }

        var translation = 50;
        var textTranslation = 0;
        var yLabelArray = ["Y Axis 1", "Y Axis 2"];

        //loop starts from 1 as primary y axis is already plotted
        for (var i = 1; i < 2; i++) {
          svg.append("g")
            .attr("transform", "translate(" + translation + "," + 20 + ")")
            .call(yAxisArray[i]);

          yAxisG.append('text')
            .attr('x', -height / 2)
            .attr('y', -60)
            .attr('transform', `rotate(-90)`)
            .style('text-anchor', 'middle')
            .style('fill', 'black')
            .text(yLabelArray[i]);

          translation -= 40;
          textTranslation += 40;
        }

        //************* Legend ***************
        var legend = svg.selectAll(".legend")
          .data(data)
          .enter().append("g")

        legend.append("rect")
          .attr("x", width + 65)
          .attr("y", function(d, i) {
            return 30 + i * 20;
          })
          .attr("width", 18)
          .attr("height", 4)
          .style("fill", function(d, i) {
            return colors[i];
          })

        legend.append("text")
          .attr("x", width + 60)
          .attr("y", function(d, i) {
            return 30 + i * 20;
          })
          .attr("dy", ".35em")
          .style("text-anchor", "end")
          .text(function(d, i) {
            return "Value" + (i + 1);
          })
          .on("click", function(d, i) {
            // Determine if current line is visible
            let opacity = d3.select("#line" + i).style("opacity");
            let newOpacity;
            if (opacity == 0) {
            	newOpacity = 1;
            }else {
            	newOpacity = 0
            }
            d3.select("#line" + i).style("opacity", newOpacity);
          });

        var pointLegend = svg.selectAll(".pointLegend")
          .data(data)
          .enter().append("g")

        pointLegend.append("circle")
          .attr("r", 3)
          .attr("cx", width + 70)
          .attr("cy", 70)

        pointLegend.append("text")
          .attr("x", width + 60)
          .attr("y", 70)
          .attr("dy", ".35em")
          .style("text-anchor", "end")
          .on("click", function(d) {
            // Determine if dots are visible
            var active = d.active ? false : true,
              newOpacity = active ? 0 : 1;
            // Hide or show the elements
            d3.selectAll(".blackDot").style("opacity", newOpacity);
            // Update whether or not the elements are active
            d.active = active;
          })
          .text(function(d) {
            return "Data";
          });

.xy_chart {
  position: relative;
  left: 70px;
  top: 100px;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg class="xy_chart"></svg>

这篇关于如何为具有多个y轴的多折线图中的每条线分配特定的id的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆