開(kāi)源三個(gè)UniApp組件:倍速播放的音頻播放器、網(wǎng)絡(luò)重試、分詞大爆炸

youzack是我開(kāi)發(fā)的一個(gè)能進(jìn)行英語(yǔ)聽(tīng)力逐句精聽(tīng)訓(xùn)練以及背單詞的網(wǎng)站,為了方便移動(dòng)端的使用,我使用Uni-App來(lái)進(jìn)行youzack移動(dòng)端的開(kāi)發(fā),因?yàn)閁ni-App不僅可以用來(lái)開(kāi)發(fā)微信小程序,也可以打包為App。
在開(kāi)發(fā)的過(guò)程中,我封裝了三個(gè)大家可能用到的Uni-App組件,他們分別是:支持音頻倍速播放以及自定義UI的音頻播放器yz-audio、支持自動(dòng)網(wǎng)絡(luò)重試請(qǐng)求的zackRetrier、支持類(lèi)似于錘子手機(jī)的“大爆炸分詞”的yz-text。
源碼地址: https://github.com/yangzhongke/uniapp-youzack-components
下面分別來(lái)介紹他們的用法以及實(shí)現(xiàn)原理:
一、倍速音頻播放器yz-audio
youzack中需要播放聽(tīng)力音頻,Uni-App有<audio>組件,但是有如下缺點(diǎn):不被推薦使用了;不支持變速播放功能;界面不能自定義。因此我開(kāi)發(fā)了yz-audio這個(gè)組件。
插件地址:https://ext.dcloud.net.cn/plugin?id=4246

?
yz-audio特點(diǎn):支持音頻的變速播放;界面中內(nèi)置了三個(gè)可以自定義內(nèi)容的文本區(qū)域,可以響應(yīng)點(diǎn)擊事件,也可以使用slot自定義UI;
基本使用方式:
<yz-audio ref="player1">
</yz-audio>
在代碼中播放以及設(shè)置名字、海報(bào)圖片等:
var player1 = this.$refs.player1;?? player1.setSrc(
"https://vkceyugu.cdn.bspapp.com/VKCEYUGU-hello-uniapp/2cc220e0-c27a-11ea-9dfb-6da8e309e0d8.mp3");???????
player1.setPoster("https://vkceyugu.cdn.bspapp.com/VKCEYUGU-uni-app-doc/7fbf26a0-4f4a-11eb-b680-7980c8a877b8.png");//海報(bào)圖片
player1.setSinger("貝多芬");//設(shè)置歌手
player1.setName("致愛(ài)麗絲");//設(shè)置作品名字

