微信小程式Video元件實踐總結
元件,在使用過程中遇到了一些問題,有些問題特別坑,我覺得有必要總結一下,分享出來供有這方面需求的同行參考,本篇文章我主要講解最終我使用的相容性比較好的方案,希望能幫助有需要的童鞋。
我們要實現的佈局

- video上面有個覆蓋video的圖片,在圖片上有個控制播放的按鈕
- video上面有視訊和圖片切換按鈕組合,視訊播放的時候該組合按鈕也必須存在

按照常規的寫法,我們的程式碼結構可能如下
<view> <!-- 視訊 --> <video></video> <!-- 覆蓋視訊的圖片 --> <view> <image/> <!-- 播放按鈕 --> <view></view> </view> <!-- 底部視訊和圖片切換按鈕 --> <view></view> </view> 複製程式碼
然後通過圖片 position:absolute
絕對定位, z-index
層疊上下文來完成佈局,這種方式在ios系統下表現還好,在一些安卓機上圖片和按鈕就會被video元件覆蓋,這是因為 video
元件是原生元件,使用上有一些限制,具體限制可以看這裡微信官方文件,在這裡主要影響我們的原因是,原生元件的層級是最高的,所以頁面中的其他元件無論設定z-index為多少,都無法蓋在原生元件上。
幸好,微信官方提供了 cover-view
與 cover-image
元件,可以覆蓋原生元件上面。通過使用,我們的方案如下:
<view> <video> <cover-view> <!-- 覆蓋圖片 --> <cover-image></cover-image> <!-- 播放按鈕 --> <cover-image></cover-image> </cover-view> </video> </view> 複製程式碼
播放功能
單個視訊,點選播放
有關video元件播放的幾個配置,主要有
- src 要播放視訊的資源地址
- autoplay 是否自動播放
另外,微信還提功能了 const video = wx.createVideoContext()
方法可以獲得一個video例項,通過video.play()方法來控制播放。
故有幾種方案:
第一種方案,拿到視訊地址後,通過 setData
方法更新video元件地址和自動播放為true,該方法優點是由元件自身控制開始播放時間,基本不會存在 黑屏或者視訊加載出來不播放的情況 ,但缺點是切換到其它頁面再回來,每次都需要重新載入視訊,效能和體驗上不是很好。
<!--方案一--> <!--js--> this.setData({ src:url, autoplay:true }) <!--html--> <video src="{{src}}" autoplay="{{autoplay}}"></video> 複製程式碼
第二種方案,src地址通過data傳入,之後無需變化,但存在的問題就是視訊播放的時機,因為視訊地址載入是需要時間的,如果我們在頁面一渲染後就點選播放按鈕,此時如果視訊還未載入完,直接呼叫 videoContext.play()
方法可能會失效,故我們需應該等待視訊載入完之後,才呼叫 play()
方法。這種方案可以只需載入一次視訊,之後播放無需重新載入,使用者體驗較好,另外對於無法控制播放時機問題做了處理,基本不會出現 黑屏 的情況
<!--方案一--> <!--js--> data:{ autoplay:false } const { videoContext } = this.data; <!--視訊播放,關鍵程式碼--> this.setData({ autoplay:true },()=>{ videoContext.play() }) <!--html--> <video src="src" autoplay="{{autoplay}}"></video> 複製程式碼
單個視訊,wifi下自動播放
微信提供判斷網路環境的api,通過api判斷,如果是wifi下,就主動呼叫 videoContext.play()
方法。
wx.getNetworkType({ success(res) { const networkType = res.networkType; if (networkType === 'wifi') { that.videoPlay(); } }, }); 複製程式碼
視訊不在可視範圍,停止播放
微信提供 IntersectionObserver
物件,用於推斷某些節點是否可以被使用者看見、有多大比例可以被使用者看見,以前判斷元素是否在檢視內,我們常用的是 getBoundingClientRect
,該方法在微信環境也能使用,但該方法因為需要計算元素位置,引起頁面重繪,比較影響效能。而 IntersectionObserver
是原生自己判斷位置,不會有相關問題,可以通過這個api來監聽元素位置,從而控制是否播放。
wx.createIntersectionObserver(that) .relativeToViewport() .observe('.video--wrap', res => { if (res && res.intersectionRatio > 0) { that.videoPlay(); } else { that.videoPause(); } }); 複製程式碼
視訊列表
一開始我是用過 wx:for
列舉視訊播放元件,子元件播放,觸發一個事件,父元件監聽子元件事件,更新視訊列表值,然後子元件監聽屬性變化來更新播放狀態的,該方案在ios上表現不錯,可是放在安卓上, 頁面竟然不能滾動了 ,後面通過查閱資料,video列表元件不能直接像普通圖片列表一樣,真正的方案是需要用視訊首幀圖片做列表,然後點選首幀圖片,跳轉到一個新頁面,或者一個蒙層播放視訊。
小程式左滑右滑實現
我們需求上還有左右滑切換圖片和視訊顯示的功能,這需要通過監聽 touchstart
和 touchmove
來判斷手機滑動方向,從而切換視訊和圖片的顯示位置。
touchstart: function(e) { this.setData({ startX: e.changedTouches[0] && e.changedTouches[0].clientX, startY: e.changedTouches[0] && e.changedTouches[0].clientY, }); }, touchmove: function(e) { var that = this, startX = that.data.startX, //開始X座標 startY = that.data.startY, //開始Y座標 touchMoveX = e.changedTouches[0].clientX, //滑動變化座標 touchMoveY = e.changedTouches[0].clientY, //滑動變化座標 }, //根據起點和終點返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑動 GetSlideDrection: function(startX, startY, endX, endY) { var dy = endY - startY; var dx = endX - startX; var result = 0; //如果滑動距離太短 if (Math.abs(dx) < 2 && Math.abs(dy) < 2) { return result; } var angle = this.GetSlideAngle(dx, dy); if (angle >= -45 && angle < 45) { result = 4; } else if (angle >= 45 && angle < 135) { result = 1; } else if (angle >= -135 && angle < -45) { result = 2; } else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) { result = 3; } return result; }, GetSlideAngle: function(dx, dy) { return (Math.atan2(dy, dx) * 180) / Math.PI; }, 複製程式碼
以上方案都可以通過小程式程式碼片段進行檢視
除錯
開發者工具和真機表現差異很大,故在開發者工具上開發完之後,需要多在不同真機上進行除錯。
總結
實際我在開發中碰到的問題還挺多,小程式video元件坑還是挺多的,我沒有一一描述出來,如果你有碰到其它問題,歡迎留言評論,如果我曾經碰到過,或許能夠提供一些意見,我寫這篇文章的目的就是讓更多的小程式開發者儘量少踩坑,如果剛好能夠幫助到你,感謝點贊支援!