D3.js之饼图动画

本文介绍了使用D3.js实现动态展开的饼图动画效果,包括如何计算各扇区动画时序、应用平滑过渡及添加文字、空隙等元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习D3差不多一个月了。最近看了刚看了echart,发现他里面的饼图初始化的时候是从无到有的打开一圈。所以想要模仿这个动画效果。


如果想要学习D3.js饼图布局可以去看这个网站: http://www.ourd3js.com

开始的想法是这样的:先给出饼图从无到有展开花费的所有时间,再设计饼图每个弧所占的比例,乘以动画总时间,就每个弧所要执行的时间。

在用一个delaytime变量保存每个弧之前所有弧需要展开的时间总和。这是用来延迟这个弧动画时间,从而每个弧的无缝连接动画。

<html>

	<head>
		<meta charset="utf-8">
		<title>饼状图</title>
	</head>

	<style>

	</style>

	<body>
		<script src="../js/d3.js" charset="utf-8"></script>
		<script>
			var width = 400;
			var height = 400;
			var dataset = [30, 10, 43, 55, 13];
			
			var svg = d3.select("body")
				.append("svg")
				.attr("width", width)
				.attr("height", height);
			var pie = d3.layout.pie().sort(null);	//定义饼图的布局
			var piedata = pie(dataset);	//将数据传给pie,就可以得到绘图的数据
			var outerRadius = 150; //外半径
			var innerRadius = 0; //内半径,为0则中间没有空白
			var sum=0;
			piedata.forEach(function(d,i){
				d._endAngle=d.endAngle;//保存这个值,后面动画要用到。
				d.endAngle=d.startAngle;//让每个弧的弧度都是0
				d.duration=2001*(d.data/d3.sum(dataset));
				d.delaytime=sum;
				sum+=d.duration;
			})
			var arc = d3.svg.arc() //弧生成器
				.innerRadius(innerRadius) //设置内半径
				.outerRadius(outerRadius); //设置外半径

			var color = d3.scale.category10();
			var arcs = svg.selectAll("g")		//先添加五个分组元素,用来存放一段弧的相关元素
				.data(piedata)
				.sort(d3.ascending)
				.enter()
				.append("g")
				.attr("transform", function(d,i){
					return "translate(" + (width / 2) + "," + (width / 2) + ")";
				});
			arcs.append("path")		//给每个分组元素g添加一个路径
				.attr("fill", function(d, i) {
					return color(i);
				})
				.attr("d", function(d, i) {
					return arc(d);		
				})
				.transition()
				.duration(function(d,i){
					return d.duration;
				})
				.ease("linear")
				.delay(function(d,i){
					console.log(d.delaytime)
					return d.delaytime;
				})
				.attr("d", function(d, i) {
					d.endAngle=d._endAngle;
					// d.endAngle=d._endAngle;
//					arc.outerRadius(outerRadius+i*10);每个边的长度不一样。
					// console.log(d);     d此时是一个对象包括data、endAngle padAngle StartAngle value	
					console.log(d);
					return arc(d);		//通过之前定义的弧生成器来转化数据
				})
				;	
		</script>
	</body>
</html>

上面就是我实现的代码。但是非常丑陋,并不是那种平滑展开那种效果。

然后最近看了一个API。

transition.attrTween - 在两个属性值之间平滑地过渡。(本人英语不是太好,感谢前人翻译的中文API)

