>[success] # 绘制圆弧 ![](https://box.kancloud.cn/55f03ffe0f25f78101e070c1243c6ccf_468x406.png) ~~~ 1.圆360° 也就是2π 弧度长 2.从右面为起点顺时针转动结尾是2π 3.绘制一个弧需要 确定圆心 坐标 --- x y 确定圆半径 --- r 确定起始绘制的位置和结束绘制的位置 确定弧的长度和位置 startAngle endAngle 弧度 取得绘制的方向 direction 默认是顺时针 false 逆时针 true ~~~ >[danger] ##### 绘制一个弧 ~~~ 1.半径长度为150,逆时针的圆 ~~~ ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*绘制圆弧*/ /*确定圆心 坐标 x y*/ /*确定圆半径 r */ /*确定起始绘制的位置和结束绘制的位置 确定弧的长度和位置 startAngle endAngle 弧度*/ /*取得绘制的方向 direction 默认是顺时针 false 逆时针 true */ /*在中心位置画一个半径150px的圆弧左下角*/ var w = ctx.canvas.width; var h = ctx.canvas.height; ctx.arc(w/2,h/2,150,Math.PI/2,Math.PI,true); ctx.stroke(); </script> </body> </html> ~~~ >[danger] ##### 绘制一个圆弧 ~~~ 1.为了能让圆弧闭合 ctx.moveTo(w/2,h/2); 从圆心位置开始 ~~~ ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*在中心位置画一个半径150px的圆弧右上角 扇形 边 填充 */ var w = ctx.canvas.width; var h = ctx.canvas.height; /*把起点放到圆心位置*/ ctx.moveTo(w/2,h/2); ctx.arc(w/2,h/2,150,0,-Math.PI/2,true); /*闭合路径*/ //ctx.closePath(); ctx.fill(); </script> </body> </html> ~~~ >[success] # 圆弧分割6等分 ~~~ 1. 要画多个扇形弧度 定义个数 定义可以分成多少个弧度 2. 画弧度通过循环实现,开始的边等i* 平均的弧度,结束i* 平均弧度+1 ~~~ >[danger] ##### 代码 ![](https://box.kancloud.cn/876489d6da0fe974234f9c1da5bab4a0_542x377.png) ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); var w = ctx.canvas.width; var h = ctx.canvas.height; /*分成几等分*/ var num = 6; /*一份多少弧度*/ var angle = Math.PI * 2 / num; /*原点坐标*/ var x0 = w / 2; var y0 = h / 2; /*获取随机颜色*/ var getRandomColor = function () { var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return 'rgb(' + r + ',' + g + ',' + b + ')'; } /*上一次绘制的结束弧度等于当前次的起始弧度*/ //var startAngle = 0; for (var i = 0; i < num; i++) { var startAngle = i * angle; var endAngle = (i + 1) * angle; ctx.beginPath(); ctx.moveTo(x0, y0); ctx.arc(x0, y0, 150, startAngle, endAngle); /*随机颜色*/ ctx.fillStyle = getRandomColor(); ctx.fill(); } </script> </body> </html> ~~~ >[success] # 计算年龄占比 ![](https://box.kancloud.cn/10af48c3dcad195608efcfdc35cbb712_475x386.png) ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*1.绘制年龄占比*/ /*2.准备统计的数据*/ /*15-20岁 6个*/ /*20-25岁 30个*/ /*25-30岁 10个*/ /*30-35岁 8个*/ var data = [6, 30, 10, 8]; /*3.在饼图表示出来*/ /*4.需要把数据转出弧度*/ var angleList = []; var total = 0; // 求出总共多少 data.forEach(function (item, i) { total += item; }); console.log(total); /*第二是转换成弧度的时候就可以去绘制扇形 减少一次遍历*/ data.forEach(function (item, i) { // 算出弧度大小 var angle = Math.PI * 2 * (item/total); angleList.push(angle); }); console.log(angleList); /*5.根据弧度绘制扇形*/ var w = ctx.canvas.width; var h = ctx.canvas.height; var x0 = w/2; var y0 = h/2; /*获取随机颜色*/ var getRandomColor = function () { var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return 'rgb(' + r + ',' + g + ',' + b + ')'; } var startAngle = 0; angleList.forEach(function (item,i) { /*上一次绘制的结束弧度等于当前次的起始弧度*/ var endAngle = startAngle + item; ctx.beginPath(); ctx.moveTo(x0,y0); ctx.arc(x0,y0,150,startAngle,endAngle); ctx.fillStyle = getRandomColor(); ctx.fill(); /*记录当前的结束位置作为下一次的起始位置*/ startAngle = endAngle; }); </script> </body> </html> ~~~ >[success] # 做一个完整饼状图 ![](https://box.kancloud.cn/2d12280f9e79cd35ca176327bd62141a_679x419.png) ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; display: block; margin: 100px auto; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> /*var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d');*/ /*1.绘制饼状态图*/ /*1.1 根据数据绘制一个饼图*/ /*1.2 绘制标题 从扇形的弧中心伸出一条线在画一条横线在横线的上面写上文字标题*/ /*1.3 在画布的左上角 绘制说明 一个和扇形一样颜色的矩形 旁边就是文字说明*/ var PieChart = function (ctx) { /*绘制工具*/ this.ctx = ctx || document.querySelector('canvas').getContext('2d'); /*绘制饼图的中心*/ this.w = this.ctx.canvas.width; this.h = this.ctx.canvas.height; /*圆心*/ this.x0 = this.w / 2 + 60; this.y0 = this.h / 2; /*半径*/ this.radius = 150; /*伸出去的线的长度*/ this.outLine = 20; /*说明的矩形大小*/ this.rectW = 30; this.rectH = 16; this.space = 20; } PieChart.prototype.init = function (data) { /*1.准备数据*/ this.drawPie(data); }; PieChart.prototype.drawPie = function (data) { var that = this; /*1.转化弧度*/ var angleList = this.transformAngle(data); /*2.绘制饼图*/ var startAngle = 0; angleList.forEach(function (item, i) { /*当前的结束弧度要等于下一次的起始弧度*/ var endAngle = startAngle + item.angle; that.ctx.beginPath(); that.ctx.moveTo(that.x0, that.y0); that.ctx.arc(that.x0, that.y0, that.radius, startAngle, endAngle); var color = that.ctx.fillStyle = that.getRandomColor(); that.ctx.fill(); /*下一次要使用当前的这一次的结束角度*/ /*绘制标题*/ that.drawTitle(startAngle, item.angle, color , item.title); /*绘制说明*/ that.drawDesc(i,item.title); startAngle = endAngle; }); }; PieChart.prototype.drawTitle = function (startAngle, angle ,color , title) { /*1.确定伸出去的线 通过圆心点 通过伸出去的点 确定这个线*/ /*2.确定伸出去的点 需要确定伸出去的线的长度*/ /*3.固定伸出去的线的长度*/ /*4.计算这个点的坐标*/ /*5.需要根据角度和斜边的长度*/ /*5.1 使用弧度 当前扇形的起始弧度 + 对应的弧度的一半 */ /*5.2 半径+伸出去的长度 */ /*5.3 outX = x0 + cos(angle) * ( r + outLine)*/ /*5.3 outY = y0 + sin(angle) * ( r + outLine)*/ /*斜边*/ var edge = this.radius + this.outLine; /*x轴方向的直角边*/ var edgeX = Math.cos(startAngle + angle / 2) * edge; /*y轴方向的直角边*/ var edgeY = Math.sin(startAngle + angle / 2) * edge; /*计算出去的点坐标*/ var outX = this.x0 + edgeX; var outY = this.y0 + edgeY; this.ctx.beginPath(); this.ctx.moveTo(this.x0, this.y0); this.ctx.lineTo(outX, outY); this.ctx.strokeStyle = color; /*画文字和下划线*/ /*线的方向怎么判断 伸出去的点在X0的左边 线的方向就是左边*/ /*线的方向怎么判断 伸出去的点在X0的右边 线的方向就是右边*/ /*结束的点坐标 和文字大小*/ this.ctx.font = '14px Microsoft YaHei'; var textWidth = this.ctx.measureText(title).width ; if(outX > this.x0){ /*右*/ this.ctx.lineTo(outX + textWidth,outY); this.ctx.textAlign = 'left'; }else{ /*左*/ this.ctx.lineTo(outX - textWidth,outY); this.ctx.textAlign = 'right'; } this.ctx.stroke(); this.ctx.textBaseline = 'bottom'; this.ctx.fillText(title,outX,outY); }; PieChart.prototype.drawDesc = function (index,title) { /*绘制说明*/ /*矩形的大小*/ /*距离上和左边的间距*/ /*矩形之间的间距*/ this.ctx.fillRect(this.space,this.space + index * (this.rectH + 10),this.rectW,this.rectH); /*绘制文字*/ this.ctx.beginPath(); this.ctx.textAlign = 'left'; this.ctx.textBaseline = 'top'; this.ctx.font = '12px Microsoft YaHei'; this.ctx.fillText(title,this.space + this.rectW + 10 , this.space + index * (this.rectH + 10)); }; PieChart.prototype.transformAngle = function (data) { /*返回的数据内容包含弧度的*/ var total = 0; data.forEach(function (item, i) { total += item.num; }); /*计算弧度 并且追加到当前的对象内容*/ data.forEach(function (item, i) { var angle = item.num / total * Math.PI * 2; item.angle = angle; }); return data; }; PieChart.prototype.getRandomColor = function () { var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return 'rgb(' + r + ',' + g + ',' + b + ')'; }; var data = [ { title: '15-20岁', num: 6 }, { title: '20-25岁', num: 30 }, { title: '25-30岁', num: 10 }, { title: '30以上', num: 8 } ]; var pieChart = new PieChart(); pieChart.init(data); </script> </body> </html> ~~~