欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

網(wǎng)易云音樂開發(fā)踩坑之旅 - 新聞資訊 - 云南小程序開發(fā)|云南軟件開發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開發(fā)/軟件開發(fā)

知識

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!

您當(dāng)前位置>首頁 » 新聞資訊 » 小程序相關(guān) >

網(wǎng)易云音樂開發(fā)踩坑之旅

發(fā)表時間:2021-1-11

發(fā)布人:葵宇科技

瀏覽次數(shù):95

問題

最近跟著慕課網(wǎng)上的課程在做一個網(wǎng)易云音樂小程序,遇到了一個進度條回跳的 bug,這里記錄一下踩坑和解決的過程。

具體情況見下圖:

預(yù)期行為:在拖拽進度條之后,直接到達(dá)拖拽之后的位置

實際行為:在拖拽進度條之后,會首先回跳到拖拽之前的位置,然后再跳到拖拽之后的位置。

模擬調(diào)試的 bug

代碼邏輯

無論如何,先來看一下代碼的邏輯:

頁面結(jié)構(gòu)如下,左右兩個 text 顯示時間就不說了,主要是中間的進度條。這個進度條沒有使用小程序原生提供的 slider 來做,而是采用 movable-area 和 movable-view 相結(jié)合的方式,movable-area 劃出了一塊可供滑動的區(qū)域,而 movable-view 則是中間可以拖拽的滑塊。拖拽滑塊的時候會有個 x 來記錄拖拽距離,同時綁定 onXChange 事件監(jiān)聽 x 的變化,綁定 onTouchEnd 事件監(jiān)聽拖拽松手的動作。另外,下面還有一個 progress 組件,這個是用來顯示進度的,已經(jīng)播放的進度給個白色樣式。

<view class="container">
  <text class="time">{{showtime.currentTime}}</text>
  <view class="control">
    <movable-area class="movable-area">
      <movable-view class="movable-view" direction="horizontal" 
      damping="1000" x="{{movableDist}}"
      bindchange="onXchange" bindtouchend="onTouchEnd">  
      </movable-view>  
    </movable-area>
    <progress percent="{{progress}}" stroke-width="4" backgroundColor="#969696" activeColor="#fff"></progress>
  </view>
  <text class="time">{{showtime.totalTime}}</text>  
</view>
復(fù)制代碼

一旦確定 x 的變化來源于用戶的拖拽,就在onXChange 里根據(jù)比例關(guān)系設(shè)置好進度。這里要注意的是,在用戶拖拽沒松手的時候先不進行 setData 渲染視圖層的操作 —— 因為用戶可能會頻繁進行拖拽,我們要避免頻繁的 setData 帶來的性能損耗。所以,這里只是把數(shù)據(jù)保存下來,等待渲染。

onXchange(event){
    if(event.detail.source == "touch"){  
        ratio = event.detail.x / (movableAreaWidth - movableViewWidth) 
        this.data.progress = ratio * 100    
        this.data.movableDist = event.detail.x
    }
},
復(fù)制代碼

用戶一旦松手,基本就可以確定他已經(jīng)把滑塊拖拽到了目標(biāo)位置,這時候就進行正式的 setData 操作,同時調(diào)用 seek 方法讓歌曲跳轉(zhuǎn)到對應(yīng)的位置去播放

onTouchEnd(){      
    let toSec = totalSec * ratio
    this.setData({
        progress:this.data.progress,
        movableDist: this.data.movableDist,
        ['showtime.currentTime']: this.timeFormat(toSec)
    })   
    backgroundAudioManager.seek(toSec)   
},
復(fù)制代碼

目前來看,好像并沒有什么問題。不過別忘了,我們還有一個 onTimeUpdate 在監(jiān)聽歌曲的播放:

