【问题标题】:How to hide and show points on a line graph如何隐藏和显示折线图上的点
【发布时间】:2019-10-07 08:12:54
【问题描述】:

单击图例文本时,我试图在折线图上隐藏和显示点。这是我当前小提琴的link。从我的小提琴来看,我的蓝线工作正常,因为它在每次点击“价值”时都会显示和隐藏。但是,当我单击“点”时,点并没有这样做。

我也尝试按照link 中的建议进行操作,但无济于事。非常感谢任何帮助!

    var data = [ {x: 0, y: 0}, {x: 5, y: 30}, {x: 10, y: 40},
                {x: 15, y: 60}, {x: 20, y: 70}, {x: 25, y: 100} ];

    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})`);


    var x = d3.scaleLinear()
            .domain([0, d3.max(data, function(d){ return d.x; })])
            .range([0,width])
            .nice();

    var y = d3.scaleLinear()
            .domain([0, d3.max(data, function(d){ return d.y; })])
            .range([0,height])
            .nice();

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

    const yAxis = d3.axisLeft()
                .scale(y)
                .ticks(5)
                .tickPadding(5)
                .tickSize(-width);

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

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

    var lineFunction = d3.line()
    .x(function(d) {return x(d.x); })
    .y(function(d) {return y(d.y); })
    .curve(d3.curveLinear);

    //defining and plotting the lines
    var path = g.append("path")
                .attr("class", "path1")
                .attr("id", "blueLine")
                .attr("d", lineFunction(data))
                .attr("stroke", "blue")
                .attr("stroke-width", 2)
                .attr("fill", "none")
                .attr("clip-path", "url(#clip)");

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

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

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

            legend.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 "Value";
                });

      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(){
            // Determine if dots are visible
            var active   = blackDot.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
            blackDot.active = active;
            })
              .text(function(d) {
                return "Point";
              });

【问题讨论】:

    标签: javascript html d3.js


    【解决方案1】:

    问题在于您跟踪点击状态的方式。具体来说,这个变量为行...

    //Determine if current line is visible
    var active = blueLine.active ? false : true;
    

    ...和这个圈子的变量:

    // Determine if dots are visible
    var active = blackDot.active ? false : true;
    

    该行(实际上是<path> 元素)有一个名为blueLine 的ID。因此,元素本身是window 对象的属性,即全局变量。例如,看看这个:

    console.log(window.foo)
    <div id="foo"></div>

    因此,blueLine.active 有效,即使您从未在代码中的任何位置声明 blueLine

    但是,虽然线路有 id,但您的圈子却没有(无论是否blackDot)。此外,有问题的行是只有一个,因此可以有一个 id,但你有 几个圈子,因此,不能使用 id(更多关于下面)。

    因此,解决方案是以另一种方式跟踪点击状态。例如,使用数据的属性:

    .on("click", function(d){
        // Determine if dots are visible
        var active   = d.active ? false : true;
    

    这是更改后的代码:

    var data = [{
        x: 0,
        y: 0
      }, {
        x: 5,
        y: 30
      }, {
        x: 10,
        y: 40
      },
      {
        x: 15,
        y: 60
      }, {
        x: 20,
        y: 70
      }, {
        x: 25,
        y: 100
      }
    ];
    
    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})`);
    
    
    var x = d3.scaleLinear()
      .domain([0, d3.max(data, function(d) {
        return d.x;
      })])
      .range([0, width])
      .nice();
    
    var y = d3.scaleLinear()
      .domain([0, d3.max(data, function(d) {
        return d.y;
      })])
      .range([0, height])
      .nice();
    
    const xAxis = d3.axisTop()
      .scale(x)
      .ticks(5)
      .tickPadding(5)
      .tickSize(-height)
    
    const yAxis = d3.axisLeft()
      .scale(y)
      .ticks(5)
      .tickPadding(5)
      .tickSize(-width);
    
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", `translate(20,${height-margin.top-60})`)
      .call(xAxis);
    
    svg.append("g")
      .attr("class", "y axis")
      .attr("transform", "translate(20,20)")
      .call(yAxis);
    
    var lineFunction = d3.line()
      .x(function(d) {
        return x(d.x);
      })
      .y(function(d) {
        return y(d.y);
      })
      .curve(d3.curveLinear);
    
    //defining and plotting the lines
    var path = g.append("path")
      .attr("class", "path1")
      .attr("id", "blueLine")
      .attr("d", lineFunction(data))
      .attr("stroke", "blue")
      .attr("stroke-width", 2)
      .attr("fill", "none")
      .attr("clip-path", "url(#clip)");
    
    // plot a circle at each data point
    g.selectAll(".dot")
      .data(data)
      .enter().append("circle")
      .attr("cx", function(d) {
        return x(d.x);
      })
      .attr("cy", function(d) {
        return y(d.y);
      })
      .attr("r", 3)
      .attr("class", "blackDot")
      .attr("clip-path", "url(#clip)");
    
    //************* Legend ***************
    var legend = svg.selectAll(".legend")
      .data(data)
      .enter().append("g")
    
    legend.append("rect")
      .attr("x", width + 65)
      .attr("y", 50)
      .attr("width", 18)
      .attr("height", 4)
      .style("fill", "blue")
    
    legend.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 "Value";
      });
    
    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 "Point";
      });
    .xy_chart {
      position: relative;
      left: 50px
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg class="xy_chart"></svg>

    请记住,虽然不建议将单个 id 设置为多个元素,但它会起作用,正如您在下面的演示中所见,我只是将您的代码按原样和将blackDot id 设置为圆圈:

    var data = [{
        x: 0,
        y: 0
      }, {
        x: 5,
        y: 30
      }, {
        x: 10,
        y: 40
      },
      {
        x: 15,
        y: 60
      }, {
        x: 20,
        y: 70
      }, {
        x: 25,
        y: 100
      }
    ];
    
    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})`);
    
    
    var x = d3.scaleLinear()
      .domain([0, d3.max(data, function(d) {
        return d.x;
      })])
      .range([0, width])
      .nice();
    
    var y = d3.scaleLinear()
      .domain([0, d3.max(data, function(d) {
        return d.y;
      })])
      .range([0, height])
      .nice();
    
    const xAxis = d3.axisTop()
      .scale(x)
      .ticks(5)
      .tickPadding(5)
      .tickSize(-height)
    
    const yAxis = d3.axisLeft()
      .scale(y)
      .ticks(5)
      .tickPadding(5)
      .tickSize(-width);
    
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", `translate(20,${height-margin.top-60})`)
      .call(xAxis);
    
    svg.append("g")
      .attr("class", "y axis")
      .attr("transform", "translate(20,20)")
      .call(yAxis);
    
    var lineFunction = d3.line()
      .x(function(d) {
        return x(d.x);
      })
      .y(function(d) {
        return y(d.y);
      })
      .curve(d3.curveLinear);
    
    //defining and plotting the lines
    var path = g.append("path")
      .attr("class", "path1")
      .attr("id", "blueLine")
      .attr("d", lineFunction(data))
      .attr("stroke", "blue")
      .attr("stroke-width", 2)
      .attr("fill", "none")
      .attr("clip-path", "url(#clip)");
    
    // plot a circle at each data point
    g.selectAll(".dot")
      .data(data)
      .enter().append("circle")
      .attr("cx", function(d) {
        return x(d.x);
      })
      .attr("cy", function(d) {
        return y(d.y);
      })
      .attr("r", 3)
      .attr("id", "blackDot")
      .attr("class", "blackDot")
      .attr("clip-path", "url(#clip)");
    
    //************* Legend ***************
    var legend = svg.selectAll(".legend")
      .data(data)
      .enter().append("g")
    
    legend.append("rect")
      .attr("x", width + 65)
      .attr("y", 50)
      .attr("width", 18)
      .attr("height", 4)
      .style("fill", "blue")
    
    legend.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 "Value";
      });
    
    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() {
        // Determine if dots are visible
        var active = blackDot.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
        blackDot.active = active;
      })
      .text(function(d) {
        return "Point";
      });
    .xy_chart {
      position: relative;
      left: 50px
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg class="xy_chart"></svg>

    但是,同样,不建议为多个元素设置相同的 id:id 必须是唯一的。

    【讨论】:

    • 感谢您的精彩解释!我认为为点设置一个 id 是行不通的,这就是为什么我没有首先尝试它,因此我为点设置了一个类。显然它也可以,但现在我知道这样做是不可取的,因为 id 必须是唯一的,我应该使用 datum 的属性。
    【解决方案2】:

    您可以查看this example

    注意工具提示选项中的 : .style("visibility", "hidden")。

    【讨论】:

      猜你喜欢
      • 2013-04-28
      • 1970-01-01
      • 2013-10-25
      • 2020-03-22
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      相关资源
      最近更新 更多