canvas画环形 pie


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <canvas width="300" height="200" id="xx" style="border: 1px solid #ccc"></canvas>
    <script>
        class PieChart {
            constructor(obj) {
                console.log(obj.id,'===>obj',document.getElementById(obj.id))
                // 获取画布
                this.ctx = document.getElementById(obj.id)?.getContext("2d");
                //获取画布的中心
                this.x0 = this.ctx.canvas.width / 2;
                this.y0 = this.ctx.canvas.height / 2;
                //外圆半径
                this.radius = this.x0 > this.y0 ? 0.5 * this.y0 : 0.5 * this.x0;
                //内圆半径
                this.inr=0.8*this.radius;
                //标题伸出去的距离
                this.outLine = 20;
                // 字体大小
                this.font=12;
                // pie数据
                this.pieData = obj.pieData || [];
                // 动画定时器
                this.timer=null;
                // 速率
                this.speed=0.1;
                // 时间
                this.rate=100;
                // 圆点半径
                this.circle=4;
            }
            //添加饼图
            drawPie() {
                //1、将数据转为弧度
                var angleList = this.drawAngle();
                let speed = this.speed;

                this.timer = setInterval(() => {
                    if (speed >= 1) {
                        clearInterval(this.timer)
                        return
                    }
                    let aniAngleList= JSON.parse(JSON.stringify(angleList)).map((item) => {
                        item.angle = item.angle * speed
                        return item
                    })
                    var start = 0;//起始弧度
                    aniAngleList.forEach(function (item, i) {
                        var end = item.angle + start;
                        this.ctx.beginPath();
                        this.ctx.moveTo(this.x0, this.y0);
                        this.ctx.fillStyle = item.color;

                        this.ctx.arc(this.x0, this.y0, this.radius, start, end);
                        this.ctx.fill()

                        if (speed >= 0.9) {
                          
                            //调用标的方法
                            this.drawTitle(start, item, this.ctx.fillStyle);
                        }

                        this.ctx.beginPath();
                        this.ctx.moveTo(this.x0, this.y0);
                        this.ctx.fillStyle = '#fff';

                        this.ctx.arc(this.x0, this.y0, this.inr, 0, Math.PI * 2);
                        this.ctx.fill()
                        //记录上一个结束的弧度
                        start = end;
                    }.bind(this));
                    speed += 0.1

                }, this.rate)

            }
            //添加转化弧度的方法
            drawAngle() {
                //先获取数据的总和
                var total = 0;
                this.pieData.forEach((item, index)=> {
                    total += item.num;
                });
                //获取每条数据的弧度
                this.pieData.forEach(function (item, index) {
                    var angle = item.num / total * Math.PI * 2;
                    item.angle = angle;
                });
                return this.pieData;
            }
            //添加标题
            drawTitle(start, item, color) {
                
                this.ctx.beginPath();
                //斜边的长度
                var edge = this.radius + this.outLine;
                // 起点坐标
                //x轴方向的直角边
                var edgeX0 = this.radius * Math.cos(start + item.angle / 2);
                //y轴方向的直角边
                var edgeY0 = this.radius * Math.sin(start + item.angle / 2);

                //x轴方向的直角边
                var edgeX = edge * Math.cos(start + item.angle / 2);
                //y轴方向的直角边
                var edgeY = edge * Math.sin(start + item.angle / 2);

                //伸出去的横纵坐标
                var outX = this.x0 + edgeX;
                var outY = this.y0 + edgeY;
                this.ctx.beginPath();
                // this.ctx.moveTo(this.x0 * 1.2, this.y0 * 1.2);
                this.ctx.moveTo(this.x0 + edgeX0, this.y0 + edgeY0);
                this.ctx.lineTo(outX, outY);
                // this.ctx.strokeStyle = color;
                this.ctx.strokeStyle = '#ccc';
                this.ctx.stroke();

                //添加字体
                var align = outX > this.x0 ? "left" : "right";
                this.ctx.font = this.font+"px 微软雅黑";
                this.ctx.textAlign = align;
                this.ctx.textBaseline = "bottom";
                // this.ctx.fillStyle = color;
                this.ctx.fillStyle = '#ccc';

                let outX1 = outX > this.x0 ? outX + 1.5*this.font : outX - 1.5*this.font;


                this.ctx.fillText(item.title, outX1, outY);
                this.ctx.fillText(item.title, outX1, outY - this.font);
                this.ctx.beginPath();
                // 字前面的圆点
                let dir = this.ctx.measureText(item.title).width
                let outX2 = outX > this.x0 ? outX + 1.5*this.font - (this.font/2).toFixed(1) : outX - 1.5*this.font - (this.font/2).toFixed(1) - dir;
                this.ctx.arc(outX2, outY - this.font/2-this.circle/4, this.circle, 0, 2 * Math.PI);
                this.ctx.fillStyle = color;
                this.ctx.fill();
                // this.ctx.restore(); // 还原状态

                //添加文字下面的线
                var textW = this.ctx.measureText(item.title).width + this.font*2;
                this.ctx.moveTo(outX, outY);
                outX = outX > this.x0 ? outX + textW : outX - textW;
                this.ctx.lineTo(outX, outY);
                this.ctx.stroke();
            }



        }

        let pieChart = new PieChart({
            id:'xx',
            pieData: [
                { title: "20岁以下", num: 1,color:'#306ABD' },
                { title: "20-25岁", num: 5,color:'#8272EE' },
                { title: "25-30岁", num: 4,color:'#306ABD' },
                { title: "30岁以上", num: 5 ,color:'#ED8661'},
                { title: "30岁以上", num: 5 ,color:'#F9D852'},
            ]
        })
        pieChart.drawPie()
    </script>
<!-- Code injected by live-server -->
<script>
	// <![CDATA[  <-- For SVG support
	if ('WebSocket' in window) {
		(function () {
			function refreshCSS() {
				var sheets = [].slice.call(document.getElementsByTagName("link"));
				var head = document.getElementsByTagName("head")[0];
				for (var i = 0; i < sheets.length; ++i) {
					var elem = sheets[i];
					var parent = elem.parentElement || head;
					parent.removeChild(elem);
					var rel = elem.rel;
					if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
						var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
						elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
					}
					parent.appendChild(elem);
				}
			}
			var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';
			var address = protocol + window.location.host + window.location.pathname + '/ws';
			var socket = new WebSocket(address);
			socket.onmessage = function (msg) {
				if (msg.data == 'reload') window.location.reload();
				else if (msg.data == 'refreshcss') refreshCSS();
			};
			if (sessionStorage && !sessionStorage.getItem('IsThisFirstTime_Log_From_LiveServer')) {
				console.log('Live reload enabled.');
				sessionStorage.setItem('IsThisFirstTime_Log_From_LiveServer', true);
			}
		})();
	}
	else {
		console.error('Upgrade your browser. This Browser is NOT supported WebSocket for Live-Reloading.');
	}
	// ]]>
</script>
</body>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

web修理工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值