更多用法詳見(jiàn)?https://ext.dcloud.net.cn/plugin?id=4246
?注意事項(xiàng):
1、 只有video組件支持倍速播放,所以這個(gè)組件是采用的video來(lái)播放音頻,因此支持的音頻文件格式取決于video組件的支持情況;
2、 由于VideoContext的內(nèi)部實(shí)現(xiàn)的未知原因,不能在play方法之前或者之后立即調(diào)用playbackRate來(lái)修改倍速,否則很可能不會(huì)生效。最好由用戶(hù)觸發(fā)的動(dòng)作來(lái)調(diào)用playbackRate,如果必須在play方法前后調(diào)用,建議通過(guò)setTimeout設(shè)置一個(gè)延時(shí)來(lái)執(zhí)行playbackRate。
比如如下是不行的:
var player1 = this.$refs.player1;??
player1.setSrc("https://www.test.com/test.mp3");
player1.playbackRate(0.5);
可以如下修改:
var player1 = this.$refs.player1;??
player1.setSrc("https://www.test.com/test.mp3");
setTimeout(function(){player1.playbackRate(0.5);},500);
3、 由于組件是使用video來(lái)播放音頻,因此具有video的所有缺點(diǎn),比如不能實(shí)現(xiàn)后臺(tái)播放。我曾經(jīng)想換用getBackgroundAudioManager()來(lái)替換video,但是發(fā)現(xiàn)BackgroundAudioManager響應(yīng)速度很慢,無(wú)法實(shí)現(xiàn)精細(xì)、及時(shí)的進(jìn)度控制。
4、 如果需要音頻的鎖屏播放,可以在需要后臺(tái)播放的時(shí)候(需要用戶(hù)操作觸發(fā)),暫停yz-audio的播放,然后再調(diào)用getBackgroundAudioManager實(shí)現(xiàn)后臺(tái)播放。
5、 如果仔細(xì)觀察播放器,你可能會(huì)發(fā)現(xiàn)右上角有一個(gè)小黑點(diǎn),那個(gè)其實(shí)就是一個(gè)尺寸非常小的video組件。但是這個(gè)video組件不能完全隱藏,否則在ios下將會(huì)無(wú)法播放。
6、 由于播放器屬于個(gè)性化需求非常強(qiáng)的組件,眾口難調(diào),因此這個(gè)組件很難完全滿足所有人的要求,因此這個(gè)組件我只是開(kāi)放源代碼,各位可以根據(jù)自己的需要修改,我將不會(huì)進(jìn)行后續(xù)維護(hù)、新需求增加等。
?
二、網(wǎng)絡(luò)請(qǐng)求重試zackRetrier
在網(wǎng)絡(luò)信號(hào)不強(qiáng)等情況下,網(wǎng)絡(luò)請(qǐng)求可能會(huì)失敗,因此我開(kāi)發(fā)了一個(gè)能自動(dòng)對(duì)失敗的請(qǐng)求進(jìn)行重試的組件zackRetrier,如果N次重試都失敗了,還會(huì)彈出對(duì)話框問(wèn)用戶(hù)“是否再次重試”。在請(qǐng)求處理期間會(huì)自動(dòng)顯示loading提示。
插件地址:https://ext.dcloud.net.cn/plugin?id=4247

