畫了一個canvas環形圖,拿去用吧~(原生和小程序2個版本)

最近作的這個小程序,因爲UI設計圖已經經業務方確認過,這個環形圖沒有商量的餘地(好比用插件什麼的),抱着正好玩一玩canvas的心態,自個看了點資料,畫了一個。(包括原生js版和小程序版)javascript

效果圖:

需求:

  • 每個月任務進度的百分比會由後臺返回,是一個動態傳遞的數值
  • 百分比不一樣,彩色圓環的長度不一樣
  • 彩色圓環要漸變色
  • 彩色圓環盡頭有個icon,也要跟着走動

原生js版本

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <canvas id="circle" width="500" height="500"></canvas>
    <script type="text/javascript">
        circle('0.68');
        function circle(percent) {
            var canvas = document.getElementById('circle');
            var imgObj = new Image();
            imgObj.src = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1572866759391&di=af21821454e4acb13e6ba85150ab690d&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01708b55ed1f8e6ac7251df808d695.jpg"; // 弧形結束點的圖片,若是失效了,本身隨便替換一個網絡圖片
            var ctx;
            //待圖片加載完後,將其顯示在canvas上,避免其沒法顯示
            imgObj.onload = function(){ //onload必須使用
            ctx = canvas.getContext("2d");
            /*填充文字*/
            ctx.font = "24px Microsoft YaHei";
            /*文字顏色*/
            ctx.fillStyle = '#999';
            /*文字內容*/
            var insertContent = '本月任務進度';
            var text = ctx.measureText(insertContent);
            /*插入文字,後面兩個參數爲文字在畫布中的座標點*/
            /*此處注意:text.width得到文字的寬度,而後就能計算出文字居中須要的x值*/
            ctx.fillText(insertContent, (360 - text.width) / 2, 240);

            /*填充百分比*/
            ctx.font = "80px Microsoft YaHei";
            ctx.fillStyle = '#222';
            const percentArr = percent.split('.');
            var ratioStr = percentArr[1] + '%';
            var text = ctx.measureText(ratioStr);
            ctx.fillText(ratioStr, (360 - text.width) / 2, 180);

            /*開始圓環*/
            var circleObj = {
                ctx: ctx,
                /*圓心座標*/
                x: 180,
                y: 180,
                /*半徑,下方出現的150都是半徑*/
                radius: 150,
                /*環的寬度*/
                lineWidth: 24
            }

            /*灰色的圓環*/
            /*開始的度數-從上一個結束的位置開始*/
            circleObj.startAngle = 0; // 注意這裏的0是3點鐘方向,而非12點方向,和數學裏的不同
            /*結束的度數*/
            circleObj.endAngle = Math.PI * 2;
            circleObj.color = '#E7EFF4';
            drawCircle(circleObj);

            /*有色的圓環*/
            /*從-90度的地方開始畫*/
            circleObj.startAngle = 1.5*Math.PI; //把起始點改爲數學裏的12點方向
            /*從當前度數減去-90度*/
            let holeCicle = 2 * Math.PI;
            let jiaodu = percent * 360; //圓弧的角度

            let x1 = 180 + (150 * Math.sin(jiaodu * Math.PI/180)) - 25; // 爲啥要減去25,由於icon圖標的寬度爲50,而定位是以icon左上角爲基準的
            let y1 = 180 - 150 +  (150 - 150 * Math.cos(jiaodu * Math.PI/180)) - 25;
            circleObj.endAngle = 1.5*Math.PI + percent*holeCicle;
            var gnt1 = ctx.createLinearGradient(0,0,x1,y1);
            gnt1.addColorStop(0,'#FF8941');
            gnt1.addColorStop(0.3,'#FF8935');
            gnt1.addColorStop(1,'#FFC255');
            // ctx.strokeStyle = gnt1;
            circleObj.color = gnt1;
            drawCircle(circleObj);
            // 這裏的this指的是imgObj,第二三個參數是它的座標,四五個是長款
            ctx.drawImage(this, x1, y1, 50, 50);
            }

        }
        /*畫曲線*/
        function drawCircle(circleObj) {
            var ctx = circleObj.ctx;
            ctx.beginPath();
            ctx.arc(circleObj.x, circleObj.y, circleObj.radius, circleObj.startAngle, circleObj.endAngle, false);
            //設定曲線粗細度
            ctx.lineWidth = circleObj.lineWidth;
            //給曲線着色
            ctx.strokeStyle = circleObj.color;

            //鏈接處樣式
            ctx.lineCap = 'round';
            //給環着色
            ctx.stroke();
            ctx.closePath();
        }
    </script>
</body>
</html>
複製代碼

一開始我是用原生寫的,他大爺的,轉移到小程序發現,怎麼api都長的不同!還有不少隱藏的深坑,好比拿不到圖片對象啊,顯示不出來啊,還好,最後順利搞定。html

小程序版

js:

