天天看點

canvas 簡易的加載進度條

做一個web app,想在第一次或者更新的時候,有一個更新進度條,我個人比較喜歡圓的那種。 

canvas + svg高低配,應該還不錯的。順便一提,canvas用來壓縮圖檔也是麼麼哒的。

 先看下效果圖,我咋覺得邊有點虛。基本的樣子有了,但是美觀,美觀,我要美觀,下次再來。

canvas 簡易的加載進度條

原理就是繪制圓和文本繪制。

特别說明:

1. 會自動計算canvas的寬高,取最大值,并重設寬高為最大值

2. 有簡單的計算邏輯,讓 百分比的文本居中

3. 超大的邊寬會被限制

上代碼。

class CanvasProgress {
    constructor(cv, options = {
        bgColor: '#123456',
        cBgColor: 'green',
        edgeWidth: 20
    }) {

        if (!cv || !cv.getContext) {
            throw new Error('參數cv為空或者getContext方法未定義')
        }

        this._ctx = cv.getContext('2d')
        this._diameter = this._getDiameter(cv)  //直徑
        this._radius = Math.ceil(this._diameter / 2) //半徑
        this._options = Object.assign({
            bgColor: '#123456', //未加載的背景色
            cBgColor: 'green', //已加載的背景色
            edgeWidth: 20,     //邊款
            textMaxWidth: this._radius  //進度文本最大長度
        }, options)
        this._options.edgeWidth = Math.min(this._radius * 0.28, this._options.edgeWidth) //重新計算
        this._circleParams = {
            x: this._radius,//圓心的x軸坐标值
            y: this._radius,//圓心的y軸坐标值
            r: Math.floor(this._radius - this._options.edgeWidth / 2 - 1) //圓的半徑
        }

        this._resizeCanvas(cv)  //調整canvas寬高一緻
        this._initialize() //繪制背景圓
    }


    /**
     * 通過canvas獲得直徑
     * @param {*canvas對象} cv 
     */
    _getDiameter(cv) {
        return Math.max(cv.height || 0, cv.width || 0) || 200
    }

    /**
     * 進度轉換角度
     * @param {*進度} progress 
     */
    _getAngle(progress) {
        return (progress / 100) * Math.PI * 2
    }

    _resizeCanvas(cvProgress) {
        cvProgress.width = cvProgress.height = this._diameter
    }

    /**
     * 調整canvas寬高一緻
     */
    _initialize() {
        let ctx = this._ctx
        // 開始一個新的繪制路徑
        ctx.beginPath()
        //設定弧線的顔色為藍色
        ctx.strokeStyle = this._options.bgColor
        ctx.lineWidth = this._options.edgeWidth
        //以canvas中的坐标點(100,100)為圓心,繪制一個半徑為50px的圓形
        ctx.arc(this._circleParams.x, this._circleParams.y, this._circleParams.r, 0, Math.PI * 2, false)
        //按照指定的路徑繪制弧線
        ctx.stroke()
    }

    /**
     * 更近進度
     * @param {*進度 0-100} progressValue 
     * @param {*繪制圓的設定} circleSettings 
     * @param {*繪制文本的設定} progressTextSettings 
     */
    updateProgress(progressValue, circleSettings, progressTextSettings) {
        if (progressValue <= 0) {
            return
        }
        this._updateCircle(progressValue, circleSettings)
        this._updateProgressText(progressValue, progressTextSettings)
    }

    /**
     * 繪制圓
     * @param {*進度} progressValue 
     * @param {*設定} settings 
     */
    _updateCircle(progressValue, settings) {
        let ctx = this._ctx
        ctx.beginPath()
        let angle = this._getAngle(progressValue)

        ctx.strokeStyle = this._options.cBgColor
        ctx.lineWidth = this._options.edgeWidth

        if (settings) {
            Object.keys(settings).forEach(k => {
                ctx[k] = settings[k]
            })
        }

        ctx.arc(this._circleParams.x, this._circleParams.y, this._circleParams.r, 0 + Math.PI * 1.5, angle + Math.PI * 1.5, false)
        ctx.stroke()
    }

    /**
     * 繪制進度文本
     * @param {*進度} progressValue 
     * @param {*設定} settings 
     */
    _updateProgressText(progressValue, settings) {

        if (progressValue < 0 || progressValue > 100) {
            return
        }

        let ctx = this._ctx,
            r = this._radius,
            fontSize = this._getFontSize(settings)
        ctx.clearRect(r * 0.5, r * 0.5, r, r)
        ctx.font = `${fontSize}px sans-serif`
        ctx.fillStyle = this._getTextGradient()
        if (settings) {
            Object.keys(settings).forEach(k => {
                ctx[k] = settings[k]
            })
        }

        ctx.fillText(progressValue + '%', this._circleParams.x - this._options.textMaxWidth / 2, this._circleParams.y + fontSize / 2, this._options.textMaxWidth)
    }

    /**
     * 漸變設定
     */
    _getTextGradient() {
        var gradient = this._ctx.createLinearGradient(0, 0, 100, 0);
        gradient.addColorStop("0", "red");
        gradient.addColorStop("0.5", "blue");
        gradient.addColorStop("1.0", "green");
        return gradient
    }

    /**
     * 獲得目前文字大小
     * @param {*設定} settings 
     */
    _getFontSize(settings) {
        if (settings && settings.font) {
            let matchItem = settings.font.match(/\d{1,2}/)
            if (matchItem) {
                return Number.parseInt(matchItem[0])
            }
        }
        return this._radius * 0.6
    }

}
      

  

調用代碼:

<!DOCTYPE>
<html>

<head>
    <title> canvas 原型進度條</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta charset="utf-8">
</head>

<body style="margin:5% 10% 0 10%">
    <div>
        <canvas id="cvProgress" height="250" width="15">
    </div>
    <script src="CanvasProgress.js"></script>
</body>
<script>
    let ps = new CanvasProgress(cvProgress, {
        edgeWidth: 50       
    }), progress = 0
    let tickets = setInterval(function () {
        progress += 5
        ps.updateProgress(progress,null,{               
        })
        if (progress > 100) {
            clearInterval(tickets)
        }
    }, 100)

</script>

</html>      

源碼位址:https://github.com/xiangwenhu/BlogCodes/tree/master/client/canvas

繼續閱讀