<html>

	<head>
		<meta charset="utf-8">
		<title>饼状图</title>
	</head>

	<style>

	</style>

	<body>
		<script src="../js/d3.js" charset="utf-8"></script>
		<script>
			var width = 400;
			var height = 400;
			var dataset = [30, 10, 43, 55, 13];
			
			// dataset.sort(function(a,b){return b-a;});
			var svg = d3.select("body")
				.append("svg")
				.attr("width", width)
				.attr("height", height);

			var pie = d3.layout.pie().sort(null);	//定义饼图的布局

			var piedata = pie(dataset);	//将数据传给pie,就可以得到绘图的数据
			var outerRadius = 150; //外半径
			var innerRadius = 0; //内半径,为0则中间没有空白
			var sum=0;
			piedata.forEach(function(d,i){
				d._endAngle=d.endAngle;
				d.endAngle=d.startAngle;
				d.duration=2000*(d.data/d3.sum(dataset));//动画时长2秒,计算每一个弧形所用动画时间
				d.delaytime=sum;
				sum+=d.duration;
			})
			var arc = d3.svg.arc() //弧生成器
				.innerRadius(innerRadius) //设置内半径
				.outerRadius(outerRadius); //设置外半径

			var color = d3.scale.category10();
			var arcs = svg.selectAll("g")		//先添加五个分组元素,用来存放一段弧的相关元素
				.data(piedata)
				.enter()
				.append("g")
				.attr("transform", function(d,i){
					return "translate(" + (width / 2) + "," + (width / 2) + ")";
				});

			arcs.append("path")		//给每个分组元素g添加一个路径
				.attr("fill", function(d, i) {
					return color(i);
				})
				.attr("d", function(d, i) {
					console.log(d);
					return arc(d);		
				})
				.transition()
				.duration(function(d,i){
					return d.duration;
				})
				.ease("linear")
				.delay(function(d,i){
					return d.delaytime;
				})
				.attrTween("d", tweenArc(function(d, i) {
					return {
						startAngle: d.startAngle,	
						endAngle: d._endAngle
					};
				}))
				;	
			 function tweenArc(b) {
			 	return function(a, i) {
			 		var d = b.call(this, a, i),
			 			i = d3.interpolate(a, d);
			 			//d保存转换之后的信息
			 			//插值模式,从d.endAnle=d.startAngle到d.endAngle=d._endAngle转换
			 		return function(t) {
			 			return arc(i(t));
			 		};
			 	};
			 }
		</script>

	</body>

</html>

效果如下:


上面看起来的效果就差不多了。



然后填饼图添加文字。因为文字是直接显示上面的,即使弧没有出来文字也在上,感觉不好看,可以跟上面一样给text设置延迟出来的动画。

			arcs.append("text")
				.attr("transform", function(d) {
					//arc.centroid计算出每个弧的中心位置
					return "translate(" + arc.centroid(d) + ")";
				})
				.attr("text-anchor", "middle")
				.text(function(d) {
					return d.data;
				})


也可以给饼图之间添加一些空隙

			d3.selectAll("path").attr("transform",function(d,i){
					var midAngle=(d.startAngle+d.endAngle)/2;
					return "translate("+(1*Math.sin(midAngle))+","+(-1*Math.cos(midAngle))+")";
				})


也可以给添加饼图添加鼠标移入移出事件

这边我实现的是往外移动一点点,也可以实现移入的时候让它变大,echart的饼图就是这样的效果

arcs.select("path").on("mouseover",function(d,i){
					d3.select(this).transition()
						.duration(500)
						.ease("linear")
						.attr("transform", function(d, i) {
							var midAngle = (d.startAngle + d.endAngle) / 2;
							return "translate(" + (20 * Math.sin(midAngle)) + "," + (-20 * Math.cos(midAngle)) + ")";
						})
				})
				.on("mouseout",function(d,i){
					d3.select(this).transition()
						.duration(500)
						.ease("linear")
						.attr("transform", function(d, i) {
							var midAngle=(d.startAngle+d.endAngle)/2;
					return "translate("+(1*Math.sin(midAngle))+","+(-1*Math.cos(midAngle))+")";
							return "translate(0)";
						})
				})

还有给饼图添加一些信息,可以查看这位大神的博客:http://blog.youkuaiyun.com/lzhlzz/article/details/46508041


然后也可以将这些数字放在外面,用连线连接着,大概思路就是将现在文字水平向左或向右平移一段距离(根据弧中心在左边右边来判断)

然后将两点信息保存下来,添加一个polyline 然后传给point就可以了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值