从D3 V3到V5的代码需要额外的更新才能正确渲染

  • 本文关键字:更新 V3 D3 V5 代码 javascript d3.js
  • 更新时间 :
  • 英文 :


我尝试从一个使用d3 v3 的块中调整代码时发现了一些问题。

首次执行时未正确显示图表,但是,通过调用函数changeData更新两次时,它将正确显示。在D3 V3中,图表显示。我在做什么错?

这是显示错误的演示:

let svg;
let radius;
let pie;
let arc;
let outerArc;
let data_ready;
let slice;
let width = 700, height= 400;
let dataChart = [];
svg = d3.select(".chart-d3")
						.append("svg")
						.append("g");
radius = Math.min(width, height) / 2.5;
svg.append("g").attr("class", "slices").attr("name", "slices");
svg.append("g").attr("class", "labels");
svg.append("g").attr("class", "lines");
pie = d3.pie()
				.sort(null)
				.value(function(d){
					return d.value;
				});
arc = d3.arc()
					.outerRadius(radius * 0.8)
					.innerRadius(radius * 0.4);
outerArc = d3.arc()
					.innerRadius(radius * 0.9)
					.outerRadius(radius * 0.9);
svg.attr("transform", "translate (" + width / 2 + "," + height / 2 + ")" );
let key = function(d){
  return d.data.label;
}
var color = d3.scaleOrdinal()
	.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing", "elit", "sed", "do", "eiusmod", "tempor", "incididunt"])
	.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
function randomData(){
	let labels = color.domain();
	let dataFor =  labels.map(function(label){
		return { label : label, value: Math.random() }
	});
	return dataFor;
}
changeData(randomData());
function changeData(data){
	data_ready = pie(data);
	slice = svg.select(".slices").selectAll("path.slice").data(data_ready, key);
	slice
		.enter()
		.append('path')
		.style('fill', function(d){ return color(d.data.label) })
		.attr("class", "slice");
	slice
		.transition().duration(1000)
			.attrTween("d", function(d) {
				this._current = this._current || d;
				var interpolate = d3.interpolate(this._current, d);
				this._current = interpolate();
				return function(t) {
					return arc(interpolate(t));
				};
			})   
	slice.exit()
		.remove();
	/* ------- TEXT LABELS -------*/
	var text = svg.select(".labels").selectAll("text")
		.data(data_ready, key);
	text.enter()
		.append("text")
		.attr("dy", ".35em")
		.text(function(d) {
			return d.data.label + "(" + Math.round(d.data.value * 1000) + ")";
		});
		text.transition().duration(1000)
		.attrTween("transform", function(d){
			this._current = this._current || d;
			let interpolate = d3.interpolate(this._current, d);
			this._current = interpolate(0);
			return function(t){
				let d2 = interpolate(t);
				let pos = outerArc.centroid(d2);
				pos[0] = radius * (midAngle(d) < Math.PI ? 1 : -1);
				return "translate(" + pos + ")";
			}
		})
		.styleTween("text-anchor", function(d){
			this._current = this._current || d;
			var interpolate = d3.interpolate(this._current, d);
			this._current = interpolate(0);
			return function(t) {
				var d2 = interpolate(t);
				return midAngle(d2) < Math.PI ? "start":"end";
			};
		});
	function midAngle(d){
		return d.startAngle + (d.endAngle - d.startAngle)/2;
	}
	
	text.exit()
		.remove();
	/* ------- SLICE TO TEXT POLYLINES -------*/
	var polyline = svg.select(".lines").selectAll("polyline")
		.data(data_ready, key);
	
	polyline.enter()
		.append("polyline");
	polyline.transition().duration(1000)
		.attrTween("points", function(d){
			this._current = this._current || d;
			var interpolate = d3.interpolate(this._current, d);
			this._current = interpolate(0);
			return function(t) {
				var d2 = interpolate(t);
				var pos = outerArc.centroid(d2);
				pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
				return [arc.centroid(d2), outerArc.centroid(d2), pos];
			};			
		});
	
	polyline.exit()
		.remove();
}
.chart-d3{
	height: 400px;
	width: 700px;
	margin: 10px auto; box-shadow: 1px 1px 10px 3px gray;
  display: flex;
  flex-direction: column;
}
svg{
	width: 100%;
	height: 100%;
}
path.slice{
	stroke-width: 2px;
}
polyline{
	opacity: .3;
	stroke: black;
	stroke-width: 2px;
	fill: none;
}
button{
height: 40px;
}
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
	<div class="chart-d3">
			<button onclick="changeData(randomData())">Update</button>
	</div>
</body>
</html>

与d3 v4 一样,常规更新模式的行为已更改。来自ChangElog:

选择。应用不再合并进入更新选择的节点;使用选择 .merge在数据加入后组合和更新。

这就是为什么您的图形在第一次运行中被打破的原因;未选择新输入的元素,因此未转移到其最终位置。这是在第二次运行期间纠正的,因为相同的元素将全部都是更新选择的一部分。

而不是做

