当前位置: 首页 > 知识库问答 >
问题:

D3如何在从下拉菜单中选择新数据后更新图表

司空俊雄
2023-03-14

我正在D3中构建一个瀑布图。当页面加载时,它将呈现默认页面,但用户可以从下拉菜单中选择不同的“公司”和“年份”。我已经能够创建我想要的图表了。但是当我选择任何不同的公司或年份时,D3会在现有图表的基础上添加另一个图表,而不是替换它,这是因为我的目标是HTML中的特定div / svg。我如何使用D3用新数据更新图表,而不是添加另一个top?如果我能让图表条的移动带有过渡效果,那就太棒了。

超文本标记语言是一个简单的svg:

<svg class="chart"></svg>

这是创建Ajax调用成功时我调用的图表的函数:

function waterfallChart (dataset) {

            var data = [];

            for (var key in dataset[0]) {
              data.push({
                name: key,
                value: dataset[0][key]
              })
            }

        var margin = {top: 20, right: 30, bottom: 30, left: 40},        
            width = 960 - margin.left - margin.right,
            height = 500 - margin.top - margin.bottom,
            padding = 0.3;

    var x = d3.scaleBand()
            .domain(data.map(function(d) {
                return d.name
                }))
            .range([0, width])
            .padding(padding);

    var y = d3.scaleLinear()
        .range([height, 0]);

    var xAxis = d3.axisBottom(x)


    var yAxis = d3.axisLeft(y)
        .tickFormat(function(d) {
            return dollarFormatter(d);
        });

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

        var cumulative = 0;
        for (var i = 0; i < data.length; i++) {
            data[i].start = cumulative;
            cumulative += data[i].value;
            data[i].end = cumulative;

            data[i].class = (data[i].value >= 0) ? 'positive' : 'negative'
        }
        data.push({
            name: 'Total',
            end: cumulative,
            start: 0,
            class: 'total'
        });

        x.domain(data.map(function(d) {
            return d.name;
        }));
        y.domain([0, d3.max(data, function(d) {
            return d.end;
        })]);

        chart.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis);

        chart.append("g")
            .attr("class", "y axis")
            .call(yAxis);

        var bar = chart.selectAll(".bar")
            .data(data)
            .enter().append("g")
            .attr("class", function(d) {
                return "bar " + d.class
            })
            .attr("transform", function(d) {
                return "translate(" + x(d.name) + ",0)";
            });

        bar.append("rect")
            .attr("y", function(d) {
                return y(Math.max(d.start, d.end));
            })
            .attr("height", function(d) {
                return Math.abs(y(d.start) - y(d.end));
            })
            .attr("width", x.bandwidth());

        bar.append("text")
            .attr("x", x.bandwidth() / 2)
            .attr("y", function(d) {
                return y(d.end) + 5;
            })
            .attr("dy", function(d) {
                return ((d.class == 'negative') ? '-' : '') + ".75em"
            })
            .text(function(d) {
                return dollarFormatter(d.end - d.start);
            });

        bar.filter(function(d) {
                return d.class != "total"
            }).append("line")
            .attr("class", "connector")
            .attr("x1", x.bandwidth() + 5)
            .attr("y1", function(d) {
                return y(d.end)
            })
            .attr("x2", x.bandwidth() / (1 - padding) - 5)
            .attr("y2", function(d) {
                return y(d.end)
            })

            function dollarFormatter(n) {
                  n = Math.round(n);
                  var result = n;
                  if (Math.abs(n) > 1000) {
                    result = Math.round(n/1000) + 'B';
                  }
                  return '$ ' + result;
                }
        }

下面是我有事件侦听器的代码,在选择时它将运行上述函数:

$("#airline-selected, #year-selected").change(function chartsData(event) {
            event.preventDefault();
            var airlineSelected = $('#airline-selected').find(":selected").val();
            var yearSelected = $('#year-selected').find(":selected").val();

            $.ajax({
                url: "{% url 'airline_specific_filtered' %}",
                method: 'GET',
                data : {
                    airline_category: airlineSelected,
                    year_category: yearSelected
                        },
                success: function(dataset){
                        waterfallChart(dataset)
         },
                error: function(error_data){
                console.log("error")
                console.log(error_data)
         }
            })
        });

共有1个答案

柴霖
2023-03-14

你在这里错过了一些非常重要的东西。如果要对数据进行更新,则需要执行一些操作。

>

  • 为<code>data()

    .data(data, function(d) { return d.name })
    

    现在,假设<code>d,d3将能够区分数据项。名称是唯一标识符。

    您需要为更新期间删除的数据提供 exit()。您需要保存数据联接选择,以便可以对其调用 enterexit

    var bar = chart.selectAll(".bar")
              .data(data, function(d) { return d.name})
    

    现在您可以调用:bar.exit()。删除()来删除已删除的项目,调用bar.enter()来添加项目。

    您需要选择一个没有调用< code>enter()来更新属性的选项。

    可能更多的是样式问题,但您应该在更新函数之外设置SVG和边距,因为它们的状态相同。您仍可以通过在更新中调用相应的函数来更新轴和缩放。

    你发布的代码对其他人来说有点难运行—

    下面是一个示例,根据您的代码在两个数据集之间更新setInterval。但是您还应该看看一般的更新模式——它们非常简单,但几乎包含了您需要了解的所有内容。(https://bl.ocks.org/mbostock/3808234)

    dataset = [
            {name: "Albert", start: 0, end:220},
            {name: "Mark", start: 0, end:200},
            {name: "Søren", start: 0, end:100},
            {name: "Immanuel", start: 0, end:60},
            {name: "Michel", start: 0, end:90},
            {name: "Jean Paul", start: 0, end: 80}
        ]
         dataset2 = [
            {name: "Albert", start: 0, end:20},
             {name: "Immanuel", start:0, end:220},
            {name: "Jaques", start: 0, end:100},
            {name: "Gerhard", start:0 , end:50},
            {name: "Søren", start: 0, end:150},
            {name: "William", start: 0, end: 180}
        ]
    
    var margin = {
        top: 10,
        right: 30,
        bottom: 30,
        left: 40
      },
      width = 400 - margin.left - margin.right,
      height = 200 - margin.top - margin.bottom,
      padding = 0.3;
    
    var chart = d3.select(".chart")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    var x = d3.scaleBand()
      .range([0, width])
      .padding(padding);
    
    var y = d3.scaleLinear()
      .range([height, 0])
    
    chart.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
    chart.append("g")
      .attr("class", "y axis")
    
    var currentData = dataset
    
    waterfallChart(currentData)
    
    setInterval(function() {
      currentData = currentData === dataset ? dataset2 : dataset
      waterfallChart(currentData)
    }, 3000)
    
    function waterfallChart(data) {
      var t = d3.transition()
        .duration(750)
    
      x.domain(data.map(function(d) {
        return d.name
      }))
      y.domain([0, d3.max(data, function(d) {
        return d.end
      })])
      var xAxis = d3.axisBottom(x)
    
      var yAxis = d3.axisLeft(y)
    
      d3.select('g.x').transition(t).call(xAxis)
      d3.select('g.y').call(yAxis)
    
      var bar = chart.selectAll(".bar")
        .data(data, function(d) {
          return d.name
        })
    
      // ENTER -- ADD ITEMS THAT ARE NEW IN DATA
      bar.enter().append("g")
        .attr("transform", function(d) {
          return "translate(" + x(d.name) + ",0)"
        })
        .attr("class", 'bar')
        .append("rect")
        .attr("y", function(d) {
          return y(Math.max(d.start, d.end));
        })
        .attr("height", function(d) {
          return Math.abs(y(d.start) - y(d.end));
        })
        .attr("width", x.bandwidth())
    
      // UPDATE EXISTING ITEMS
      chart.selectAll(".bar")
        .transition(t)
        .attr("transform", function(d) {
          return "translate(" + x(d.name) + ",0)"
        })
        .select('rect')
        .attr("y", function(d) {
          return y(Math.max(d.start, d.end))
        })
        .attr("height", function(d) {
          return Math.abs(y(d.start) - y(d.end))
        })
        .attr("width", x.bandwidth())
    
      // REMOVE ITEMS DELETED FROM DATA
      bar.exit().remove()
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg class="chart"></svg>

  •  类似资料:
    • 问题内容: 我正在建立一个网站来学习编码,并且正在尝试建立一个工具,用户单击一个选择/下拉列表,其中包含从数据库 cat 提取的某些类别名称,然后另一个选择与从数据库 subcat 提取的子类别名称一起出现。这几乎与Yelp的(向下分类)完全一样,就像Yelp的(向下分类)一样。 我还做了一个图: 我已经有一个从 cat 数据库中提取的类别下拉列表: 我有一个从subcat数据库中提取的subca

    • 我想从下面的列表中选择一个使用selenium的选项: 这里 但问题是没有列表可供选择。 在此输入图像说明 我到目前为止的代码: 需要帮助!!

    • 问题内容: 我在尝试制作自己的内容时遇到了麻烦,具体取决于其他方面的选择值。第一个的内容来自我的数据库中的一个表,并且运行良好,但是第二个的内容应该来自另一个表,但是我无法使其工作。这是我的,我只是在尝试证明其工作原理: 这是我的Bean部分,应该获取第二个菜单的内容: 我已经为此工作了好几个小时,但还是一无所获,我真的很着急,如果能在这里给我一些帮助,我将不胜感激。非常感谢您:D 问题答案: i

    • 我正在编写一个python脚本,它将调用一个网页,并从下拉列表中选择一个选项来下载该文件。为了完成这个任务,我使用chropath。它是一个浏览器扩展,可以为您提供网页上任何按钮或字段的相对xpath或id,我们可以使用它从python脚本调用它。 上图显示了下拉菜单,其中我必须选择作为年份并下载文件。在图像的下部,您可以看到我使用了来获取下拉菜单的相对xpath,即 下面是我使用的代码: 从上面

    • 各位前端大佬们,小白求助Echarts图表根据下拉列表选择日期更新数据。 这种功能应该怎么实现,或者有没有什么案例可以借鉴。 感激感激������ 尝试了写写 和百度 没有结果��

    • 问题内容: 我试图使用AngularJS创建一个链接/级联的下拉列表(选择元素),但是我很难用我的对象属性过滤和更新“ selected”属性。 首次加载页面时,所选项目将被过滤并正确显示在下拉菜单中。更改父级下拉菜单后,子级选择项不会抓住已过滤列表中的第一个项,导致子级下拉项不会更新。 任何见解将不胜感激,请注意,我将父/子/孙子数组分开(而不是在子数组中),因为最终我将从SQL中的单独spoc