1. 程式人生 > >canvas畫波浪進度球

canvas畫波浪進度球

封裝 src 有效 想象 移動 先來 aws 弧度 download

波浪進度球是一種非常常見的進度展示方式,常用於加載頁。

首先來個例子

再來個過程

下面我們來學習一下如何畫一個波浪進度球

  1. 首先我們分析一下進度球的組成部分有:一個圓,波紋,波紋的填充色,百分比文字
  2. 我們可以根據這幾個組成部分來制作動畫。

畫一個圓

var drawCircle = function(){
    ctx.beginPath();
    ctx.strokeStyle = ‘#1080d0‘;
    ctx.arc(r, r, cR+1, 0, 2 * Math.PI);
    ctx.stroke();
}

技術分享圖片
上面代碼中的:
ctx.arc() 方法就是畫一個圓形路徑,括號裏面都是參數:坐標軸的位置,半徑長度,起始位置弧度,終止位置弧度;
ctx.beginPath()

是用來開始一條路徑或者重置一條路徑,如果沒有這個方法,畫路徑的時候會用新的筆觸樣式將之前的路徑重畫一遍。
而且不管你用moveTo把畫筆移動到哪裏,只要不beginPath,那你一直都是在畫一條路徑。如果你畫出的圖形和你想像的不一樣,記得查看是否有合理的beginPath.

我們再來畫波浪

var drawSin = function(xOffset, color, waveHeight){
    ctx.save(); // 存儲當前畫布的樣式等
    var points=[];  //用於存放繪制Sin曲線的點
    ctx.beginPath();
    //在整個軸長上取點 , sx = 0; axisLength 是x軸軸長
    for(var x = sX; x < sX + axisLength; x += Math.PI/6){
        //此處坐標(x,y)的取點,依靠公式 “振幅高*sin(x*振幅寬 + 振幅偏移量)”
        var y = Math.sin((sX + x) * waveWidth + xOffset) * 0.8 + 0.1;
        var dY = mH * (1 - nowRange / 100 ); // 因為坐標系起點是左上角,所以y軸0點位置要用1-nowRange的差。
        points.push([x, dY + y * waveHeight]); // 存儲點
        ctx.lineTo(x, dY + y * waveHeight);    // 描路徑  
    }
    ctx.stroke()
    ctx.restore(); // 恢復上一次ctx.save() 存儲的,一定要有save()才有效,無默認。
};

技術分享圖片

主要內容就是三角函數裏面的sin,畫一個sin的坐標圖來實現波紋。知識關聯到canvas畫圖,就是將sin函數坐標點的xy軸坐標記錄下來,並且用lineTo() 方法描點。上圖是描點之後的樣子,接下來我們需要填充顏色,填充顏色相關的方法是fill()。設置fillStyle顏色值。

ctx.fillStyle = color;
ctx.fill();

技術分享圖片

Σ(;?д?)

怎麽成了這個鬼樣子,和想象中不一樣啊。對比之前的圖片好像是根據y軸0坐標的位置形成的封閉回路做的填充。

所以我們需要規劃一個封閉區間,因為我們的目的是要在波浪的下方填充顏色,所以需要將波浪下方的區域都包裹進來。我們要lineTo()到畫布的右下角,再左下角,再回到起點。
技術分享圖片

這樣我們再填充上顏色就會發現波浪線以下的區域都填充了顏色,但是可以預料到的是,這個顏色同時還覆蓋了我們之前畫的圓,成了圓上面的一個遮擋,並不像是圓裏面的波浪。

將填充的顏色區域限制,讓其看起來像在園內。

canvas有個方法叫clip(),對css比較熟悉的同學應會知道這個方法,他的作用就是限制可視範圍的。我們可以在畫圓的時候,用這個方法,將畫布的可視範圍限制在圓內, 記得不要在這裏使用ctx.restore() ,否則無法做區域限制。

var drawCircle = function(){
    ctx.beginPath();
    ctx.lineWidth  = 1;
    ctx.strokeStyle = ‘#1080d0‘;
    ctx.arc(r, r, cR+1, 0, 2 * Math.PI);
    ctx.stroke();
    ctx.beginPath();
    ctx.arc(r, r, cR, 0, 2 * Math.PI);
    ctx.clip();
}

//畫sin 曲線函數  xOffset表示x軸方向的偏移距離
var drawSin = function(xOffset, color, waveHeight){
    ctx.save();
    var points=[];  //用於存放繪制Sin曲線的點
    ctx.beginPath();
    //在整個軸長上取點 , sx = 0; axisLength 是x軸軸長
    for(var x = sX; x < sX + axisLength; x += Math.PI/6){
        //此處坐標(x,y)的取點,依靠公式 “振幅高*sin(x*振幅寬 + 振幅偏移量)”
        var y = Math.sin((sX + x) * waveWidth + xOffset) * 0.8 + 0.1;

        var dY = mH * (1 - nowRange / 100 ); // 因為坐標系起點是左上角,所以y軸0點位置要用1-nowRange的差。

        points.push([x, dY + y * waveHeight]); // 存儲點
        ctx.lineTo(x, dY + y * waveHeight);    // 描路徑  
    }
    //封閉路徑
    ctx.lineTo(axisLength, mH); // 右下角的點
    ctx.lineTo(sX, mH);   // 左下角的點
    ctx.lineTo(points[0][0],points[0][1]);  // 波浪起點
    // ctx.stroke()
    ctx.fillStyle = color;
    ctx.fill();

    ctx.restore();
};

技術分享圖片

因為已經填充顏色了,所以ctx.stroke() 方法就不需要了。

讓波浪動起來

波浪動起來的原理就是我們的sin曲線動起來,怎麽動呢? 比較初級的就是水平平移,所以我們需要重復調用畫曲線的方法,然後每次調用都給一個平移的值。這就是上面代碼中xOffset的作用。
平移有了,然後就是需要讓他自動重復調用這個方法就可以。這個我們可以用setTimeout或者setInterval來實現,但是這兩個方法不是動畫專用的方法,所以性能方面不是很優,最好使用由瀏覽器專門為動畫提供的API——requestAnimationFrame() 。把上面的畫圓畫波浪方法放進一個render() 方法封裝起來,然後requestAnimationFrame(render) 調用。就可以實現動畫的效果了。但是這個時候我們發現上面的曲線雖然運動了,但是看起來很違和,這是因為波浪寬度太小的原因,我們可以修改參數將波浪擴大。也就是修改waveWidth 這個變量的值,waveWidth越小,波紋越寬。我們還可以再添加一個顏色潛一些的波紋,讓波浪看起來更加立體。

寫文字

var drawText = function(){
    ctx.save();
    var size = 0.4*cR;
    ctx.font = size + ‘px Microsoft Yahei‘;
    ctx.textAlign = ‘center‘;
    ctx.fillStyle = "rgba(06, 85, 128, 0.5)";
    ctx.fillText(~~nowRange + ‘%‘, r, r + size / 2); // ~是取反,~~ 的目的是為了保證nowRange是數字
    ctx.restore();
};

技術分享圖片
這就是最終效果了,文中提到的是繪制的主要過程和思路,不是所有代碼。————jsdoit

canvas畫波浪進度球