波浪進度球是一種非常常見的進度展示方式,常用於加載頁。
下面我們來學習一下如何畫一個波浪進度球
- 首先我們分析一下進度球的組成部分有:一個圓,波紋,波紋的填充色,百分比文字
- 我們可以根據這幾個組成部分來制作動畫。
畫一個圓
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