backgroundAudioManager.onTimeUpdate(() => {
    let currentTime = backgroundAudioManager.currentTime          
    // 獲取當(dāng)前激活時刻
    let sec = currentTime.toString().split('.')[0]
    // 設(shè)置movableview進度
    let movableDist = (movableAreaWidth - movableViewWidth) * currentTime / totalSec
    // 設(shè)置progress-bar進度
    let progress = 100 * currentTime / totalSec
    // 賦值
    if(compareSec != sec){
        this.setData({
            movableDist,
            progress,
            ['showtime.currentTime']: this.timeFormat(currentTime)
        })
        compareSec = sec
    }
})
復(fù)制代碼

歌曲播放的時候,進度條要跟著走,這個函數(shù)就是用來實現(xiàn)該功能的。

解決方案

問題就在于:拖拽和歌曲播放是同時進行的,這兩者都會對綁定同一個狀態(tài)的數(shù)據(jù)進行修改,可能就是數(shù)據(jù)的沖突導(dǎo)致了最后渲染時回跳的 bug。

解決的方案很簡單,這里參考視頻的做法。其實很像 OS 中的進程互斥(這么說不準(zhǔn)確,但可以近似理解)問題,進度條就相當(dāng)于是互斥資源,我們只要保證一個時間段內(nèi)只有一個操作可以修改進度條就好了。具體做法是聲明一個變量 isMoving 作為“鎖”,在拖拽的時候置為 true,并限制此時 onTimeUpdate 無法修改數(shù)據(jù);而在松手后置為 false,并調(diào)用 seek 跳轉(zhuǎn)到音樂的某個播放位置 A。由于對 onTimeUpdate 來說,他獲取的 currentTime 也是 A 位置對應(yīng)的時間,這樣就不會發(fā)生沖突了。

修改代碼后再來看一下拖拽效果,發(fā)現(xiàn)確實沒有回跳的 bug 了:

你以為事情就這么結(jié)束了嗎?No~~

真機調(diào)試的 bug

在確定模擬調(diào)試沒問題的情況下,我打開手機進行真機調(diào)試,詭異的是,這個 bug 再次出現(xiàn)了,并且機率幾乎是 100%,這怎么能忍呢?于是繼續(xù)想方法解決。

在前面說過,“調(diào)用 seek 跳轉(zhuǎn)到音樂的某個播放位置 A,對于 onTimeUpdate 來說,他獲取的 currentTime 也是 A 位置對應(yīng)的時間?!?在真機調(diào)試的場景下并不是這樣。

延遲更新的問題

我們假設(shè)一下,調(diào)用 seek 進行跳轉(zhuǎn)后,onTimeUpdate 內(nèi)部獲取的 currentTime 不是當(dāng)前時間,而仍然是跳轉(zhuǎn)前的時間,也就是說它的時間沒有更新過來,那么按照這個時間計算的數(shù)據(jù)最后渲染到進度條上,我們看到的就還會是拖拽之前的進度條,而在稍后,時間更新過來了,進度條再次跳回到拖拽之后的位置。如果真的是這樣,或許就可以解釋回跳的原因了。那么怎么驗證呢?

我們可以在 onTimeUpdate 函數(shù)內(nèi)部打印格式化的 currentTimeprogress 的值,如果這兩者保持在差不多的水平,那么可以認(rèn)為它們是同步的,如果某個時刻出現(xiàn)了很大的差距,那么就說明 currentTime 沒有及時進行更新(progress 是通過 onXchange 修改的,不會有問題)。

console.log('currentTime:' + this.timeFormat(backgroundAudioManager.currentTime))
console.log('progress:' + this.data.progress)
復(fù)制代碼

打印結(jié)果見下圖:

一開始沒有拖拽,所以理所當(dāng)然, currentTimeprogress 保持在差不多的水平。然后,注意看紅圈部分,紅圈的時刻我往后拖拽了進度條,所以可以看到 progress 突然變大了,但是這時候的 currentTime 竟然沒有跟著改變(仍然是一個很小的數(shù))!這就驗證了上面的假設(shè)了,因為 currentTime 沒有及時更新,而它又影響著其它數(shù)據(jù),所以導(dǎo)致進度條又跳回到之前的位置,而稍后 currentTime 更新了,所以時間又從 00:07 驟增到 02:11,此后才恢復(fù)正常。