用法:
首先引入組件import {zRetrier} from "@/js_sdk/yz-zackRetrier/zackRetrier/index.js";
調(diào)用發(fā)送網(wǎng)絡(luò)請(qǐng)求:zRetrier({url:"https://api.youzack.com/test"}),zRetrier函數(shù)返回的是Promise對(duì)象,所以既可以用await的方式獲取調(diào)用結(jié)果,也可以使用then()方式。返回值是一個(gè)長(zhǎng)度為2的數(shù)組,數(shù)組的第一個(gè)元素是錯(cuò)誤對(duì)象,如果調(diào)用成功了,則第一個(gè)元素是null,數(shù)組的第二個(gè)元素是服務(wù)器端返回的響應(yīng)。
?
示例代碼1
zRetrier({url:"https://api.youzack.com/test",manualRetryContent:"登錄失敗,是否重試"})
.then(res=>{
?????? let err = res[0];
?????? let resp = res[1];
?????? if(err)
?????? {
????????????? uni.showModal({??????????????????????????????
???????????????????? content:"調(diào)用失敗"+err.errMsg
????????????? });
????????????? return;
?????? }
?????? uni.showModal({
????????????? content:"調(diào)用成功"+resp
?????? });
});
?
示例代碼2:
let err1,resp1;
[err1,resp1] = await zRetrier({url:"https://api.youzack.com/test",manualRetryContent:"登錄失敗,是否重試"});
if(err1)
{
?????? uni.showModal({??????????????????????????????
????????????? content:"調(diào)用失敗"+err1.errMsg
?????? });
?????? return;
}
uni.showModal({
?????? content:"調(diào)用成功"+resp1
});
zRetrier函數(shù)只有一個(gè)參數(shù),這個(gè)參數(shù)會(huì)完整的傳遞給內(nèi)部封裝的uni.request方法,所以如果想設(shè)定method、headers等,用法和uni.request一樣。除此之外,zRetrier的參數(shù)還有其他屬性。
參數(shù):
1)????? retryTimes:整數(shù)類(lèi)型。代表一次失敗之后,最多自動(dòng)重試retryTimes次,默認(rèn)值是3。
2)????? retryWaitTime:整數(shù)類(lèi)型,代表每次自動(dòng)重試之前等待的毫秒數(shù)。默認(rèn)值是200。
3)????? autoLoading:boolean類(lèi)型,代表請(qǐng)求期間是否自動(dòng)顯示loading,默認(rèn)值是true。
4)????? manualRetryContent:string類(lèi)型。如果設(shè)置了manualRetryContent的值,則在retryTimes次自動(dòng)重試都失敗后,會(huì)顯示重試對(duì)話框,然后對(duì)話框中顯示manualRetryContent的內(nèi)容。對(duì)話框中有【取消】、【重試】?jī)蓚€(gè)按鈕,如果用戶(hù)點(diǎn)擊了【重試】按鈕之后,會(huì)再自動(dòng)最大重試retryTimes次。
感謝C#中發(fā)明了await、async關(guān)鍵字極大的簡(jiǎn)化了異步編程,并且讓JavaScript從C#中把這兩個(gè)關(guān)鍵字借鑒了過(guò)來(lái),最終能讓我實(shí)現(xiàn)這個(gè)組件起來(lái)太簡(jiǎn)單了。
注意事項(xiàng):由于這個(gè)組件可能多次重試后端接口,所有后端的接口需要是冪等的,也就是調(diào)用一次和調(diào)用N次的效果應(yīng)該是一樣的。
三、允許自定義選擇菜單的“分詞大爆炸”yz-text
為了方便用戶(hù)直接查詢(xún)例句中某個(gè)單詞或者詞組的中文翻譯,youzack小程序中需要允許用戶(hù)對(duì)于界面中的英文選中進(jìn)行查詢(xún)。微信小程序中,可以對(duì)于<text>設(shè)定user-select=”true”來(lái)允許用戶(hù)自由選擇其中的文字,在Android手機(jī)中,選中文字會(huì)彈出一個(gè)只帶【復(fù)制】一個(gè)菜單項(xiàng)的菜單,在蘋(píng)果手機(jī)中,選中文字會(huì)彈出一個(gè)帶【查詢(xún)】等菜單項(xiàng)的菜單。但是這個(gè)菜單中是無(wú)法自定義菜單項(xiàng)的。
我想監(jiān)聽(tīng)剪切板中內(nèi)容的變化,來(lái)實(shí)現(xiàn)“用戶(hù)復(fù)制單詞后自動(dòng)查詢(xún)單詞”的功能。但是微信小程序中沒(méi)有提供監(jiān)聽(tīng)剪切板內(nèi)容變化的API,所以只能用定時(shí)器監(jiān)聽(tīng)剪切板中的內(nèi)容。發(fā)布后,有用戶(hù)反映,在小米的某些手機(jī)上,手機(jī)系統(tǒng)會(huì)頻繁的提示“應(yīng)用正在讀取剪切板中的內(nèi)容”,非常影響使用。這個(gè)可以理解,為了保護(hù)用戶(hù)隱私,避免很多應(yīng)用通過(guò)監(jiān)聽(tīng)剪切板來(lái)偷窺用戶(hù)的內(nèi)容,因此新版的IOS和Android中都會(huì)做出類(lèi)似的提示。
因此我換了另外一種思路,我借鑒錘子手機(jī)中“分詞大爆炸”的效果,在用戶(hù)長(zhǎng)按一段文字后,彈出對(duì)話框,在對(duì)話框中對(duì)于文字進(jìn)行分詞,然后允許用戶(hù)選中一個(gè)或者多個(gè)單詞,然后進(jìn)行查詢(xún)。這樣不僅規(guī)避了問(wèn)題,而且用戶(hù)的使用體驗(yàn)更好,真是無(wú)心插柳柳成蔭呀。

插件地址:https://ext.dcloud.net.cn/plugin?id=4249
組件用法:
<yz-text text="I love you, I've get set !"></yz-text>
text屬性中就是要顯示的文字,長(zhǎng)按文字后就會(huì)彈出分詞對(duì)話框。
?更多用法詳見(jiàn)?https://ext.dcloud.net.cn/plugin?id=4249
我主要做后端開(kāi)發(fā),Uni-App等這些都是做這個(gè)小程序時(shí)候才現(xiàn)學(xué)的,所以如果有做的不好的地方,請(qǐng)指正,謝謝。