使用canvas畫圓形(弧形)進度條

效果以下:javascript

clipboard.png

能夠展現整個圓、半圓以及任意角度弧形(左右對稱)的進度條。總體思路以下:html

  • 先肯定展現的形狀,是整個圓、半圓仍是通常的弧形java

  • 把肯定好形狀的圓弧均分100等份,計算出每一份所佔的弧度canvas

  • 灰色圓弧佔100份,紅色圓弧最終佔的份數由參數肯定函數

  • 設置setInterval定時器,重複執行畫圖操做優化

    • 清空畫布動畫

    • 先畫灰色的圓弧,佔100份spa

    • 再畫紅色的圓弧:紅色圓弧的份數從0開始,每次加1scala

    • 畫紅色圓弧末端的紅色圓:難點在於根據角度肯定紅色圓的圓心,這裏面涉及到三角函數,在草稿紙上畫個圖就大體明白了code

    • 當紅色圓弧的份數達到指定值(傳的參數)的時候,清除定時器

完整代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
  <title>Canvas</title>
</head>
<body>
  <canvas id="canvas" width="300" height="300"></canvas>

  <script>
    draw(66);
    /**
     * [順時針方向畫圖,起始點在左側]
     * @param  {[number]} percent [所佔的進度百分比,好比66%,則傳66便可,0 <= percent <= 100]
     * @param  {[number]} sR      [圓弧起始角度,可不傳,默認是π/2,Math.PI/2 <= sR < 3/2 * Math.PI]
     */
    function draw(percent, sR) {
      if (percent < 0 || percent > 100) {
        return;
      }
      if (sR < Math.PI/2 || sR >= 3/2 * Math.PI) {
        return;
      }

      var canvas = document.querySelector('#canvas'),
          cxt = canvas.getContext('2d'),
          cWidth = canvas.width,
          cHeight = canvas.height,
          baseColor = '#e1e1e1',
          coverColor = '#fe4d43',
          PI = Math.PI,
          sR = sR || 1/2 * PI; // 默認圓弧的起始點弧度爲π/2

      var finalRadian = sR + ((PI + (PI - sR) * 2) * percent / 100); // 紅圈的終點弧度
      var step = (PI + (PI - sR) * 2)/100; // 一個1%對應的弧度大小
      var text = 0; // 顯示的數字
      var timer = setInterval(function() {
        cxt.clearRect(0, 0, cWidth, cHeight);
        var endRadian =  sR + text * step;
        // 畫灰色圓弧
        drawCanvas(cWidth/2, cHeight/2, 80, sR, sR + (PI + (PI - sR) * 2), baseColor, 2);
        // 畫紅色圓弧
        drawCanvas(cWidth/2, cHeight/2, 80, sR, endRadian, coverColor, 2);

        // 畫紅色圓頭
        // 紅色圓頭其實就是一個圓,關鍵的是找到其圓心,涉及到三角函數知識,本身畫個圖一看就明瞭
        var angle = 2*PI - endRadian; // 轉換成逆時針方向的弧度(三角函數中的)
        xPos = Math.cos(angle) * 80 + cWidth/2; // 紅色圓 圓心的x座標
        yPos = -Math.sin(angle) * 80 + cHeight/2; // 紅色圓 圓心的y座標
        drawCanvas(xPos, yPos, 2, 0, 2*PI, coverColor, 2);

        // 數字
        cxt.fillStyle = coverColor;
        cxt.font = '40px PT Sans';
        var textWidth = cxt.measureText(text+'%').width;
        cxt.fillText(text+'%', cWidth/2 - textWidth/2, cHeight/2 + 15);
        text++;

        if (endRadian.toFixed(2) >= finalRadian.toFixed(2)) {
          clearInterval(timer);
        }
      }, 30);

      function drawCanvas(x,y,r,sRadian,eRadian,color,lineWidth) {
        cxt.beginPath();
        cxt.lineCap = "round";
        cxt.strokeStyle = color;
        cxt.lineWidth = lineWidth;
        cxt.arc(x, y, r, sRadian, eRadian, false);
        cxt.stroke();
      }
    }
  </script>
</body>
</html>

關於動畫部分,能夠使用requestAnimationFrame作優化,函數改寫以下:

function draw(percent, sR) {
  if (percent < 0 || percent > 100) {
    return;
  }
  if (sR < Math.PI/2 || sR >= 3/2 * Math.PI) {
    return;
  }

  var canvas = document.querySelector('#canvas'),
      cxt = canvas.getContext('2d'),
      cWidth = canvas.width,
      cHeight = canvas.height,
      baseColor = '#e1e1e1',
      coverColor = '#fe4d43',
      PI = Math.PI,
      sR = sR || 1/2 * PI; // 默認圓弧的起始點弧度爲π/2

  var finalRadian = sR + ((PI + (PI - sR) * 2) * percent / 100); // 紅圈的終點弧度
  var step = (PI + (PI - sR) * 2)/100; // 一個1%對應的弧度大小
  var text = 0; // 顯示的數字

  window.requestAnimationFrame(paint);
  function paint() {
    cxt.clearRect(0, 0, cWidth, cHeight);
    var endRadian =  sR + text * step;
    // 畫灰色圓弧
    drawCanvas(cWidth/2, cHeight/2, 80, sR, sR + (PI + (PI - sR) * 2), baseColor, 2);
    // 畫紅色圓弧
    drawCanvas(cWidth/2, cHeight/2, 80, sR, endRadian, coverColor, 2);

    // 畫紅色圓頭
    // 紅色圓頭其實就是一個圓,關鍵的是找到其圓心,涉及到三角函數知識,本身畫個圖一看就明瞭
    var angle = 2*PI - endRadian; // 轉換成逆時針方向的弧度(三角函數中的)
    xPos = Math.cos(angle) * 80 + cWidth/2; // 紅色圓 圓心的x座標
    yPos = -Math.sin(angle) * 80 + cHeight/2; // 紅色圓 圓心的y座標
    drawCanvas(xPos, yPos, 2, 0, 2*PI, coverColor, 2);

    // 數字
    cxt.fillStyle = coverColor;
    cxt.font = '40px PT Sans';
    var textWidth = cxt.measureText(text+'%').width;
    cxt.fillText(text+'%', cWidth/2 - textWidth/2, cHeight/2 + 15);
    text++;

    if (endRadian.toFixed(2) < finalRadian.toFixed(2)) {
      window.requestAnimationFrame(paint);
    }
  }

  function drawCanvas(x,y,r,sRadian,eRadian,color,lineWidth) {
    cxt.beginPath();
    cxt.lineCap = "round";
    cxt.strokeStyle = color;
    cxt.lineWidth = lineWidth;
    cxt.arc(x, y, r, sRadian, eRadian, false);
    cxt.stroke();
  }
相關文章
相關標籤/搜索