不過,為什么在真機調(diào)試下就會有這個“延遲更新”的問題呢?一開始我還猜想這是因為 seek 是異步的,onTimeUpdate 搶先它執(zhí)行了,但經(jīng)過測試發(fā)現(xiàn)它其實是同步的。所以,或許是因為真機調(diào)試下有延遲?這個先不管了,現(xiàn)在我們先看一下怎么解決這個 bug。

解決方案

問題的根源在于,我們在 onTimeUpdate 中是拿 currentTime 作為標(biāo)準(zhǔn)去進行數(shù)據(jù)修改的,并且認(rèn)定 currentTime 是正確的數(shù)據(jù),但其實,由于延遲更新的問題,這個數(shù)據(jù)有時候是錯誤的。所以我們可以做一個判斷,一旦發(fā)現(xiàn)數(shù)據(jù)是錯誤的(沒更新過來),我們就改用 progress 作為標(biāo)準(zhǔn)去進行數(shù)據(jù)修改(progress 不會出錯)

PS:為什么不統(tǒng)一以 progress 作為標(biāo)準(zhǔn)呢?因為在不拖拽的情況下,progress 是基于 currentTime 進行計算的,所以正常情況還是得用 currentTime

如何判斷數(shù)據(jù)是錯誤的呢?這里用了一個比較笨+不優(yōu)雅的方法:在調(diào)用 onTimeUpdate 的時候,拿到實際的當(dāng)前秒數(shù)以及基于 progress 計算的理想的當(dāng)前秒數(shù)。經(jīng)過測試發(fā)現(xiàn),正常情況下這兩者的偏差不會大于2,而在不正常的情況下(比如截圖紅圈部分),這兩者相差會很大,彼此的差距大概就是我們拖動進度條前后的差距。

這樣,我們就可以把代碼改成:

backgroundAudioManager.onTimeUpdate(() => {
    // 不拖拽的時候才setData
    if(!isMoving){         
        let currentTime = 0
        if(Math.abs(backgroundAudioManager.currentTime - totalSec * this.data.progress/100) < 2){
            console.log('同步')
            currentTime = backgroundAudioManager.currentTime
        } else {
            console.log('不同步')
            currentTime = totalSec * this.data.progress/100     
        }
        // 獲取當(dāng)前激活時刻
        let sec = currentTime.toString().split('.')[0]
        // 設(shè)置movableview進度
        let movableDist = (movableAreaWidth - movableViewWidth) * currentTime / totalSec
        // 設(shè)置progress-bar進度
        let progress = 100 * currentTime / totalSec
        // 賦值
        if(compareSec != sec){
            this.setData({
                movableDist,
                progress,
                ['showtime.currentTime']: this.timeFormat(currentTime)
            })
            compareSec = sec
        }
    }
})
復(fù)制代碼

理論上好像說得過去,實際效果如何呢?真機調(diào)試看一下:

因為我是錄屏然后轉(zhuǎn)成 GIF 的,幀數(shù)比較低,但是經(jīng)過反復(fù)測試,確實沒有進度條回跳的 bug 了。

到這里,bug 就算解決了。當(dāng)然,可能還會有其它更好的解決方式,后續(xù)我會找個時間再看下能不能進行優(yōu)化和改進,有思路的大佬也歡迎留言指點。小程序的坑著實不少,但是我覺得應(yīng)該享受這種踩坑后又從坑里爬出來的感覺。最后要特別感謝群里的 @「瘋子」大佬,多虧他的提醒,讓我定位到問題的關(guān)鍵部位。


相關(guān)案例查看更多