javascript - d3 v4 折线图过渡不起作用

标签 javascript d3.js

我希望我的线条像这个例子一样绘制:

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

下面的代码没有做任何转换,图表只是出现了。

我知道浏览器缓存,这不是问题所在。我也尝试过更改持续时间,但这也无济于事。我觉得我可能没有明确说明我希望 d3 如何过渡,但我不确定如何为 d3 提供它想要的东西。非常感谢您的帮助。

编辑:x 轴域:[0, 1]。 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);
};

最佳答案

D3 documentation 开始:

To apply a transition, select elements, call selection.transition, and then make the desired changes.

我在代码中发现了这个:

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

这不会激活任何东西,因为最后没有 .attr().style() — 没有进行“所需的更改”。这是一个无需更改的过渡。

现在,让我们看看这个:

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

这几乎满足了要求。它选择 .line,创建转换(并对其进行自定义),并设置 d 属性。如果您在别处设置了 d,那么这会将路径从空转为拥有所有数据,仅...

D3 不会以这种方式转换字符串。在第一个 checking if the attribute is a number or color 之后,D3 决定使用一种叫做 interpolateString 的东西。你会认为 interpolateString 会将字符从 a 更改为 ab 再到 abc,但实际上,它所做的一切都是 is look for numbers within the string, and interpolate those, leaving the rest of the string constant .结果是,除非您自己动手,否则您无法将 d 这样的字符串从空变为有数据。

使用 attrTween (注意:这不是个好主意),您可以这样做:

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

这实际上会在没有文本到 d 属性的整个文本之间进行转换。但是,由于 SVG 路径的工作方式,这看起来不太好。

还有另一种方法,如您链接到的示例所示(并且 Ryan Mortoncomment 中也提到): transitioning the stroke-dashoffset 。以下是您将如何做到这一点:

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

基本上,第一部分告诉 D3:

  • 创建线条,使填充不可见(这样您就可以看到线条)
  • 使笔划破折号等于直线的总长度
  • 偏移破折号,使线条在开始时完全隐藏

下一部分设置过渡并告诉它将偏移量过渡到 0(此时线条将完全可见,因为每个破折号的长度与线条本身的长度相同)。

如果你想转换填充,你可以将 .attr("fill", "none") 更改为 .attr("fill", "#fff"),然后执行如下操作:

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

这将使用 .delay() 等待第一个转换完成,然后再将背景从白色更改为黑色。请注意 opacity might be better to animate for performance

关于javascript - d3 v4 折线图过渡不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50337429/

相关文章:

javascript - d3js 阻止 forceSimulation 一直重新计算位置

javascript - 为 d3 网络图添加选择框

javascript - D3.js HTML数据表下拉过滤

javascript - 将基于 css 样式而不是图像的自定义元素添加到图表 D3.js

javascript - 不断丢失 session 状态 ASP.NET

javascript - 在 firefox 中提交 jQuery 表单

javascript - ie8 设置样式时找不到成员

Javascript:替换直到找到单词

javascript - Raphael.js 已弃用函数和响应式函数的替代品

javascript - D3 : The data on the axis not mapping