slice = svg.select(".slices").selectAll("path.slice").data(data_ready, key);
slice
  .enter()
  .append('path')
  .style('fill', function(d){ return color(d.data.label) })
  .attr("class", "slice");
slice   // <-- This is just the update selection without newly entered elements
  .transition().duration(1000)

您的代码应该喜欢:

slice = svg.select(".slices").selectAll("path.slice").data(data_ready, key);
slice = slice     // <══ 2. Store in update selection ══════════════════════╗
  .enter()                                                               // ║
  .append('path')                                                        // ║
  .style('fill', function(d){ return color(d.data.label) })              // ║
  .attr("class", "slice")                                                // ║
  .merge(slice);  // ═══ 1. Merge update selection into enter selection ════╝
slice   // <-- Now, this holds both entered as well as updated elements.
  .transition().duration(1000)

当然,需要将同样的内容应用于文本和各个小节。

看更新的工作演示:

let svg;
let radius;
let pie;
let arc;
let outerArc;
let data_ready;
let slice;
let width = 700, height= 400;
let dataChart = [];
svg = d3.select(".chart-d3")
						.append("svg")
						.append("g");
radius = Math.min(width, height) / 2.5;
svg.append("g").attr("class", "slices").attr("name", "slices");
svg.append("g").attr("class", "labels");
svg.append("g").attr("class", "lines");
pie = d3.pie()
				.sort(null)
				.value(function(d){
					return d.value;
				});
arc = d3.arc()
					.outerRadius(radius * 0.8)
					.innerRadius(radius * 0.4);
outerArc = d3.arc()
					.innerRadius(radius * 0.9)
					.outerRadius(radius * 0.9);
svg.attr("transform", "translate (" + width / 2 + "," + height / 2 + ")" );
let key = function(d){
  return d.data.label;
}
var color = d3.scaleOrdinal()
	.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing", "elit", "sed", "do", "eiusmod", "tempor", "incididunt"])
	.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
function randomData(){
	let labels = color.domain();
	let dataFor =  labels.map(function(label){
		return { label : label, value: Math.random() }
	});
	return dataFor;
}
changeData(randomData());
function changeData(data){
	data_ready = pie(data);
	slice = svg.select(".slices").selectAll("path.slice").data(data_ready, key);
	slice = slice
		.enter()
		.append('path')
		.style('fill', function(d){ return color(d.data.label) })
		.attr("class", "slice")
    .merge(slice);
	slice
		.transition().duration(1000)
			.attrTween("d", function(d) {
				this._current = this._current || d;
				var interpolate = d3.interpolate(this._current, d);
				this._current = interpolate();
				return function(t) {
					return arc(interpolate(t));
				};
			})   
	slice.exit()
		.remove();
	/* ------- TEXT LABELS -------*/
	var text = svg.select(".labels").selectAll("text")
		.data(data_ready, key);
	text = text.enter()
		.append("text")
		.attr("dy", ".35em")
		.text(function(d) {
			return d.data.label + "(" + Math.round(d.data.value * 1000) + ")";
		}).merge(text);
		text.transition().duration(1000)
		.attrTween("transform", function(d){
			this._current = this._current || d;
			let interpolate = d3.interpolate(this._current, d);
			this._current = interpolate(0);
			return function(t){
				let d2 = interpolate(t);
				let pos = outerArc.centroid(d2);
				pos[0] = radius * (midAngle(d) < Math.PI ? 1 : -1);
				return "translate(" + pos + ")";
			}
		})
		.styleTween("text-anchor", function(d){
			this._current = this._current || d;
			var interpolate = d3.interpolate(this._current, d);
			this._current = interpolate(0);
			return function(t) {
				var d2 = interpolate(t);
				return midAngle(d2) < Math.PI ? "start":"end";
			};
		});
	function midAngle(d){
		return d.startAngle + (d.endAngle - d.startAngle)/2;
	}
	
	text.exit()
		.remove();
	/* ------- SLICE TO TEXT POLYLINES -------*/
	var polyline = svg.select(".lines").selectAll("polyline")
		.data(data_ready, key);
	
	polyline = polyline.enter()
		.append("polyline").merge(polyline);
	polyline.transition().duration(1000)
		.attrTween("points", function(d){
			this._current = this._current || d;
			var interpolate = d3.interpolate(this._current, d);
			this._current = interpolate(0);
			return function(t) {
				var d2 = interpolate(t);
				var pos = outerArc.centroid(d2);
				pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
				return [arc.centroid(d2), outerArc.centroid(d2), pos];
			};			
		});
	
	polyline.exit()
		.remove();
}
.chart-d3{
	height: 400px;
	width: 700px;
	margin: 10px auto; box-shadow: 1px 1px 10px 3px gray;
  display: flex;
  flex-direction: column;
}
svg{
	width: 100%;
	height: 100%;
}
path.slice{
	stroke-width: 2px;
}
polyline{
	opacity: .3;
	stroke: black;
	stroke-width: 2px;
	fill: none;
}
button{
height: 40px;
}
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
	<div class="chart-d3">
			<button onclick="changeData(randomData())">Update</button>
	</div>
</body>
</html>

最新更新