画扇形的方法
方法一:起始角度是0,那么第一条线就是line(r,0),通过旋转扇形的角度,第二条线就是line(r,0)
//圆弧 ctx.save(); ctx.translate(100, 100); ctx.arc(0,0,100,0, 30*Math.PI/180); ctx.restore(); //第一条线 ctx.save(); ctx.moveTo(100,100); ctx.lineTo(200,100); ctx.restore(); //第二条线 ctx.save(); ctx.translate(100, 100); ctx.moveTo(0,0); ctx.rotate(30*Math.PI/180); ctx.lineTo(100,0); ctx.stroke(); ctx.restore();
第一步为什么是设置原点呢,为什么不用moveTo来设置起始点呢?
因为画布的默认原点在0,0的位置上,如果用moveTo来设置起始点,原点依然还在0,0的位置,而变换是以原点为基准点的,即使你设置了起始点,但是起始点不是原点的话,图形旋转依然会围绕0,0点旋转然后自转,得到的图形就不知道是什么图形了。
方法二:
//将原点设置100,100位置 ctx.translate(100,100); //原点在100,100,则圆心设为0,0 ——> 100,100的位置 ctx.arc(0,0,100,30*Math.PI/180,60*Math.PI/180); //save(),restore()是为了防止角度旋转的污染 ctx.save(); ctx.rotate(30*Math.PI/180); ctx.moveTo(0,0); ctx.lineTo(100,0); ctx.restore(); ctx.rotate(60*Math.PI/180); ctx.moveTo(0,0); ctx.lineTo(100,0); ctx.stroke();
关于save()和restore():
其作用不只是可以防止外面的属性或方法对里面的绘制产生影响,它的本质意思是save()保存当前环境的状态,restore()返回之前保存路径的状态。在这里要注意还原的触点在什么地方,如下例子:
1 //将原点设置100,100位置 2 ctx.translate(100,100); 3 //原点在100,100,则圆心设为0,0 ——> 100,100的位置 4 ctx.arc(0,0,100,30*Math.PI/180,60*Math.PI/180); 5 //save(),restore()是为了防止角度旋转的污染 6 ctx.save(); 7 ctx.rotate(30*Math.PI/180); 8 ctx.moveTo(0,0); 9 ctx.lineTo(100,0); 10 ctx.restore(); 11 ctx.rotate(60*Math.PI/180); 12 ctx.lineTo(100,0); 13 ctx.stroke();跟上面的代码比就是少了12行的moveTo(0,0),结果如下图1:
原因是:线在旋转的时候,是从它的起点为圆心旋转的,而上面的代码是,第一条线从圆心开始,到圆弧的起点(旋转过后),自然现在的起点就是圆弧的起点了,然后再rotate(60*Math.PI/180);lineTo(100,0);就是从圆弧的起点画到(100,0)的线了
现在我们将第一条直线的起点设在(r,0)的位置,旋转后就到了圆弧的起始点,然后在画到圆心地方,那现在的起始点就是圆心了,再画一条线到圆弧,就可以了,如下:
//将原点设置100,100位置 ctx.translate(100,100); //原点在100,100,则圆心设为0,0 ——> 100,100的位置 ctx.arc(0,0,100,30*Math.PI/180,60*Math.PI/180); //save(),restore()是为了防止角度旋转的污染 ctx.save(); ctx.rotate(30*Math.PI/180); ctx.moveTo(100,0); ctx.lineTo(0,0); ctx.restore(); ctx.rotate(60*Math.PI/180); ctx.lineTo(100,0); ctx.stroke();
方法三:充分使用触点的作用,当我们再画圆弧的时候,画完之后其触点在圆弧的结束位置,为何不直接将这个触点作为起点,画一条到圆心的线,然后再画第二条线。如下代码:
//将原点设置100,100位置 ctx.translate(100,100); //原点在100,100,则圆心设为0,0 ——> 100,100的位置 ctx.arc(0,0,100,30*Math.PI/180,60*Math.PI/180); //以圆弧终点为起点画直线,rotate使得直角坐标系旋转了30° ctx.lineTo(0,0); ctx.rotate(30*Math.PI/180); //以0,0为起点画直线 ctx.lineTo(100,0); ctx.stroke();
方法四:配合beginPath()和closePath()
为什么要用translate,而不要moveTo,是因为如果需要旋转,所以就需要原点,现在不需要旋转,而是正常的画图,那么就不需要原点,就可以用moveTo。
配合beginPath()和closePath(),就会将一个圆弧封闭起来。
ctx.beginPath(); //定义起点 ctx.moveTo(100,100); //以起点为圆心,画一个半径为100的圆弧 ctx.arc(100,100,100,30*Math.PI/180, 60*Math.PI/180); ctx.closePath(); ctx.stroke();
特别注意:
- 使用
arc()
绘制图形时,如果没有设置moveTo(),
那么会从圆弧的开始的点作为起始点。如果设置了moveTo()
,那么该点会连线到圆弧起始点。- 如果使用
stroke()
方法,那么会从开始连线到圆弧的起始位置。 如果是 fill 方法, 会自动闭合路径填充
封装函数
CanvasRenderingContext2D.prototype.sector = function(x,y,r,angle1,angle2){ this.save(); this.beginPath(); this.moveTo(x,y); this.arc(x,y,r,angle1*Math.PI/180,angle2*Math.PI/180,false); this.closePath(); this.restore(); return this; } ctx.fillStyle = 'red'; ctx.sector(200,200,100,30,150).fill(); ctx.fillStyle = 'green'; ctx.sector(200,200,100,150,270).fill(); ctx.fillStyle = 'blue'; ctx.sector(200,200,100,270,390).fill();
画饼图
思路:
1)将每块饼的占比以整数形式储存在数组nums中,将每个饼的颜色以字符串形式储存在数组colors中,两个数组的值一一对应。将画布旋转坐标定义在即将绘制圆的中心,定义绘制圆弧(饼)的起始角度start和终止角度end均为0
2)绘制圆饼,6个不同板块,所以有6次for循环,从绘制第二个饼开始,起始角度是在上一个饼的终止角度位置,因此,每次循环开始后,要对当前的终止角度end进行累加一次,绘制圆弧直接以start开始,以end结束,当前绘制完成之后,要对起始角度start完成一次累加;同时每次绘制都给板块填充对应的颜色。封装为函数pieChart();
3)绘制圆饼对应的占比数值,也是6次for循环,为了便于代码易读,这里重新定义了一个函数。并对起始角度start和终止角度end重新利用,nums中储存的是当前数值占100的份数,将其转化为对应角度为nums[i]/50*Math.PI;让其显示在板块角度中线位置nums[i]/50*Math.PI/2;同样每次循环起始角度是在上一个饼的终止角度位置,绘制前后也要进行累加。封装为函数pieNum();
<canvas id="can1" width="800" height="600"></canvas> <script type="text/javascript"> var can1 = document.getElementById("can1"); var ctx = can1.getContext("2d"); var nums = [26,15,12,5,25,17]; var colors = ["#983335","#77963f","#5d437c","#35859f","#d1702f","#365e96"]; var start = 0; var end = 0; ctx.translate(400,350); //绘制圆饼 function pieChart(){ for (var i = 0;i < nums.length; i ++){ ctx.beginPath(); ctx.moveTo(0,0); end += nums[i]/50*Math.PI;//终止角度 ctx.strokeStyle = "white"; ctx.fillStyle = colors[i]; ctx.arc(0,0,200,start,end); ctx.fill(); ctx.closePath(); ctx.stroke(); start += nums[i]/50*Math.PI;//起始角度 } } //绘制圆饼上的数值 function pieNum(){ for (var i = 0;i < nums.length; i ++){ start = nums[i]/50*Math.PI/2; ctx.rotate(end+start);//旋转数值 ctx.font = "25px scans-serif"; ctx.fillStyle = "#000"; ctx.fillText(nums[i]+"%",140,0); end = nums[i]/50*Math.PI/2; } } ctx.rotate(-Math.PI/6);//旋转一定角度更加自然 pieChart(); pieNum(); </script>