canvas畫扇形、餅圖


畫扇形的方法

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM