最先上1下实际效果图:
上下的箭头和正下方的标明和环状图自身都可以以操纵环状图的选定情况。
最先讲1下思路:
合理布局很简易,我就不写了,关键讲下作图全过程。
最先跟图要求能够了解,作出这样的实际效果必须1组目标,每一个目标有色调,所占有率例,姓名等,也便是这样:
let chartData=[ {color:"#FD7A4F",title:"别的",percent:0.2}, {color:"#FDD764",title:"工程建筑/土木匠程",percent:0.25}, *** ]
留意百分比加在1起务必是100%,也便是1,不然圆环将会不容易画满,或多处1一部分。
依据每一部分所占有率例测算出每一个一部分所占的弧度,应用ctx.arc(x0, y0,r, startAngle, endAngle);画出圆弧,当今项必须向外偏位1些,全过程中实际上编码讲这一部分:
最先界定1个multiCircleChart类,
//ES6写法 class multiCircleChart { constructor(id, chartDatas, defalutIndex,callback) { /*结构涵数: 传入的主要参数ID,canvas的id,用于置放美术绘画內容; chartDatas:画图所需主要参数数据信息; defalutIndex:defalutIndex:当今选定项 callback:点一下环状图的回调函数涵数 */ this.canvas = document.getElementById(id); this.size = this.canvas.parentNode.clientWidth * 4; this.canvas.style.width = this.size / 4 + "px"; this.canvas.style.height = this.size / 4 + "px"; this.canvas.width = this.size; this.canvas.height = this.size; /* 由于在挪动端画图必须多倍图,这样图象会很清楚,因此这里size,也便是canvas的context设定为canvas尺寸的4倍; 留意:!!!canvas.width指的是画布內容(context)的尺寸,cavas.style.width是canvas在网页页面上显示信息的尺寸,也便是说,简直的照片是显示信息照片尺寸的4倍 */ this.ctx = this.canvas.getContext("2d"); this.defalutIndex = defalutIndex;//当今选定项 this.chartDatas = chartDatas;//绘图所需数据信息 this.lineWidth = this.size/5;//环状图的圆环宽度,设定为canvas宽度的1/5; this.startAngle = -0.5;//环状图起止角度,这里为-0.5,测算时也便是-0.5*Math.PI,放在座标系中也便是环状图最高点那个部位的角度;顺带说1下,右边为0,正下方为0.5,左边为1 this.callback=callback; this.canvas.addEventListener('click',this.mouseDownEvent.bind(this)); /*给canvas加上监视涵数,并将恶性事件传送以往,用于测算点一下部位在哪儿个数据信息项里*/ this.AngleList=[];//纪录每项的完毕角度,融合监视恶性事件,测算点一下恶性事件的部位在哪儿个数据信息项里 } }
结构涵数写好了,接下来必须画环状图了:
class multiCircleChart { *** draw() { this.ctx.clearRect(0,0,this.size,this.size);//每次美术绘画前,先清空1下画布,防止画布被污染 if (this.chartDatas.length == 0) return;//假如传入的主要参数长度为0的话,也就不必须再次画了 this.ctx.lineWidth = this.lineWidth;//为圆环宽度取值 let startAngle = Math.PI * -0.5;//设定起止角度 let endAngle = startAngle;设定完毕角度 this.AngleList=[]; /*下面就刚开始动笔划图了*/ this.chartDatas.map((item, i) => { this.ctx.beginPath();//刚开始画图指令,防止粘连 this.ctx.strokeStyle = item.color;//设定边框色调,由于大家画的是圆环,因此填充色不必须,要是有边框色就可以了 if (i > 0) { //从第2项刚开始(i==1)时,起止角度便是上1次的完毕角度 startAngle = endAngle; } endAngle = startAngle + item.percent * Math.PI * 2;//测算当今项的完毕角度,依据所占的百分比测算所占角度(item.percent * Math.PI * 2),再融合起止角度便可以测算出真实的偏位角度了(startAngle + item.percent * Math.PI * 2) this.AngleList.push(endAngle); //选定当今项,必须向外偏位 if (i == this.defalutIndex) { /* 选定当今项,必须向外偏位 应用起止角度和完毕角度的正中间值来即使偏位部位 */ let centerAngle=(startAngle+endAngle)/2; let x=this.lineWidth*0.2*Math.cos(centerAngle);//x轴偏位量 let y=this.lineWidth*0.2*Math.sin(centerAngle);//y轴偏位量 //未选定项的圆心部位是(this.size / 2, this.size / 2),选定的必须偏位,圆心是(this.size / 2+x, this.size / 2+y);这样画出的环状就会向外偏位环状宽度的1/5了; this.ctx.arc(this.size / 2+x, this.size / 2+y, this.size / 2 - this.lineWidth / 2 - this.lineWidth * 0.2, startAngle, endAngle); } else { this.ctx.arc(this.size / 2, this.size / 2, this.size / 2 - this.lineWidth / 2 - this.lineWidth * 0.2, startAngle, endAngle); } this.ctx.stroke(); }); } }
如今所画的图是1个静态数据的,点一下环状图是不容易有任何转变的,自然如今这样也是能够用的
let circlePeiChart = new multiCircleChart("circle-pei-chart",chartDatas,defalutIndex,);//new 1个 circlePeiChart.draw();//画图
外界切换选定项
circlePeiChart.defalutIndex=2;//改动选定项Index值 circlePeiChart.draw();//重绘
那末如何点一下canvas切换当今选项呢,思路很简易:以canvas管理中心为圆心,监测点一下部位,点一下部位与圆心连成1线,以直角座标系为参考,测算出点一下部位的弧度,跟angleList做较为,测算出点一下的是第几项,随后改动defalutIndex,重绘canvas.
class multiCircleChart { *** *** mouseDownEvent(e){ const [x1,y1]=[e.offsetX,e.offsetY];//点一下恶性事件部位 const [x0,y0]=[this.size/2/4,this.size/2/4];//原点部位;留意:原点部位为canvas管理中心,并不是context管理中心 let angle=0; if(x1>x0){ //点一下部位在第1象限(y1>y0)或第2象限(y1<y0) y1<y0?angle=-0.5*Math.PI+Math.atan((x1-x0)/(y0-y1)):angle=Math.atan((y1-y0)/(x1-x0)); }else{ //点一下部位在第3象限(y1<y0)或第4象限(y1>y0) y1<y0?angle=Math.PI+Math.atan((y0-y1)/(x0-x1)):angle=Math.atan((x0-x1)/(y1-y0))+Math.PI*0.5; } for(let i=0;i<this.AngleList.length;i++){//测算角度落在第几项 if(angle<this.AngleList[i]){ //当点一下部位角度值第1次超过某1项时,也便是说点一下部位就在这1项上 this.defalutIndex=i;//再次取值defaultIndex this.draw();//重绘canvas this.callback?this.callback(i):'';//假如有回调函数涵数,则启用摧毁涵数 break;//跳出来循环系统,完毕实际操作; } } } }
最终把总体编码贴上吧
//html <canvas id="circle-pei-chart"></canvas>
//启用 let chartDatas=[ {color: "rgb(253, 122, 79)",title: "后端开发开发设计",percent: 0.2}, **]; let defalutIndex=0 let circlePeiChart = new multiCircleChart("circle-pei-chart",chartDatas, defalutIndex,(i)=>{defalutIndex=i}); circlePeiChart.draw();
//重绘 circlePeiChart.defaultIndex=2; circlePeiChart.draw();
/* chartDatas [ {color: "rgb(253, 122, 79)",title: "后端开发开发设计",percent: 0.2}, **]; */ class multiCircleChart { constructor(id, chartDatas, defalutIndex,callback) { this.canvas = document.getElementById(id); this.size = this.canvas.parentNode.clientWidth * 4; this.canvas.style.width = this.size / 4 + "px"; this.canvas.style.height = this.size / 4 + "px"; this.canvas.width = this.size; this.canvas.height = this.size; this.ctx = this.canvas.getContext("2d"); this.defalutIndex = defalutIndex; this.chartDatas = chartDatas; this.lineWidth = this.size/5; this.startAngle = -0.5; this.callback=callback; this.canvas.addEventListener('click',this.mouseDownEvent.bind(this)); this.AngleList=[]; } draw() { this.ctx.clearRect(0,0,this.size,this.size); if (this.chartDatas.length == 0) return; this.ctx.lineWidth = this.lineWidth; this.ctx.lineCap="butt"; let startAngle = Math.PI * -0.5; let endAngle = startAngle; this.AngleList=[]; this.chartDatas.map((item, i) => { this.ctx.beginPath(); this.ctx.strokeStyle = item.color; if (i > 0) { startAngle = endAngle; } endAngle = startAngle + item.percent * Math.PI * 2; this.AngleList.push(endAngle); //选定当今项,必须向外偏位 if (i == this.defalutIndex) { //选定当今项,必须向外偏位 let centerAngle=(startAngle+endAngle)/2; let x=this.lineWidth*0.2*Math.cos(centerAngle); let y=this.lineWidth*0.2*Math.sin(centerAngle); this.ctx.arc(this.size / 2+x, this.size / 2+y, this.size / 2 - this.lineWidth / 2 - this.lineWidth * 0.2, startAngle, endAngle); } else { this.ctx.arc(this.size / 2, this.size / 2, this.size / 2 - this.lineWidth / 2 - this.lineWidth * 0.2, startAngle, endAngle); } this.ctx.stroke(); }); } mouseDownEvent(e){ const [x1,y1]=[e.offsetX,e.offsetY]; const [x0,y0]=[this.size/2/4,this.size/2/4]; let angle=0; if(x1>x0){ y1<y0?angle=-0.5*Math.PI+Math.atan((x1-x0)/(y0-y1)):angle=Math.atan((y1-y0)/(x1-x0)); }else{ y1<y0?angle=Math.PI+Math.atan((y0-y1)/(x0-x1)):angle=Math.atan((x0-x1)/(y1-y0))+Math.PI*0.5; } for(let i=0;i<this.AngleList.length;i++){ if(angle<this.AngleList[i]){ this.defalutIndex=i; this.draw(); this.callback?this.callback(i):''; break; } } } }
以上便是本文的所有內容,期待对大伙儿的学习培训有一定的协助,也期待大伙儿多多适用脚本制作之家。