canvas繪製圖表(二)

圖表是數據表示的一種直觀的形式,前端開發常常回合圖表打交道,通常都會借用第三方的js庫,如echats、bizchats等,
但若是隻是很簡單的圖標,咱們徹底能夠本身繪製,如今咱們來繪製一個簡單的百分比展現圖表javascript

最終效果以下

圖片描述

html代碼

<canvas id="charts"></canvas>

額,很簡單,隨便放在html某個地方html

分析

從最終效果來看,這個圖表由6部分組成
1.最大的外圈
2.最中心向外的內二圈
3.最裏面的內圈
4.帶顏色的多個圓弧組成的彩圈
5.中心的指針
6.數字文字前端

下來就按照順序繪製出來java

配置

首先仍是寫一些可配置的變量canvas

let r = 100,     //半徑
    cx = 0,         //圓心位置
    cy = 0,
    parts = [     //彩條
        '#78C77C','#F3D263','#FC9136','#FB574A','#AE1A51','#8F66DD'
    ], 
    texts = [     //文字段落
        0,30,60,90,120,150
    ],
    pad = 1/100, //間隙
    data = 0.5  //數據,百分比,20%應寫成0.2

初始化

let canvas = document.getElementById('chart')
canvas.width = 262;
canvas.height = 262;
let ctx = canvas.getContext('2d')
ctx.translate(canvas.width/2,canvas.height/2);
ctx.save()
ctx.rotate(0.5*Math.PI)

爲何要旋轉0.5π呢,由於爲了便於計算,須要把圓的起始位置轉至最下函數

繪製-外圈

ctx.beginPath()
ctx.arc(cx,cy,r+30,0,2*Math.PI)
ctx.fillStyle="#fff"
ctx.strokeStyle="gray"

ctx.shadowOffsetX = 0; // 陰影Y軸偏移
ctx.shadowOffsetY = 0; // 陰影X軸偏移
ctx.shadowBlur = 1; // 模糊尺寸
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'; // 顏色
ctx.stroke();
ctx.fill();

效果

圖片描述

繪製-內二圈

ctx.beginPath()
ctx.arc(cx,cy,r-20,0,2*Math.PI)
ctx.fillStyle="#F2F1F3"
ctx.fill();

效果

圖片描述

注意

爲何不先繪製最內層的圈,由於canvas的層級關係是後來者居上,因此有重疊的部分最上面的圖形最後繪製優化

繪製-內圈

ctx.beginPath()
ctx.arc(cx,cy,r-20-30,0,2*Math.PI)
ctx.fillStyle="#fff"
ctx.strokeStyle="#fff"
ctx.stroke();
ctx.fill();

效果

圖片描述

繪製-彩圈

這裏能夠理解爲多個同心圓弧,把2π等分,中間有相同的間隔spa

let startRad = 0
let partRad = ( 2 - (pad*parts.length) ) / parts.length
parts.forEach((color,index)=>{
    if (index===0) {
        startRad += pad/2
    } else {
        startRad += pad + partRad
    }
    
    ctx.beginPath()
    ctx.strokeStyle = color
    ctx.lineWidth = 20
    ctx.arc(cx,cy,r,startRad * Math.PI,(startRad+partRad)*Math.PI)
    ctx.stroke()
})
ctx.restore();

注意

1.等分2π的時候別忘了間隔
2.下一個圓弧的起始點是上一個圓弧的終點加上間隔3d

效果

圖片描述

繪製-指針

ctx.save();
ctx.rotate(data*2*Math.PI)    
ctx.beginPath()
ctx.moveTo(-6,0)
ctx.lineTo(0,r-30)
ctx.lineTo(6,0)
ctx.closePath();
ctx.fillStyle = '#4394F8';
ctx.fill();
ctx.beginPath()
ctx.arc(0,0,10,0,2*Math.PI)
ctx.fillStyle = '#F4F6F9';
ctx.strokeStyle = 'gray'
ctx.strokeWidth = 1
ctx.shadowOffsetX = 0; // 陰影Y軸偏移
ctx.shadowOffsetY = 0; // 陰影X軸偏移
ctx.shadowBlur = 10; // 模糊尺寸
ctx.shadowColor = '#F4F6F9'; // 顏色
ctx.stroke()
ctx.fill();
ctx.restore();

注意

1.指針由2部分構成,中心的圓形三角形
2.注意是圓形在上,三角形在下,因此最後繪製圓形指針

效果

圖片描述

繪製-文字

ctx.save()
ctx.rotate(0.5*Math.PI)
let beginRotate = 0,
    textPartRad = 2/(texts.length)
