简体   繁体   English

d3 v4折线图过渡不起作用

[英]d3 v4 line chart transition not working

I would like my line to draw like this example: 我希望我的线条像这样的例子:

https://bl.ocks.org/shimizu/f7ef798894427a99efe5e173e003260d https://bl.ocks.org/shimizu/f7ef798894427a99efe5e173e003260d

The code below does not make any transitions, the chart just appears. 下面的代码没有进行任何转换,只显示图表。

I'm aware of browser caching and that is not the issue. 我知道浏览器缓存,这不是问题。 I've also tried changing the duration and that doesn't help either. 我也试过改变持续时间,这也无济于事。 I feel like I'm probably not being explicit about how I want d3 to transition, but I'm unsure how to give d3 what it wants. 我觉得我可能没有明确表示我希望d3如何转换,但我不确定如何给d3它想要的东西。 Your help is greatly appreciated. 非常感谢您的帮助。

EDIT: x-axis domain: [0, 1]. 编辑:x轴域:[0,1]。 y-axis domain: [-18600, -3300]. y轴域:[ - 18600,-3300]。

// Here's just a few rows of the data
data = [{"threshold": 0.0, "loss": -18600},
        {"threshold": 0.008571428571428572, "loss": -18600},
        {"threshold": 0.017142857142857144, "loss": -18600}]

var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 30, left: 20},
    width = +svg.attr("width") - 400 - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom;


var x = d3.scaleLinear()
    .range([0, width]);

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

var line = d3.line()
    .x(d => x(d.threshold))
    .y(d => y(d.loss));

var g = svg.append("g")
    .attr("transform", "translate(" + (margin.left + 50) + "," + margin.top + ")");

d3.json("static/data/thresh_losses.json", function(thisData) {
 draw(thisData);
});

let draw = function(data) {
    $("svg").empty()
    var x = d3.scaleLinear()
        .range([0, width]);

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

    var line = d3.line()
        .x(d => x(d.threshold))
        .y(d => y(d.loss));

    var g = svg.append("g")
        .attr("transform", "translate(" + (margin.left + 50) + "," + margin.top + ")");

    d3.selectAll("g").transition().duration(3000).ease(d3.easeLinear);

    x.domain([0, d3.max(data, d => d.threshold)]);
    y.domain([d3.max(data, d => d.loss), d3.min(data, d => d.loss)]);

    g.append("g")
        .attr("class", "axis axis--x")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x))
        .append("text")
        .attr("class", "axis-title")
        .attr("y", 18)
        .attr("dy", "1em")
        .attr("x", (height/2) - 40)
        .attr("dx", "1em")
        .style("text-anchor", "start")
        .attr("fill", "#5D6971")
        .text("Threshold");

    g.append("g")
        .attr("class", "axis axis--y")
        .call(d3.axisLeft(y))
        .append("text")
       .attr("class", "axis-title")
       .attr("transform", "rotate(-90)")
       .attr("y", -40)
       .attr("dy", ".71em")
       .attr("x", -height/2 + 40)
       .attr("dx", ".71em")
       .style("text-anchor", "end")
       .attr("fill", "#5D6971")
       .text("Profit ($)");

    var line_stuff = g.selectAll(".line")
        .data([data]);

    line_stuff.enter().append("path").classed("line", true)
           .merge(line_stuff);

    g.selectAll(".line")
      .transition()
      .duration(10000)
      .ease(d3.easeLinear)
      .attr("d", line);
};

From the D3 documentation : D3文档

To apply a transition, select elements, call selection .transition , and then make the desired changes. 要应用转换,请选择元素,调用选择 .transition ,然后进行所需的更改。

I found this in the code: 我在代码中发现了这个:

d3.selectAll("g").transition().duration(3000).ease(d3.easeLinear);

This won't animate anything, because there's no .attr() or .style() at the end—no "desired changes" are being made. 这不会动画任何东西,因为最后没有.attr().style() - 没有“期望的变化”。 It's a transition with no changes to make. 这是一个没有变化的过渡。