Page({
  data: {
    canvas: {
      height: 0,
      width: 0
    },
    canvasWidth: 100, // 這裏拜託必定要給個初始寬度啊~否則iPhone6以及華爲手機會偶爾沒法顯示,並且是鎖屏後打開又莫名其妙顯示的那種……
    x:0,
    y:0, // 圓心座標
    r:0, // 半徑
  },

  onReady() {
    let rpx = this.getRatio();
    let canvasWidth = rpx * 300;
    let r = rpx * 75;
    let x = canvasWidth / 2;
    let y = canvasWidth / 2;
    this.setData({
      canvasWidth,
      r,
      x,
      y,
      rpx
    });
    this.drawRing(68%);
  },

  // 獲取畫布寬度相對於375的比例,方便適配各類屏幕
  getRatio() {
    let w = 0;
    wx.getSystemInfo({
      success: function (res) {
        w = res.windowWidth / 375;//按照750的屏寬
      },
    })
    return w
  },

  drawRing(percent) {
    const {r, x, y, rpx, canvasWidth} = this.data;
    let url = "success.png";
    var ctx;
    var ctx2; // 這裏也是很迷,原生js只用建立一個,這裏卻要2個,分別用來盛放灰色圈和彩色圈
    //待圖片加載完後,將其顯示在canvas上
    ctx = wx.createCanvasContext('circle');
    ctx2 = wx.createCanvasContext('circle2');
    /*填充文字*/
    ctx.setFontSize(12 * rpx);
    ctx2.setFontSize(40 * rpx);
    /*文字顏色*/
    ctx.setFillStyle('#999');
    /*文字內容*/
    var insertContent = '本月任務進度';
    var text = ctx.measureText(insertContent);
    /*插入文字,後面兩個參數爲文字的位置*/
    /*此處注意:text.width得到文字的寬度,而後就能計算出文字居中須要的x值*/
    ctx.fillText(insertContent, (canvasWidth - text.width)/2, 185 * rpx);

    /*填充百分比*/
    ctx2.setFillStyle('#222');
    var ratioStr = this.data.options.clockRate;
    var text2 = ctx2.measureText(ratioStr);
    ctx2.fillText(ratioStr, (canvasWidth - text2.width) / 2, 165*rpx);

    /*開始圓環*/
    var circleObj = {
        ctx: ctx,
        /*圓心*/
        x: x,
        y: y,
        /*半徑*/
        radius: r,
        /*環的寬度*/
        lineWidth: 12 * rpx,
        startAngle: 0,
        endAngle: 0,
        color: ''
    }

    /*灰色的圓環*/
    /*開始的度數-從上一個結束的位置開始*/
    circleObj.startAngle = 0;
    /*結束的度數*/
    circleObj.endAngle = Math.PI * 2;
    circleObj.color = '#E7EFF4';
    this.drawCircle(circleObj);

    /*有色的圓環*/
    var coloredObject = {
      ctx: ctx2,
      /*圓心*/
      x: x,
      y: y,
      /*半徑*/
      radius: r,
      /*環的寬度*/
      lineWidth: 12 * rpx,
      startAngle: 0,
      endAngle: 0,
      color: ''
  }
    /*從-90度的地方開始畫*/
    coloredObject.startAngle = 1.5*Math.PI;
    /*從當前度數減去-90度*/
    let holeCicle = 2 * Math.PI;

    const percentArr = percent.split('.');
    var ratioStr = percentArr[1] + '%';
    const rate = percent
    let x1 = x + (r * Math.sin(Number(rate) * 360 * Math.PI/180)) - 30*rpx/2;
    let y1 = y - r +  (r - r * Math.cos((Number(rate) * 360) * Math.PI/180)) - 30*rpx/2;
    coloredObject.endAngle = 1.5*Math.PI + Number(rate)*holeCicle;
    var gnt1 = ctx2.createLinearGradient(0,0,x1,y1);
    gnt1.addColorStop(0,'#FF8941');
    gnt1.addColorStop(0.3,'#FF8941');
    gnt1.addColorStop(1,'#FFC255');
    coloredObject.color = gnt1;
    this.drawCircle(coloredObject, x1, y1);
    // }

},

  /*畫曲線*/
  drawCircle(circleObj, x1, y1) {
    var ctx = circleObj.ctx;
    ctx.beginPath();
    ctx.arc(circleObj.x, circleObj.y, circleObj.radius, circleObj.startAngle, circleObj.endAngle, false);
    //設定曲線粗細度
    ctx.setLineWidth(circleObj.lineWidth);
    //給曲線着色
    ctx.setStrokeStyle(circleObj.color);

    //鏈接處樣式
    ctx.setLineCap('round');
    //給環着色
    ctx.stroke();
    ctx.closePath();
    if (x1 && y1) {
      ctx.drawImage('success.png', x1, y1, 30*this.data.rpx, 30*this.data.rpx);
    }
    ctx.draw();
  },
});
複製代碼

wxml:

這裏也有一個坑,那就是原生js直接經過id屬性獲取canvas畫布對象就好,可是,小程序裏必定要搞個canvas-id屬性……java

<canvas wx:if="{{canvasWidth}}" canvas-id="circle" id="circle" style="height: {{canvasWidth || 100}}px;width: {{canvasWidth || 100}}px;margin:0 auto;"></canvas>
<canvas wx:if="{{canvasWidth}}" canvas-id="circle2" id="circle2" style="height: {{canvasWidth}}px;width: {{canvasWidth}}px;position:absolute;top:0;left:50%;margin-left:-{{canvasWidth/2}}px;"></canvas>
複製代碼

好了,到此就大功告成咯~這玩意就是有點繁瑣,而後須要一丟丟三角函數(只要你想找,公式處處都有啦)canvas

由於寫了有大半個月了,也不記得原生js版借鑑了哪些文章,抱歉不能列出……小程序

相關文章
相關標籤/搜索