texts.forEach((text,index)=>{
    if (index!=0) {
        beginRotate = beginRotate + textPartRad
    }
    ctx.save();
    ctx.beginPath()
    ctx.font = "15px"
    ctx.textAlign = 'center'
    ctx.textBaseline = 'middle'
    ctx.translate(cx+Math.cos(beginRotate*Math.PI)*(r-35),cy+Math.sin(beginRotate*Math.PI)*(r-35));
    ctx.rotate(beginRotate*Math.PI+(-0.5*Math.PI))
    ctx.rotate(Math.PI)
    ctx.fillText(text,0,0)
    ctx.restore();
})
ctx.restore();

注意

1.每一個文字先放到最中間(0,0)
2.而後根據圓的半徑和文字放置地點的旋轉角度,經過三角函數計算出位置,而後移動
3.根據旋轉角度將文字旋轉到合適位置

完整代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Chart</title>
</head>
<body>
    <canvas id="chart"></canvas>
</body>
<script type="text/javascript">
    let r = 100,     //半徑
        cx = 0,         //圓心位置
        cy = 0,
        parts = [     //彩條
            '#78C77C','#F3D263','#FC9136','#FB574A','#AE1A51','#8F66DD'
        ], 
        texts = [     //文字段落
            0,35,75,115,130,250
        ],
        pad = 1/100, //間隙
        data = 0.7  //數據,百分比,20%應寫成0.2

    let canvas = document.getElementById('chart')
    canvas.width = 262;
    canvas.height = 262;
    let ctx = canvas.getContext('2d')
    ctx.translate(canvas.width/2,canvas.height/2);
    ctx.save()
    ctx.rotate(0.5*Math.PI)

    //最大的外圈
    ctx.beginPath()
    ctx.arc(cx,cy,r+30,0,2*Math.PI)
    ctx.fillStyle="#fff"
    ctx.strokeStyle="gray"

    ctx.shadowOffsetX = 0; // 陰影Y軸偏移
    ctx.shadowOffsetY = 0; // 陰影X軸偏移
    ctx.shadowBlur = 1; // 模糊尺寸
    ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'; // 顏色
    ctx.stroke();
    ctx.fill();

    //內二圈
    ctx.beginPath()
    ctx.arc(cx,cy,r-20,0,2*Math.PI)
    ctx.fillStyle="#F2F1F3"
    ctx.fill();



    //內圈
    ctx.beginPath()
    ctx.arc(cx,cy,r-20-30,0,2*Math.PI)
    ctx.fillStyle="#fff"
    ctx.strokeStyle="#fff"
    ctx.stroke();
    ctx.fill();
    

    // 彩圈
    let startRad = 0
    let partRad = ( 2 - (pad*parts.length) ) / parts.length
    parts.forEach((color,index)=>{
        if (index===0) {
            startRad += pad/2
        } else {
            startRad += pad + partRad
        }
        
        ctx.beginPath()
        ctx.strokeStyle = color
        ctx.lineWidth = 20
        ctx.arc(cx,cy,r,startRad * Math.PI,(startRad+partRad)*Math.PI)
        ctx.stroke()
    })
    ctx.restore();

    //指針
    ctx.save();
    ctx.rotate(data*2*Math.PI)    
    ctx.beginPath()
    ctx.moveTo(-6,0)
    ctx.lineTo(0,r-30)
    ctx.lineTo(6,0)
    ctx.closePath();
    ctx.fillStyle = '#4394F8';
    ctx.fill();
    ctx.beginPath()
    ctx.arc(0,0,10,0,2*Math.PI)
    ctx.fillStyle = '#F4F6F9';
    ctx.strokeStyle = 'gray'
    ctx.strokeWidth = 1
    ctx.shadowOffsetX = 0; // 陰影Y軸偏移
    ctx.shadowOffsetY = 0; // 陰影X軸偏移
    ctx.shadowBlur = 10; // 模糊尺寸
    ctx.shadowColor = '#F4F6F9'; // 顏色
    ctx.stroke()
    ctx.fill();
    ctx.restore();

    // 文字
    ctx.save()
    ctx.rotate(0.5*Math.PI)
    let beginRotate = 0,
        textPartRad = 2/(texts.length)
    texts.forEach((text,index)=>{
        if (index!=0) {
            beginRotate = beginRotate + textPartRad
        }
        ctx.save();
        ctx.beginPath()
        ctx.font = "15px"
        ctx.textAlign = 'center'
        ctx.textBaseline = 'middle'
        ctx.translate(cx+Math.cos(beginRotate*Math.PI)*(r-35),cy+Math.sin(beginRotate*Math.PI)*(r-35));
        ctx.rotate(beginRotate*Math.PI+(-0.5*Math.PI))
        ctx.rotate(Math.PI)
        ctx.fillText(text,0,0)
        ctx.restore();
    })
    ctx.restore();
    
    


</script>
</html>

還有許多能夠優化的地方,如代碼中顏色,局部位置的距離等等,能夠作成配置項,有時間在優化吧^_^

相關文章
相關標籤/搜索