Now, let's look at this: 现在,让我们来看看:

g.selectAll(".line")
  .transition()
  .duration(10000)
  .ease(d3.easeLinear)
  .attr("d", line);

This almost fulfills the requirements. 这几乎满足了要求。 It selects .line , creates the transition (and customizes it), and sets the d attribute. 它选择.line ,创建转换(并自定义),并设置d属性。 If you have d set elsewhere, then this would to transition the path from being empty to having all the data, only... 如果您d设置在其他地方,那么这将要被空其所有的数据转换的路径,只...

D3 doesn't transition strings that way. D3不会以这种方式转换字符串。 After first checking if the attribute is a number or color , D3 settles on using something called interpolateString . 在首先检查属性是数字还是颜色之后 ,D3开始使用名为interpolateString东西。 You'd think interpolateString would change characters from a to ab to abc , but actually, all it does is look for numbers within the string, and interpolate those, leaving the rest of the string constant . 您认为interpolateString会将字符从a更改为ababc ,但实际上,它所做的只是查找字符串中的数字,然后插入这些数字,使字符串的其余部分保持不变 The upshot is, you just can't animate a string like d from empty to having data unless you do it yourself. 结果是,除非你自己动手,否则你无法动画像d一样的字符串从空到动画。

Here's how you can do that, using attrTween (note: not a good idea) : 以下是使用attrTween (注意:不是一个好主意)

.attrTween("d", function() {
  return function(t) {
    const l = line(data);
    return l.substring(0, Math.ceil(l.length * t));
  };
})

This will actually transition between no text to the entire text of the d attribute. 这实际上将在没有文本到d属性的整个文本之间转换。 However, because of the way SVG paths work, this doesn't look very good. 但是,由于SVG路径的工作方式,这看起来不太好。

There is another way, as demonstrated in the example you linked to (and also mentioned by Ryan Morton in a comment ): transitioning the stroke-dashoffset . 还有另一种方式,如您链接的示例( Ryan Morton评论中也提到)中所示: 转换stroke-dashoffset Here's how you would do that: 这是你如何做到这一点:

line_stuff.enter().append("path").classed("line", true)
  .merge(line_stuff)
  .attr('d', line)
  .attr("fill", "none")
  .attr("stroke", "black")
  .attr("stroke-dasharray", function(d) {
    return this.getTotalLength()
  })
  .attr("stroke-dashoffset", function(d) {
    return this.getTotalLength()
  });

g.selectAll(".line")
  .transition()
  .duration(10000)
  .ease(d3.easeLinear)
  .attr("stroke-dashoffset", 0);

Essentially, the first part tells D3 to: 基本上,第一部分告诉D3:

  • create the line, make the fill invisible (so you can see the line) 创建线,使填充不可见(所以你可以看到线)
  • make the stroke dashes equal to the total length of the line 使笔划破折号等于该行的总长度
  • offset the dashes, so that the line is completely hidden at the start 偏移破折号,以便在开始时完全隐藏该行

The next part sets up the transition and tells it to transition the offset to 0 (at which point the line will be completely visible because each dash is the same length as the line itself). 下一部分设置转换并告诉它将偏移转换为0(此时该线将完全可见,因为每个破折号与线本身的长度相同)。

If you want to transition the fill, you could change .attr("fill", "none") to .attr("fill", "#fff") , and then do something like this: 如果要转换填充,可以将.attr("fill", "none")更改为.attr("fill", "#fff") ,然后执行以下操作:

g.selectAll(".line")
  .transition()
  .delay(10000)
  .duration(2000)
  .ease(d3.easeLinear)
  .attr('fill', '#000');

This would use .delay() to wait for the first transition to finish before changing the background from white to black. 这将使用.delay()等待第一次转换完成,然后将背景从白色更改为黑色。 Note that opacity might be better to animate for performance . 请注意, 不透明度可能更适合为性能设置动画

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM