如何实现立体的 环形

482 阅读6分钟

this

image.png

  1. 6个面
  2. 每个面都是单色+透明
  3. 每个换 高度不同

d3 demo

blog.csdn.net/qq_26562641…

bl.ocks.org/NPashaP/999… image.png

github.com/d3/d3/wiki/…

基于 donut3d.js 实现齿轮 增加了左侧面和右侧面

www.jianshu.com/p/601042dae…

image.png

正向

image.png

!(function () {
	var Donut3D = {}

	function pieTop(d, rx, ry, ir) {
		if (d.endAngle - d.startAngle == 0) return 'M 0 0'
		var sx = rx * Math.cos(d.startAngle),
			sy = ry * Math.sin(d.startAngle),
			ex = rx * Math.cos(d.endAngle),
			ey = ry * Math.sin(d.endAngle)

		var ret = []
		ret.push(
			'M',
			sx,
			sy,
			'A',
			rx,
			ry,
			'0',
			d.endAngle - d.startAngle > Math.PI ? 1 : 0,
			'1',
			ex,
			ey,
			'L',
			ir * ex,
			ir * ey
		)
		ret.push('A', ir * rx, ir * ry, '0', d.endAngle - d.startAngle > Math.PI ? 1 : 0, '0', ir * sx, ir * sy, 'z')
		return ret.join(' ')
	}
	function pieBottom(d, rx, ry, h, ir) {
		if (d.endAngle - d.startAngle == 0) return 'M 0 0'
		var sx = rx * Math.cos(d.startAngle),
			sy = ry * Math.sin(d.startAngle),
			ex = rx * Math.cos(d.endAngle),
			ey = ry * Math.sin(d.endAngle)

		var ret = []
		ret.push(
			'M',
			sx,
			sy + h,
			'A',
			rx,
			ry,
			'0',
			d.endAngle - d.startAngle > Math.PI ? 1 : 0,
			'1',
			ex,
			ey + h,
			'L',
			ir * ex,
			ir * ey + h
		)
		ret.push('A', ir * rx, ir * ry, '0', d.endAngle - d.startAngle > Math.PI ? 1 : 0, '0', ir * sx, ir * sy + h, 'z')
		return ret.join(' ')
	}

	function pieOuter(d, rx, ry, h) {
		var startAngle = d.startAngle > Math.PI ? Math.PI : d.startAngle
		var endAngle = d.endAngle > Math.PI ? Math.PI : d.endAngle

		var sx = rx * Math.cos(startAngle),
			sy = ry * Math.sin(startAngle),
			ex = rx * Math.cos(endAngle),
			ey = ry * Math.sin(endAngle)

		var ret = []
		ret.push('M', sx, h + sy, 'A', rx, ry, '0 0 1', ex, h + ey, 'L', ex, ey, 'A', rx, ry, '0 0 0', sx, sy, 'z')
		return ret.join(' ')
	}

	//	return pieInner(d, rx + 0.5, ry + 0.5, d.data.h, ir)
	function pieInner(d, rx, ry, h, ir) {
		var startAngle = d.startAngle < Math.PI ? Math.PI : d.startAngle
		var endAngle = d.endAngle < Math.PI ? Math.PI : d.endAngle

		var sx = ir * rx * Math.cos(startAngle),
			sy = ir * ry * Math.sin(startAngle),
			ex = ir * rx * Math.cos(endAngle),
			ey = ir * ry * Math.sin(endAngle)

		var ret = []
		// ret.push('M', sx, sy, 'L', sx + 30, sy + 30)
		ret.push(
			'M',
			sx,
			sy,
			'A',
			ir * rx,
			ir * ry,
			'0 0 1',
			ex,
			ey,
			'L',
			ex,
			h + ey,
			'A',
			ir * rx,
			ir * ry,
			'0 0 0',
			sx,
			h + sy,
			'z'
		)
		return ret.join(' ')
	}

	//https://www.jianshu.com/p/601042dae817
	// 左侧面
	/*
  a ←   b
  ↓     ↑  (S)
  a' →  b'
    (E)
  */

	function pieLeftSide(d, rx, ry, h, ir) {
		var startAngle = d.startAngle
		var endAngle = d.endAngle

		var sx = rx * Math.cos(startAngle),
			sy = ry * Math.sin(startAngle),
			ex = rx * Math.cos(endAngle),
			ey = ry * Math.sin(endAngle)

		var ret = []
		ret.push('M', ir * ex, h + ir * ey, 'L', ir * ex, ir * ey, 'L', ex, ey, 'L', ex, h + ey, 'z')
		return ret.join(' ')
	}

	// 右侧面
	//↑↓← →
	/*
  a ←   b
  ↓     ↑  (S)
  a' →  b'
    (E)
  */

	function pieRightSide(d, rx, ry, h, ir) {
		var startAngle = d.startAngle
		var endAngle = d.endAngle

		var sx = rx * Math.cos(startAngle),
			sy = ry * Math.sin(startAngle),
			ex = rx * Math.cos(endAngle),
			ey = ry * Math.sin(endAngle)

		var ret = []
		ret.push('M', sx, h + sy, 'L', sx, sy, 'L', ir * sx, ir * sy, 'L', ir * sx, h + ir * sy, 'z')
		return ret.join(' ')
	}

	function getPercent(d) {
		return d.endAngle - d.startAngle > 0.2
			? Math.round((1000 * (d.endAngle - d.startAngle)) / (Math.PI * 2)) / 10 + '%'
			: ''
	}

	Donut3D.transition = function (id, data, rx, ry, h, ir) {
		function arcTweenInner(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieInner(i(t), rx + 0.5, ry + 0.5, h, ir)
			}
		}
		function arcTweenTop(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieTop(i(t), rx, ry, ir)
			}
		}
		function arcTweenOuter(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieOuter(i(t), rx - 0.5, ry - 0.5, h)
			}
		}
		function textTweenX(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return 0.6 * rx * Math.cos(0.5 * (i(t).startAngle + i(t).endAngle))
			}
		}
		function textTweenY(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return 0.6 * rx * Math.sin(0.5 * (i(t).startAngle + i(t).endAngle))
			}
		}

		var _data = d3.layout
			.pie()
			.sort(null)
			.value(function (d) {
				return d.value
			})(data)

		d3.select('#' + id)
			.selectAll('.innerSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenInner)

		d3.select('#' + id)
			.selectAll('.topSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenTop)

		d3.select('#' + id)
			.selectAll('.outerSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenOuter)

		d3.select('#' + id)
			.selectAll('.percent')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('x', textTweenX)
			.attrTween('y', textTweenY)
			.text(getPercent)
	}
	Donut3D.drawInner = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.innerSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'innerSlice')
			.style('fill', function (d) {
				// return d3.hsl(d.data.color).darker(0.07)
				return d.data.color + fillAlpah
			})
			.style('stroke', function (d) {
				return d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieInner(d, rx + 0.5, ry + 0.5, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
	}

	Donut3D.drawTop = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.topSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'topSlice')
			.style('fill', function (d) {
				return d.data.color + fillAlpah
			})
			.style('stroke', function (d) {
				return d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieTop(d, rx, ry, ir)
			})
			.each(function (d) {
				this._current = d
			})
	}

	Donut3D.drawBottom = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.bottomSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'bottomSlice')
			.style('fill', function (d) {
				return d.data.color + '30'
			})
			.style('stroke', function (d) {
				return d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieBottom(d, rx, ry, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
	}
	Donut3D.drawOut = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.outerSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'outerSlice')
			.style('fill', function (d) {
				// return d3.hsl(d.data.color).darker(0.1)
				return d.data.color + fillAlpah
			})
			.style('stroke', function (d) {
				return d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieOuter(d, rx - 0.5, ry - 0.5, d.data.h)
			})
			.each(function (d) {
				this._current = d
			})
	}
	Donut3D.drawRight = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.rightSideSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'rightSideSlice')
			.style('fill', function (d) {
				if (d.data.color == 'transparent') return 'transparent'
				else return d.data.color + '20'
			})
			.style('stroke', function (d) {
				return d.data.color + '20'
			})
			.attr('d', function (d) {
				return pieRightSide(d, rx, ry, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
		// .attr('opacity', 0)
		// .transition()
		// .delay(function (d, i) {
		// 	// 延时显示,最终效果是一节一节动态显示
		// 	return i * 50
		// })
		// .duration(100)
		// .attr('opacity', 1)
	}
	Donut3D.drawLeft = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.leftSideSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'leftSideSlice')
			.style('fill', function (d) {
				if (d.data.color == 'transparent') return 'transparent'
				else return d.data.color + '20'
			})
			.style('stroke', function (d) {
				return d.data.color + '20'
			})
			.attr('d', function (d) {
				return pieLeftSide(d, rx, ry, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
		// .attr('opacity', 0)
		// .transition()
		// .delay(function (d, i) {
		// 	// 延时显示,最终效果是一节一节动态显示
		// 	// return i * 50
		// })
		// .duration(100)
		// .attr('opacity', 1)
	}
	Donut3D.draw = function (
		id,
		data,
		x /*center x*/,
		y /*center y*/,
		rx /*radius x*/,
		ry /*radius y*/,
		h /*height*/,
		ir /*inner radius*/,
		fillAlpah,
		strokeAlpha
	) {
		var _data = d3.layout
			.pie()
			.sort(null)
			.value(function (d) {
				return d.value
			})(data)

		var slices = d3
			.select('#' + id)
			.append('g')
			.attr('transform', 'translate(' + x + ',' + y + ')')
			.attr('class', 'slices')
		Donut3D.drawInner(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawBottom(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawTop(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawOut(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawRight(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawLeft(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)

		 

		// slices
		// 	.selectAll('.percent')
		// 	.data(_data)
		// 	.enter()
		// 	.append('text')
		// 	.attr('class', 'percent')
		// 	.attr('x', function (d) {
		// 		return 0.6 * rx * Math.cos(0.5 * (d.startAngle + d.endAngle))
		// 	})
		// 	.attr('y', function (d) {
		// 		return 0.6 * ry * Math.sin(0.5 * (d.startAngle + d.endAngle))
		// 	})
		// 	.text(getPercent)
		// 	.each(function (d) {
		// 		this._current = d
		// 	})
	}

	this.Donut3D = Donut3D
})()

成品

image.png

<!DOCTYPE html>
<meta charset="utf-8" />
<style>
	body {
		font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
		width: 960px;
		height: 500px;
		position: relative;
	}
	path.slice {
		stroke-width: 2px;
	}
	/* polyline {
		opacity: 0.3;
		stroke: black;
		stroke-width: 2px;
		fill: none;
	} */
	svg text.percent {
		fill: white;
		text-anchor: middle;
		font-size: 12px;
	}
</style>
<body>
	<!-- transform: rotateX(-147DEG); transform-origin: 100px 149px; -->

	<button onClick="changeData()">Change Data</button>
	<script src="https://d3js.org/d3.v3.min.js"></script>
	<script src="Donut3Drevers.js"></script>
	<script>
		var salesData = [
			{ label: '优质', color: '#3366CC', h: 40, value: 20 },
			{ label: '推荐', color: '#DC3912', h: 60, value: 30 },
			{ label: '观察', color: '#FF9500', h: 80, value: 40 },
			{ label: '疑难', color: '#109618', h: 100, value: 10 },
			// { label: 'Delux', color: '#990099' },
		]

		var width = 500
		var height = 300
		var svg = d3.select('body').append('svg').attr('width', 1200).attr('height', 1200)

		var xScale = d3.scale.linear().domain([0, 200]).range([0, 1000])

		//定义坐标轴
		var axiosTop = d3.svg
			.axis()
			.scale(xScale)
			.orient('top') //刻度方向向上
			// .ticks(12)
			.tickSize(1)

		var gAxis = svg
			.append('g')
			.attr('transform', 'translate(0,30)') //平移到(0,30)
			.attr('class', 'axis')
		//在gAxis中绘制坐标轴
		axiosTop(gAxis)

		//==========
		// svg
		// 	.append('ellipse')
		// 	.attr('cx', 600)
		// 	.attr('cy', 0)
		// 	.attr('rx', width / 2 - 20)
		// 	.attr('ry', height / 4)
		// 	.attr('class', 'circle_line')
		// 	.attr('transform', 'translate(' + width / 2 + ',' + (height * 1.2) / 2 + ')')
		// svg
		// 	.append('ellipse')
		// 	.attr('cx', 600)
		// 	.attr('cy', 0)
		// 	.attr('rx', width / 8)
		// 	.attr('ry', height / 13)
		// 	.attr('class', 'circle_line')
		// 	.attr('transform', 'translate(' + width / 2 + ',' + (height / 2 + 5) + ')')

		// svg
		// 	.append('ellipse')
		// 	.attr('cx', 600)
		// 	.attr('cy', 0)
		// 	.attr('rx', width / 15)
		// 	.attr('ry', height / 22)
		// 	.attr('class', 'circle_line')
		// 	.attr('transform', 'translate(' + width / 2 + ',' + (height / 2 + 5) + ')')
		// svg
		// 	.append('ellipse')
		// 	.attr('cx', 600)
		// 	.attr('cy', 0)
		// 	.attr('rx', width / 25)
		// 	.attr('ry', height / 40)
		// 	.attr('fill', '#2D3C41') // 实心椭圆:填充颜色
		// 	.attr('transform', 'translate(' + width / 2 + ',' + (height / 2 + 5) + ')')

		svg.append('g').attr('id', 'salesDonut')
		// svg.append('g').attr('id', 'quotesDonut')

		Donut3D.draw('salesDonut', randomData(), 290, 150, 200, 100, 30, 0.6, 'c1', '31')

		// Donut3D.draw('quotesDonut', randomData(), 450, 150, 130, 100, 30, 0)
		var h = 40
		function changeData() {
			Donut3D.transition('salesDonut', randomData(), 130, 100, h++, 0.6)
			// Donut3D.transition('quotesDonut', randomData(), 130, 100, 30, 0)
		}

		function randomData() {
			return salesData.map(function (d) {
				return { label: d.label, value: 1000 * d.value * Math.random(), color: d.color, h: d.h }
			})
		}
	</script>
</body>

<style>
	.circle_line {
		border: 2px solid #3f51b5;
	}
	.domain {
	}
</style>

!(function () {
	var Donut3D = {}
	// 最大高度,实际场景中,可以自己计算
	var maxHeight = 100
	var withStroke = false
	function pieTop(d, rx, ry, h, ir) {
		if (d.endAngle - d.startAngle == 0) return 'M 0 0'
		var sx = rx * Math.cos(d.startAngle),
			sy = ry * Math.sin(d.startAngle),
			ex = rx * Math.cos(d.endAngle),
			ey = ry * Math.sin(d.endAngle)

		var deltaY = maxHeight - h
		var ret = []
		ret.push(
			'M',
			sx,
			sy + deltaY,
			'A',
			rx,
			ry,
			'0',
			d.endAngle - d.startAngle > Math.PI ? 1 : 0,
			'1',
			ex,
			ey + deltaY,
			'L',
			ir * ex,
			ir * ey + deltaY
		)
		ret.push(
			'A',
			ir * rx,
			ir * ry,
			'0',
			d.endAngle - d.startAngle > Math.PI ? 1 : 0,
			'0',
			ir * sx,
			ir * sy + deltaY,
			'z'
		)
		return ret.join(' ')
	}
	function pieBottom(d, rx, ry, h, ir) {
		if (d.endAngle - d.startAngle == 0) return 'M 0 0'
		var sx = rx * Math.cos(d.startAngle),
			sy = ry * Math.sin(d.startAngle),
			ex = rx * Math.cos(d.endAngle),
			ey = ry * Math.sin(d.endAngle)
		var deltaY = maxHeight - h
		var ret = []
		ret.push(
			'M',
			sx,
			sy + h + deltaY,
			'A',
			rx,
			ry,
			'0',
			d.endAngle - d.startAngle > Math.PI ? 1 : 0,
			'1',
			ex,
			ey + h + deltaY,
			'L',
			ir * ex,
			ir * ey + h + deltaY
		)
		ret.push(
			'A',
			ir * rx,
			ir * ry,
			'0',
			d.endAngle - d.startAngle > Math.PI ? 1 : 0,
			'0',
			ir * sx,
			ir * sy + h + deltaY,
			'z'
		)
		return ret.join(' ')
	}

	function pieOuter(d, rx, ry, h) {
		var startAngle = d.startAngle > Math.PI ? Math.PI : d.startAngle
		var endAngle = d.endAngle > Math.PI ? Math.PI : d.endAngle

		var sx = rx * Math.cos(startAngle),
			sy = ry * Math.sin(startAngle),
			ex = rx * Math.cos(endAngle),
			ey = ry * Math.sin(endAngle)
		var deltaY = maxHeight - h
		var ret = []
		ret.push(
			'M',
			sx,
			h + sy + deltaY,
			'A',
			rx,
			ry,
			'0 0 1',
			ex,
			h + ey + deltaY,
			'L',
			ex,
			ey + deltaY,
			'A',
			rx,
			ry,
			'0 0 0',
			sx,
			sy + deltaY,
			'z'
		)
		return ret.join(' ')
	}

	//	return pieInner(d, rx + 0.5, ry + 0.5, d.data.h, ir)
	function pieInner(d, rx, ry, h, ir) {
		var startAngle = d.startAngle < Math.PI ? Math.PI : d.startAngle
		var endAngle = d.endAngle < Math.PI ? Math.PI : d.endAngle

		var sx = ir * rx * Math.cos(startAngle),
			sy = ir * ry * Math.sin(startAngle),
			ex = ir * rx * Math.cos(endAngle),
			ey = ir * ry * Math.sin(endAngle)
		var deltaY = maxHeight - h
		var ret = []
		// ret.push('M', sx, sy, 'L', sx + 30, sy + 30)
		ret.push(
			'M',
			sx,
			sy + deltaY,
			'A',
			ir * rx,
			ir * ry,
			'0 0 1',
			ex,
			ey + deltaY,
			'L',
			ex,
			h + ey + deltaY,
			'A',
			ir * rx,
			ir * ry,
			'0 0 0',
			sx,
			h + sy + deltaY,
			'z'
		)
		return ret.join(' ')
	}

	// 左侧面
	/*
  a ←   b
  ↓     ↑  (S)
  a' →  b'
    (E)
  */

	function pieLeftSide(d, rx, ry, h, ir) {
		var startAngle = d.startAngle
		var endAngle = d.endAngle

		var //sx = rx * Math.cos(startAngle),
			//sy = ry * Math.sin(startAngle),
			ex = rx * Math.cos(endAngle)
		var ey = ry * Math.sin(endAngle)
		var deltaY = maxHeight - h
		var ret = []
		if (d.data.leftHeight) {
			ret.push(
				'M',
				ir * ex,
				// ir * ey,
				d.data.leftHeight + ir * ey + deltaY,
				'L',
				ir * ex,
				ir * ey + deltaY,
				'L',
				ex,
				ey + deltaY,
				'L',
				ex,
				// ey,
				d.data.leftHeight + ey + deltaY,
				'z'
			)
		} else {
			ret.push(
				'M',
				ir * ex,
				h + ir * ey + deltaY,
				'L',
				ir * ex,
				ir * ey + deltaY,
				'L',
				ex,
				ey + deltaY,
				'L',
				ex,
				h + ey + deltaY,
				'z'
			)
		}
		return ret.join(' ')
	}

	// 右侧面
	//↑↓← →
	/*
  a ←   b
  ↓     ↑  (S)
  a' →  b'
    (E)
  */

	function pieRightSide(d, rx, ry, h, ir) {
		var startAngle = d.startAngle
		var endAngle = d.endAngle
		var deltaY = maxHeight - h
		var sx = rx * Math.cos(startAngle)
		var sy = ry * Math.sin(startAngle)
		// ex = rx * Math.cos(endAngle),
		// ey = ry * Math.sin(endAngle)

		var ret = []
		if (!d.data.index) return ''
		if (d.data.rightHeight) {
			ret.push(
				'M',
				sx,
				d.data.rightHeight + sy + deltaY,
				'L',
				sx,
				sy + deltaY,
				'L',
				ir * sx,
				ir * sy + deltaY,
				'L',
				ir * sx,
				d.data.rightHeight + ir * sy + deltaY,
				'z'
			)
		} else {
			ret.push(
				'M',
				sx,
				h + sy + deltaY,
				'L',
				sx,
				sy + deltaY,
				'L',
				ir * sx,
				ir * sy + deltaY,
				'L',
				ir * sx,
				h + ir * sy + deltaY,
				'z'
			)
		}
		return ret.join(' ')
	}

	function getPercent(d) {
		return d.endAngle - d.startAngle > 0.2
			? Math.round((1000 * (d.endAngle - d.startAngle)) / (Math.PI * 2)) / 10 + '%'
			: ''
	}
	Donut3D.setMaxHeight = function (height) {
		maxHeight = height
	}
	Donut3D.transition = function (id, data, rx, ry, h, ir) {
		function arcTweenTop(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieTop(i(t), rx, ry, a.data.h, ir)
			}
		}
		//ccs
		function arcTweenBottom(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieBottom(i(t), rx + 0.5, ry + 0.5, a.data.h, ir)
			}
		}

		function arcTweenOuter(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieOuter(i(t), rx - 0.5, ry - 0.5, a.data.h)
			}
		}

		function arcTweenInner(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieInner(i(t), rx + 0.5, ry + 0.5, a.data.h, ir)
			}
		}
		function arcTweenLeft(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieLeftSide(i(t), rx + 0.5, ry + 0.5, a.data.h, ir)
			}
		}
		function arcTweenRight(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return pieRightSide(i(t), rx + 0.5, ry + 0.5, a.data.h, ir)
			}
		}
		function textTweenX(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return 0.6 * rx * Math.cos(0.5 * (i(t).startAngle + i(t).endAngle))
			}
		}
		function textTweenY(a) {
			var i = d3.interpolate(this._current, a)
			this._current = i(0)
			return function (t) {
				return 0.6 * rx * Math.sin(0.5 * (i(t).startAngle + i(t).endAngle))
			}
		}

		var _data = d3.layout
			.pie()
			.sort(null)
			.value(function (d) {
				return d.value
			})(data)

		d3.select('#' + id)
			.selectAll('.innerSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenInner)

		d3.select('#' + id)
			.selectAll('.topSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenTop)

		d3.select('#' + id)
			.selectAll('.bottomSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenBottom)

		d3.select('#' + id)
			.selectAll('.outerSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenOuter)

		d3.select('#' + id)
			.selectAll('.leftSideSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenLeft)
		d3.select('#' + id)
			.selectAll('.rightSideSlice')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('d', arcTweenRight)

		d3.select('#' + id)
			.selectAll('.percent')
			.data(_data)
			.transition()
			.duration(750)
			.attrTween('x', textTweenX)
			.attrTween('y', textTweenY)
			.text(getPercent)
	}
	Donut3D.drawInner = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.innerSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'innerSlice')
			.style('fill', function (d) {
				// return d3.hsl(d.data.color).darker(0.07)
				return d.data.color + fillAlpah
			})
			.style('stroke', function (d) {
				return withStroke && d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieInner(d, rx + 0.5, ry + 0.5, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
	}

	Donut3D.drawTop = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.topSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'topSlice')
			.style('fill', function (d) {
				return d.data.color + fillAlpah
			})
			.style('stroke', function (d) {
				return withStroke && d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieTop(d, rx, ry, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
	}

	Donut3D.drawBottom = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.bottomSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'bottomSlice')
			.style('fill', function (d) {
				return d.data.color + '30'
			})
			.style('stroke', function (d) {
				return withStroke && d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieBottom(d, rx, ry, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
	}
	Donut3D.drawOut = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.outerSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'outerSlice')
			.style('fill', function (d) {
				// return d3.hsl(d.data.color).darker(0.1)
				return d.data.color + fillAlpah
			})
			.style('stroke', function (d) {
				return withStroke && d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieOuter(d, rx - 0.5, ry - 0.5, d.data.h)
			})
			.each(function (d) {
				this._current = d
			})
	}
	Donut3D.drawRight = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.rightSideSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'rightSideSlice')
			.style('fill', function (d) {
				if (d.data.color == 'transparent') return 'transparent'
				// else return d.data.color + '60'
				else {
					let def = d.data.color + '60'
					if (d.data.rightHeight) return d.data.color + 'ee'
					return def
				}
			})
			.style('stroke', function (d) {
				return withStroke && d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieRightSide(d, rx, ry, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
		// .attr('opacity', 0)
		// .transition()
		// .delay(function (d, i) {
		// 	// 延时显示,最终效果是一节一节动态显示
		// 	return i * 50
		// })
		// .duration(100)
		// .attr('opacity', 1)
	}
	Donut3D.drawLeft = function (slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha) {
		slices
			.selectAll('.leftSideSlice')
			.data(_data)
			.enter()
			.append('path')
			.attr('class', 'leftSideSlice')
			.style('fill', function (d) {
				if (d.data.color == 'transparent') return 'transparent'
				else {
					let def = d.data.color + '60'
					if (d.data.leftHeight) return d.data.color + 'e1'
					return def
				}
			})
			.style('stroke', function (d) {
				return withStroke && d.data.color + strokeAlpha
			})
			.attr('d', function (d) {
				return pieLeftSide(d, rx, ry, d.data.h, ir)
			})
			.each(function (d) {
				this._current = d
			})
		// .attr('opacity', 0)
		// .transition()
		// .delay(function (d, i) {
		// 	// 延时显示,最终效果是一节一节动态显示
		// 	// return i * 50
		// })
		// .duration(100)
		// .attr('opacity', 1)
	}
	function setItemDelta(data, index) {
		let item = data[index]
		let myHeight = item.data.h
		//右侧修补
		if (index != data.length - 1) {
			let nextHeight = data[index + 1].data.h
			item.data.rightBottom = nextHeight
			item.data.rightHeight = nextHeight - myHeight
		}
		if (index == data.length - 1) {
			// 左侧修补

			let endHeight = data[0].data.h
			item.data.leftHeight = myHeight - endHeight
			item.data.leftBottom = endHeight
		}
		item.data.index = index
	}
	Donut3D.draw = function (
		id,
		data,
		x /*center x*/,
		y /*center y*/,
		rx /*radius x*/,
		ry /*radius y*/,
		h /*height*/,
		ir /*inner radius*/,
		fillAlpah,
		strokeAlpha
	) {
		var _data = d3.layout
			.pie()
			.sort(null)
			.value(function (d) {
				return d.value
			})(data)

		_data.map(function (item, index) {
			setItemDelta(_data, index)
		})

		console.log('after setitem delta', _data)
		var slices = d3
			.select('#' + id)
			.append('g')
			.attr('transform', 'translate(' + x + ',' + y + ')')
			.attr('class', 'slices')

		Donut3D.drawLeft(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawRight(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawInner(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawBottom(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawTop(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)
		Donut3D.drawOut(slices, _data, rx, ry, h, ir, fillAlpah, strokeAlpha)

		slices
		// 	.selectAll('.percent')
		// 	.data(_data)
		// 	.enter()
		// 	.append('text')
		// 	.attr('class', 'percent')
		// 	.attr('x', function (d) {
		// 		return 0.6 * rx * Math.cos(0.5 * (d.startAngle + d.endAngle))
		// 	})
		// 	.attr('y', function (d) {
		// 		return 0.6 * ry * Math.sin(0.5 * (d.startAngle + d.endAngle))
		// 	})
		// 	.text(getPercent)
		// 	.each(function (d) {
		// 		this._current = d
		// 	})
	}

	this.Donut3D = Donut3D
})()