D3 v4动画:如何在多个路径上循环多个圆?

时间:2018-07-24 18:54:43

标签: javascript animation d3.js path

受到https://www.nytimes.com/interactive/2018/03/19/upshot/race-class-white-and-black-men.html

的启发

我正在尝试创建一个动画,使标记在y轴上从一个点移动到另一个点的多个级别。感谢Amelia和Mike Bostock在bl.ocks.org和stackoverflow中提供的详细页面。到目前为止,我已经获得了圈子并为其设置了动画。但是,我无法使每个标记都按路径级别循环并进行过渡

此处的路径级别指示它们是高,中还是低(1,2,3)。

使用d3.v4的整个代码已粘贴到下面。我想念什么?

感谢您的帮助。

<!DOCTYPE html>
<meta charset="utf-8">
<title>SANKEY Experiment</title>
<style>

</style>
<body>

<script type="text/javascript" src="d3.v4.js"></script>

<script>

	
	//ref: very important for path animation: https://stackoverflow.com/questions/21226523/trace-path-with-dom-object/21228701#21228701
	//ref: clustered force layout: https://bl.ocks.org/mbostock/7881887
	//ref: data manipulation: http://learnjsdata.com/iterate_data.html
	var margin = {top: 30, right: 10, bottom: 30, left: 20},
		width = 500 - margin.left - margin.right,
		height = 500 - margin.top - margin.bottom;

	var series = [{x:5,y:10},{x:150,y:10}],
		series2 = [{x:5,y:10},{x:50,y:15},{x:100,y:30},{x:150,y:30}],
		series3 = [{x:5,y:10},{x:50,y:22},{x:100,y:50},{x:150,y:50}];
	
	
			
	var data = [{"dim":"a","pos":"high","pathlevel":1,"x1":1,"y1":10,"x2":150,"y2":8},
	{"dim":"b","pos":"high","pathlevel":1,"x1":1,"y1":10,"x2":150,"y2":8},
	{"dim":"a","pos":"mid","pathlevel":2,"x1":1,"y1":10,"x2":150,"y2":28},
	{"dim":"b","pos":"mid","pathlevel":2,"x1":1,"y1":10,"x2":150,"y2":28},
	{"dim":"a","pos":"low","pathlevel":3,"x1":1,"y1":10,"x2":150,"y2":48},
	{"dim":"b","pos":"low","pathlevel":3,"x1":1,"y1":10,"x2":150,"y2":48}]

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

	var y = d3.scaleLinear()
				.domain([10,50])
				.range([0,height]);
				
	var line = d3.line()
		.curve(d3.curveCardinal)
		.x(function(d) { return x(d.x); })
		.y(function(d) { return y(d.y); });
	  var svg = d3.select("body").append("svg").attr("height",600).attr("width",600);

	var chart = svg.append("g").attr("transform","translate("+margin.left+","+margin.top+")");
	
	
	var update = function(series,k){
	
	chart.append("path")
		  .attr("class","route"+k)	
		  .attr("fill","none")
		  .attr("stroke","blue")
		  .attr("d",line(series));
		  }

	update(series,1);
	update(series2,2);
	update(series3,3);
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//create transistions along the path //////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	
	
	var colorScale = d3.scaleOrdinal()
					.domain(['a','b'])
					.range(['orange','darkblue']);
					
	//Get path start point for placing marker
	var path = d3.select('.route1')
	var startPoint = path.attr("d").split(",")[1];
	//path example["M12.885906040268456", "84.48979591836735C12.885906040268456", "84.48979591836735", "241.07382550335572", "84.48979591836735", 
	//"318.9261744966443", "84.48979591836735C396.7785234899329", "84.48979591836735", "480", "84.48979591836735", "480", "84.48979591836735"]
	//selecting class route which represents the path. d represents the path that is held by the path object. in that we split by comma and take the first
	
	console.log(startPoint);
	
	
	var glider = function(data,p){//p for path level

		var marker = chart.selectAll(".marker")
				.data(data)
				.enter().append('circle')
				.attr("class","marker")
				.attr("fill",function(d){ return colorScale(d.dim);})
				//.attr("x",function(d,i){return x(d.x1)+i*10;})
				//.attr("y",function(d,i){return y(d.y1)+i*10;})
				.attr("r",5);
				//.attr("width",10)
				//.attr("height",10);
		
				
		var simulation = d3.forceSimulation()
					.force("x",d3.forceX().strength(0.05))
					.force("y",d3.forceY().strength(0.01))
					.force('charge', d3.forceManyBody().strength(20))
					.force("collide",d3.forceCollide(function(d){return y(d.y1)+4;}))
					.alphaTarget(.03)
					.restart();

		simulation.nodes(data)
				.on('tick',ticked);
		
		function ticked(){
			marker
			.attr("cx",function(d){ return d.x;})
			.attr("cy",function(d){ return d.y;})
		}//end of ticked
			
		//marker.transition().duration(3000).delay(200)
		//		.attr("x",function(d,i){return x(d.x2)+i*10;});

		function translateAlong(path) {
			var l = path.getTotalLength();
			return function (d) {
			  return function (t) {
				var p = path.getPointAtLength(t * l);
				return "translate(" + p.x + "," + p.y + ")";//Move marker
			  }
			}
		}//end of translateAlong

		console.log(marker);
		
		function transition(){
		
			var path2 = d3.select('.route'+p);
			
			marker.attr("transform", "translate(" + startPoint + ")").transition().duration(3000).delay(function(d,i) { return i * 100; })
				.attrTween("transform",translateAlong(path2.node()));
				//.attr("x",function(d,i){return x(d.x2)+i*10;});
			
		}//end of transition
		
		transition();
	}
	
	/*var check = d3.map(data, function(d){return d.pathlevel;}).keys(); //for getting unique values from a column
	
	check.forEach(function(i){
		datapoints = data.filter(function(d){return d.pathlevel==i});
		console.log(i);
		glider(datapoints,i);
		
	});*/
	
	data1 = data.filter(function(d){return d.pathlevel==1});
	data2 = data.filter(function(d){return d.pathlevel==2});
	data3 = data.filter(function(d){return d.pathlevel==3});
	
	//glider(data1,1);
	//glider(data2,2);
	glider(data3,3);
	//glider(data,2);
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/*function createPathTween(d, i, a) {
			var path = this.parentNode.getElementsByTagName("path")[1];
			//i.e., go from this <circle> -> parent <g> -> array of child <path> elements 
               //-> first (and only) element in that array
			//console.log(path);
			var l = path.getTotalLength();
		return function(t) {
			var p = path.getPointAtLength(t * l);
			console.log(p);
			return "translate(" + p.x + "," + p.y  + ")";
			};    
		}//end of tweenpath*/
	
</script>

</body>

0 个答案:

没有答案