目錄
本文8475字,閱讀9分鐘,實踐40分鐘
文 / 石橋碼農
progress是進度條組件。
1)主要屬性
progress組件的主要屬性有:
progress進度條組件是一個很完備的組件了,不需要修改,就可以大部分場景需求。有兩點需要注意:
a)前景色activeColor
默認是與小程序中success圖標的顏色是相同的,都是「#09BB07」。雖然小程序框架給了開發者修改的自由,但是這個顏色並不能隨便修改。程序中的設計風格要保持一致性,在其它地方,「#09BB07」這個顏色表示完成,在進度條裡也應該作為前景完成色。如果想修改,所有地方都要修改。
在這張圖中共有五個顏色,分別用作小程序中五類信息的顏色。同類信息保持顏色的一致性,有助於向用戶提供清晰明確、主次分明的互動界面。
b)動畫啟動模式active-mode
這個屬性默認為「backwards」,這是不合適的。一個進度條往往指示一件事情的進度,所以最好的動畫啟動模式是「forwards」,即每次從上次結束處開始。
在啟用progress的active動畫後,每走一段都是一段動畫,每段動畫都是基於css的動畫繪製,都有時間。屬性duration用於標識行走1%需要花費的時間,默認值為30毫秒。這個值越大,動畫越細膩。
用於設計的標準屏幕寬度為375px,人類眼睛的動畫覺察閥值是200毫秒,以默認值30毫秒走掉1%計算,200毫秒會走掉大約25px。這個值不能大了,再大的話動畫看起來就不流暢,有卡頓。30毫秒可以視為是一個在體驗上可被允許的最小值。但也不可設置過大,設置大了會影響性能。
2)示例代碼與最佳實踐
wxml:
<progress bindtap="onTapProgressBar" stroke-width="2" percent="{{percentValue}}" active-mode="forwards" active show-info="{{false}}" bindactiveend="onProgressActiveEnd"/>
將寬度設置為2,啟用動畫,從數據源中以變量綁定動畫進度,使用「forwards」作為動畫啟動模式,不顯示百分比數字。其它顏色等,採用默認值。
js:
onTapProgressBar(e){
let progress = this.data.percentValue
if (progress < 100){
progress += 5
this.setData({percentValue:Math.min(100, progress)})
}
}
這個示例的動畫效果與文首的動畫相似,只是為了方便單擊演示,進度條寬度不同。
在progress組件中雖然沒有bindtap這個事情屬性,但tap事件是所有視圖組件的基礎事件,所以在這裡也可以綁定事情句柄。在這個示例中,碼農以onTapProgressBar這個單擊後觸發的函數模擬網絡加載的進度事件。
每單擊一次,進度+5,到100時停止。每次進度值變化,都附有一個動畫。動畫基本是連續和細膩的。但如果加載任務小,時間短,這個動畫也是一掃而過,基本是看不到的。
下面看一下相關問題:
1)如何實現一個下載文件並顯示動態進度條的功能?
有人在開發者社區問到這個問題,他想實現一個下載文件並顯示動態進度條的功能,但看了文檔發現percent這個必須要有固定的值(類似80),但進度是一直變化的,該如何讓它實現動態進度條呢?
示例代碼就可以滿足需求。當啟用active、並將active-mode設置為」forwards」後,動畫就會隨下載進度動起來。
通過文件下載的總大小和已完成大小,可以實時計算出percent數值。需要注意的是,percent屬性是動態綁定的,每次變化後,需要使用setData觸發視圖更新,不然動畫是看不到的。
2)progress已選進度條如何設置圓角?
代碼:
<progress border-radius="5" percent="20" show-info />
使用border-radius可以設置進度條外框的圓角大小,但是無法設置已選進度的圓角,即上面效果中綠色右端的圓角。
官方progress組件沒有提供修改已選進度條圓角值的屬性,有什麼辦法可以修改呢?
有人說,progress組件並不複雜,可以自己基於view組件實現一個。這也是一個辦法,但若實現像progress那樣功能完備的組件,沒有看起來那麼簡單;況且,微信團隊已經做好了一個,直接使用就是了,我們的目的是快速研發產品,沒有必要在一個小組件上浪費太多精力。
小程序界面是基於瀏覽器內核渲染的,這也就是說,所有組件都是有它本身的css樣式的。無奈微信開發者工具只開放了Wxml面板,屏蔽了Elements面板,沒有辦法直接查看progress組件的內部樣式。
我們可以從微信開發者工具的本地源碼中尋找辦法。在下面這個文件中:
~/Library/Application\ Support/微信開發者工具/WeappCode/package.nw/js/vendor/dev/wx-components.css
放置的都是官方小程序組件的樣式定義,其中有這樣一條:
.wx-progress-inner-bar { width: 0; height: 100%;}
它就是progress組件內部已選div的css類樣式。
知道了樣式類名就好辦了。在我們項目中wxss文件中,添加如下樣式:
.wx-progress-inner-bar { border-radius: 5px;}
給已選進度條加一個5px的圓角。看一下效果:
已經有圓角了。
progress本身有一個border-radius屬性,將這個屬性與上面樣式中的border-radius設置成一樣,就可以保證左右圓角一致。
這個方案在手機上測試,也有圓角效果。但它不是正規的路子,如果微信團隊修改了內部樣式類名,那麼這個hack就不好用了。
但對於小微信團隊和個人開發者來講,無所謂了,能達到效果就好了,即使官方有變化,不能再用了,也不過是一個樣式,不影響產品內容的展示。重要的是快速迭代,不在小問題上浪費太多時間。
3)已經加載完的進度條progress怎麼點擊某個按鈕讓它重新加載呢?
在這個使用示例中:
<progress bindtap="onTapProgressBar" stroke-width="2" percent="{{percentValue}}" active-mode="forwards" active show-info="{{false}}" bindactiveend="onProgressActiveEnd"/>
當進度條完成後,直接將percentValue再鎰設置為100,並不能讓動畫重新播放。
有人設想改變兩次,藉助nextTick或延時定時器分別在兩個渲染周期裡設置:
this.setData({ percentValue: 0 });
if (wx.canIUse('nextTick')) {
wx.nextTick(() => {
this.setData({ percentValue: 100 });
});
} else {
setTimeout(() => {
this.setData({ percentValue: 100 });
}, 17);
}
nextTick是基礎2.2.3版本以上支持的,所以這位開發者用了wx.canIUse判斷能不能使用這個Api。如果不能使用,則改用setTimeout設置一個延時定時器。
先將percentValue的值設置為0,過了一個渲染周期或17毫秒,再設置一次。這樣就可以得到動畫重新播放的效果。
其實每一次setData在底層都需要調用evaluateJavascript這個底層函數。這個函數用於邏輯層與視圖層的通訊,它的執行本來就需要時間,並不是馬上可以得到結果。因此,直接使用兩次setData:
<button bindtap="onTapReloadBtn">重新加載</button>
onTapReloadBtn(e){
this.setData({percentValue:0})
this.setData({percentValue:50})
}
也可以達到同樣的效果:
在這裡有一個問題,讀者朋友們請思考一下,為什麼上面setTimeout設置的延時定時器,要使用17毫秒呢?
這是因為目前小程序1秒內最大渲染60幀,每幀渲染約平均花費16.66毫秒,這是一個渲染周期最小的時間單位,17毫秒相當於延時一個nextTick的效果。
4)能否實現一個圓環形進度條呢?
官方的progress組件只支持常規場景,從左向左顯示進度。那麼,如何實現一個類似於這樣的環形進度條呢:
可以用Canvas繪製。
使用Component創建一個自定義組件circle-progress,在組件的wxml代碼裡放置一個canvas:
<view class='canvasBox'>
<view class='bigCircle'></view>
<view class='littleCircle'></view>
<canvas canvas-id="runCanvas" id="runCanvas" class='canvas'></canvas>
</view>
這個id為「runCanvas」的canvas將用於繪製兩個圓圈,下面是灰色的圓,上面是綠色的圓。
在自定義組件中,通過一個percent的屬性用於標識進度:
properties: {
percent: {
type: Number,
value: 50,
observer: function (newVal, oldVal) {
this.draw(newVal);
}
},
},
observer用於監聽屬性變化,當進度增加時,調用draw函數繪製新增的進度條。
在draw函數及後續調用的函數中,計算出需要繪製的弧度及使用Canvas Api arc進行繪製是關鍵:
var num = (2 * Math.PI / 100 * c) - 0.5 * Math.PI;that.ctx2.arc(w, h, w - 8, -0.5 * Math.PI, num)
circle-progress是一個獨立的組件。在使用時,先於json配置中聲明對組件的引用:
{ "usingComponents": { "circle-progress": "../circle-progress/index" }}
「circle-progress」是聲明的名稱。聲明後,在wxml中就可以這樣使用:
{
"usingComponents": {
"circle-progress": "../circle-progress/index"
}
}
在js代碼中模擬網絡改變進度值:
drawProgress(){
if (this.data.percentValue >= 100){
this.setData({
percentValue:0
})
}
this.setData({
percentValue:this.data.percentValue+10
})
}
運行效果就是上面問題起始處的gif圖片的效果。所有源碼在文未階段源碼中可以找到,位於:
miniprogram/pages/2.1/circle-progress
這個組件實現起來不複雜,但有兩點值得注意:
a)當在自定義組件中使用wx.createCanvasContext(canvasId)創建畫布的上下文繪製對象時,需要在第二個參數處傳遞this:
const ctx2 = wx.createCanvasContext(canvasId, this)
這樣才是在組件中查找,不然只是在主文件中查找畫布。
b)使用wx.createSelectorQuery().select(componentId)查找組件對象時,如果在自定義組件中,必須在查找前先調用一下in方法:
const query = wx.createSelectorQuery().in(this)
query.select('#'+id).boundingClientRect((res)=>{
...
}).exec()
不然,這個組件是查找不到的。默認組件查詢也僅是在主文件中查找,不涉及主文件中的子文件。
5)progress 右邊的進度百分比數字的顏色怎麼設置呢?
有兩個方法,最簡單直接的,是直接使用內聯樣式:
<progress percent="40" stroke-width="5" show-info style="color:red"/>
另一種方法,和第2個問題解決圓角的方法類似。就是在這個樣式文件中:
~/Library/Application\ Support/微信開發者工具/WeappCode/package.nw/js/vendor/dev/wx-components.css
找到百比比文字的樣式,然後在自己的wxss文件中將其重寫:
.wx-progress-info {
color: red;
}
現在所有progress組件的百分比文字都是紅色了。
6)progress組件右側的百分比文字,與左邊離得太近了,可否增加一個邊距?
就是感覺兩者離得太近了,想優化一下,這也是社區上有人提的一個問題。
這個問題很簡單,方法同問題5,直接修改樣式就好了。並且這樣的問題,涉及所有組件,最好是修改全局樣式:
.wx-progress-info {
color: red;
margin-left: 5px;
}
一處修改,所有progress組件都有效果了。
好了,我是石橋碼農,今天主要分享關於小程序開發中progress這個組件,以及如何自定義實現一個環形進度條組件。有什麼問題歡迎留言,也歡迎群內討論。
2020年03月27日
階段源碼:略