效果圖
兩個(gè)步驟:
第一步畫一個(gè)扇形
第二步把扇形畫滿360°
-
來看第一個(gè)步驟埋凯,怎么畫扇形锅减?
看下面的示意圖
原理示意圖
- 要畫扇形圖脆诉,首先需要知道圓心坐標(biāo)甚亭,然后需要知道半徑,這樣就可以畫出一條線击胜,然后在用畫圓函數(shù)obj.arc在線的終點(diǎn)坐標(biāo)根據(jù)起始角度和終止角度畫出一段弧亏狰,最后在閉合圖形,就能畫出一條弧偶摔,圖中cx暇唾、cy是圓心坐標(biāo),#1是畫出的第一段線辰斋,x策州、y是線的終點(diǎn)坐標(biāo),然后以線的終點(diǎn)坐標(biāo)為弧的起點(diǎn)坐標(biāo)宫仗,即x菱农、y怜庸,加上起始角度(圖中ang1)和終止角度(圖中ang2)畫出一段弧(圖中#2),最后閉合路徑(obj.closePath())即可以畫出圖中所示的一小段弧娇未。
- 畫扇形步驟開始:
先獲取canvas對(duì)象:
let c1 = document.getElementById('can');
let gd = c1.getContext('2d');
- 給一個(gè)圓心坐標(biāo)cx华坦、cy间驮,半徑r:
let cx=200,cy=200,r=150;
- 開始路徑:
gd.beginPath();
- 畫筆移到圓心:
gd.moveTo(cx,cy);
- 然后移動(dòng)到x驾凶、y畫出一條線
那x、y怎么求呢滩褥?再來看這張圖
原理示意圖
已知圓心cx病蛉、cy、r和起始角度ang1要求圖中x铸题、y铡恕,利用三角函數(shù)知識(shí)可知,圖中的紅邊a=cos(ang1)r丢间,藍(lán)色邊b=sin(ang1)r探熔;而x、y的值就是圓心坐標(biāo)分別加上a和b烘挫,就是我們求的線的終點(diǎn)坐標(biāo)即弧的起點(diǎn)坐標(biāo)诀艰。
假設(shè)我們令起始角度ang1=30°柬甥,終止角度ang2=80°,由于Math.cos和Math,sin的參數(shù)是弧度制其垄,因此我們還需要一個(gè)函數(shù)把角度制轉(zhuǎn)成弧度制:
function d2a(n){//角度轉(zhuǎn)弧度
return n*Math.PI/180;
}
function a2d(n){//弧度轉(zhuǎn)角度
return n*180/Math.PI;
}
let startAng=30,endAng=80;
let x=cx+Math.cos(d2a(startAng)),y=cy+Math.sin(d2a(startAng));
把線畫過去
gd.lineTo(x,y);
- 然后畫豢疗选:
gd.arc(cx,cy,r,d2a(startAng),d2a(endAng),false);
- 最后閉合路徑、填充:
gd.closePath();
gd.fillStyle='orange';
gd.fill();
扇形就畫出來了:
扇形
-
接下來第二個(gè)步驟绿满,把扇形畫滿360°
在解決這個(gè)問題之前臂外,先要解決一個(gè)問題:怎么把一份數(shù)據(jù)用餅狀圖表示出來?
再來看那個(gè)圖:
原理示意圖
假設(shè)一份數(shù)據(jù)包含200個(gè)男的喇颁,100個(gè)女的和50個(gè)其他漏健,那么200/(200+100+50)≈0.571就是性別為男的占比,用0.571*360=205.56就是200個(gè)男的在餅狀圖中占了多少度橘霎,這個(gè)角度就是我們要用的起始角度蔫浆,終止角度就是起始角度+弧長(zhǎng)就自己給我們畫出來了
- 那么需要畫多少個(gè)扇形呢?
有多少份數(shù)據(jù)就畫多少個(gè)扇形
例:
let datas=[
{name:'菠菜',data:300,color:'#f5c'},
{name:'玉米',data:200,color:'#dd0'},
{name:'花生',data:20,color:'#0fc'},
{name:'大豆',data:180,color:'#f85'}
]
這是我們的數(shù)據(jù)姐叁,菠菜300斤瓦盛,餅圖顏色etc.現(xiàn)在需要用餅狀圖把他表示出來
我們把前面畫扇形的函數(shù)封裝一下,然后用一個(gè)循環(huán)遍歷數(shù)組就可以畫出來了
封裝:
function pie(startAng, endAng, color) {
let x = cx + r*Math.cos(d2a(startAng)), y = cy + r*Math.sin(d2a(startAng));
gd.beginPath();
//#1
gd.moveTo(cx, cy);
gd.lineTo(x, y);
//#2
gd.arc(cx, cy, r, d2a(startAng), d2a(endAng), false);
//#3
gd.closePath();
gd.fillStyle = color;
gd.fill();
}
原理示意圖
先求出總共有多少斤:
let sum=0;
datas.forEach(data=>{
sum+=data.data;
})
在畫餅圖的時(shí)候我們還需要一個(gè)變量now外潜,用來記錄當(dāng)前的角度原环,然后用now+我們算出來的在餅狀圖占多少度就是我們要畫的一個(gè)扇形
let now=0;
開始畫:
datas.forEach(data=>{
let ang=360*data.data/sum;//求出需要畫多少度
pie(now,now+ang,data.color);//畫扇形
//添加名字
let mid=(now+now+ang)/2;//取每一塊扇圖的中間位置為字體的坐標(biāo)
let x = cx + r*Math.cos(d2a(mid)), y = cy + r*Math.sin(d2a(mid));//算出字體的x、y坐標(biāo)
gd.font='bold 20px 宋體';//添加字體
if(mid<180){//如果小于180°就讓坐標(biāo)大一點(diǎn)橡卤,文字往下走
gd.fillText(data.name,x+30,y+30);
}else{//如果大于180°就讓坐標(biāo)小一點(diǎn)扮念,文字往上走
gd.fillText(data.name,x-30,y-30);
}
now+=ang;//更新now
})
完整代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body {
background: gray;
text-align: center;
}
</style>
<script>
window.onload = function () {
let c1 = document.getElementsByTagName('canvas')[0];
let gd = c1.getContext('2d');
let cx = 400, cy = 300, r = 150;
function d2a(n) {//角度轉(zhuǎn)弧度
return n * Math.PI / 180;
}
function a2d(n) {//弧度轉(zhuǎn)角度
return n * 180 / Math.PI;
}
function pie(startAng, endAng, color) {
let x = cx + r*Math.cos(d2a(startAng)), y = cy + r*Math.sin(d2a(startAng));
gd.beginPath();
//#1
gd.moveTo(cx, cy);
gd.lineTo(x, y);
//#2
gd.arc(cx, cy, r, d2a(startAng), d2a(endAng), false);
//#3
gd.closePath();
gd.fillStyle = color;
gd.fill();
}
let datas=[//數(shù)據(jù)
{name:'菠菜',data:300,color:'#f5c'},
{name:'玉米',data:200,color:'#dd0'},
{name:'花生',data:20,color:'#0fc'},
{name:'大豆',data:180,color:'#f85'}
]
let sum=0,now=0;
datas.forEach(data=>{
sum+=data.data;//求出總和
})
datas.forEach(data=>{
let ang=360*data.data/sum;//求出需要畫多少度
pie(now,now+ang,data.color);//畫扇形
//添加名字
let mid=(now+now+ang)/2;//取每一塊扇圖的中間位置為字體的坐標(biāo)
let x = cx + r*Math.cos(d2a(mid)), y = cy + r*Math.sin(d2a(mid));//算出字體的x损搬、y坐標(biāo)
gd.font='bold 20px 宋體';//添加字體
if(mid<180){//如果小于180°就讓坐標(biāo)大一點(diǎn)碧库,文字往下走
gd.fillText(data.name,x+30,y+30);
}else{//如果大于180°就讓坐標(biāo)小一點(diǎn),文字往上走
gd.fillText(data.name,x-30,y-30);
}
now+=ang;//更新now
})
}
</script>
</head>
<body>
<canvas width="800" height="600" style="background:white;"></canvas>
</body>
</html>
原理示意圖