畫扇形的方法
方法一:起始角度是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>