D3 v4 折线图转换不起作用



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

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 文档:

若要应用过渡,请选择元素,调用selection.transition,然后进行所需的更改。

我在代码中找到了这个:

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 不会以这种方式转换字符串。在首先检查属性是数字还是颜色后,D3 决定使用名为interpolateString的东西。你会认为interpolateString会将字符从a更改为ab更改为abc,但实际上,它所做的只是在字符串中查找数字,并插入这些数字,使字符串的其余部分保持不变。结果是,除非你自己做,否则你不能将像d这样的字符串从空到有数据进行动画处理。

以下是使用attrTween(注意:不是一个好主意(的方法:

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

这实际上将在无文本之间过渡到d属性的整个文本。但是,由于 SVG 路径的工作方式,这看起来不是很好。

还有另一种方法,如您链接到的示例所示(Ryan Morton 在评论中也提到了(:转换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()等待第一个过渡完成,然后再将背景从白色更改为黑色。请注意,不透明度可能更适合为性能设置动画。

最新更新