如何用JS開發自定義播放欄的視頻播放器
效果圖
原始控件效果圖:

修改后的效果圖:
看過了演示效果,接下來講解如何開發。
開發部分
在開發之前,我們要先翻閱文檔,了解系統提供的能力。

我們從屬性、事件、方法中可以得出,以現在提供的能力,完全可以重寫視頻的控制欄。
接下來就是界面的構思和布局:
界面布局
1. 整體布局
整體界面分為:視頻播放、視頻列表、評論區三大部分。
所以我們代碼上先構建三個div。

2.video區域布局
想要把控制欄覆蓋到video上面我們就需要借助stack組件,如下圖:

視頻控制欄我們分為上下兩個部分,所以需要構建兩個div來包裹里面控件,如下圖:

再往下就是控制欄上細節上的控件了,包括文字、按鈕、滑動條等,在此就不進行贅述了。
3.視頻列表區域及評論區域
視頻列表區域和評論區域排版相對比較簡單,一個標題欄將顯示區域進行分割,再一個list列表顯示內容。
評論區域則多一欄評論功能,其結構排版如圖:

整個排版結構大致介紹完畢,以下為hml頁面的全部代碼供參考:
- <div class="container">
- <!--播放區域-->
- <div class="video">
- <!--播放區域-->
- <div class="video-play" show="{{!isShowController}}">
- <stack>
- <video if="{{resetVideo}}" speed="{{playSpeed}}" @touchmove="videoTouchMoved" direction="horizontal" @error="resetVideo" id="m_video" src="{{playedVideo.src}}" @timeupdate="timeChanged" @prepared="mPrepared" @start="mStart" @pause="mStop" @finish="mStop" direction="horizontal" poster="{{playedVideo.image}}" @touchend="toolsTouched" controls="false"></video>
- <!--工具欄,當觸發屏幕時顯示,n秒后自動隱藏-->
- <div class="vide-tools" show="{{ showToolsTime>0 }}" @touchend="toolsTouched" @touchmove="videoTouchMoved">
- <div class="video-tools_child tools-top" @touchstart="childToolTouched">
- <div class="tools-left">
- <!--標題-->
- <text class="title">{{playedVideo.name}}</text>
- </div>
- <div class="tools-top-right">
- <!--流轉-->
- <image class="control_button" @touchend="remoteIt" src="../../common/images/ic_hop.svg"></image >
- </div>
- </div>
- <div class="video-tools_child tools-bottom" @touchstart="childToolTouched">
- <div class="tools-left">
- <!--播放/暫停-->
- <button @touchend="playOrPause" class="tools-btn">{{isPlayed?"‖":"▷"}}</button>
- <!--當前播放時長-->
- <text class="duration-text">{{thisTimeStr}}</text>
- <!--滑塊-->
- <slider class="v-slider" step="1" max="{{ totalTime }}" value="{{thisTime}}" @change="sliderChanged"></slider>
- <!--總播放時長-->
- <text class="duration-text">{{totalTimeStr}}</text>
- </div>
- <div class="tools-right">
- <!--倍速-->
- <button id="speedBtn" @touchend="showSpeedMenu" class="tools-btn btn-speed">{{speedStr}}</button>
- <popup id="speedPopup" target="speedBtn" placement="top" >
- <div>
- <text class="speed-child" @touchend="{{speedChanged(2.0)}}">2.0X</text>
- <text class="speed-child" @touchend="{{speedChanged(1.5)}}">1.5X</text>
- <text class="speed-child" @touchend="{{speedChanged(1.0)}}">1.0X</text>
- <text class="speed-child" @touchend="{{speedChanged(0.5)}}">0.5X</text>
- </div>
- </popup>
- <!--全屏-->
- <button class="tools-btn" @touchend="showAllScreen">{{isAllScreen?"╬":"▞"}}</button>
- </div>
- </div>
- </div>
- </stack>
- </div>
- </div>
- <!--視頻列表-->
- <div class="video-lists">
- <text class="card-title">視頻列表:</text>
- <list initialindex="{{playIndex}}" selected="{{playIndex}}" scrollbar="on" >
- <list-item for="{{ videoSource }}" @touchmove="listTouchMoved" @touchend="{{checkVideo($idx)}}" >
- <div class="preview-video">
- <image src="{{$item.image}}"></image>
- <text>{{$item.name}}</text>
- </div>
- </list-item>
- </list>
- </div>
- <!-- 評論區 -->
- <div class="comments">
- <text class="card-title">評論區:</text>
- <!--評論列表-->
- <list divider="true">
- <list-item for="thisComments">
- <div class="comment-info">
- <!--用戶信息-->
- <div class="comment-user">
- <label>用戶:{{$item.user}}</label>
- </div>
- <!--內容-->
- <div class="comment-content">
- <text>{{$item.content}}</text>
- </div>
- </div>
- </list-item>
- </list>
- <div class="comment-put">
- <input id="commentMsg" @change="commentChanged" value="{{ commentContent }}" placeholder="請發表你的想法"></input>
- <button class="btn" @touchend="sendComment">發 布</button>
- </div>
- </div>
- </div>
當然僅有hml是不夠的,還需要樣式的配合,樣式相關文件請參考gitee:https://gitee.com/panda-coder/harmonyos-apps/tree/master/Player。
邏輯控制-js
邏輯控制是個復雜的過程,每個人都有不同的實現方式,不可能細講,就挑一部分進行講解。
1.實現控制欄的功能
要想實現控制欄的播放/暫停、滑動條、全屏等基礎功能需要依賴video控件的方法。只需要執行對應函數即可。
- this.$element(控件id).方法(參數)
2.computed
computed是一個非常好用的屬性方法。computed內的函數能在hml中直接使用,并且只要在computed函數的data數據改變就會觸發重新計算。
例如:
- computed:{
- playedVideo(){//當前播放視頻
- return this.videoSource[this.playIndex]
- },
- }
獲取當前播放視頻的數據,在上述方法中,this.videoSource或this.playIndex其中任何一個值進行修改,都會觸發playedVideo函數進行重新計算。
這樣處理一些實時變化的數據就非常的方便。代碼中的totalTimeStr、thisTimeStr、speedStr這些值就是通過計算來返回一個格式化的文字內容。
3.$watch
在視頻中有個功能,就是點擊屏幕后顯示控制欄,不操作間隔5s后控制欄自動消失,但是點擊非控制欄部分直接消失,點擊控制欄部分刷新時間間隔為5s。
大家可以先思考一下該怎么去設計這部分的邏輯控制,以及需要的函數邏輯操作等再往下看。
我使用$watch部分對showToolsTime 參數進行監聽。
$watch是當監聽的值改變后執行回調函數,并回傳監聽值變化前幾變化后的值。
在onInit函數中監聽showToolsTime。
- onInit() {
- this.$watch("showToolsTime",'watchShowToolsTimeEvent')
- this.videoSource=data.videoSource
- this.commentsSource=data.comments
- this.playIndex=0;
- this.showToolsTime=5;
- },
- /監聽控制欄顯示時間
- watchShowToolsTimeEvent(newV,oldV){
- if(newV>0){
- clearTimeout(this.watchTimer)
- this.watchTimer=setTimeout(()=>{
- if(newV>oldV)
- this.showToolsTime=newV
- --this.showToolsTime;
- },1000)
- }else{
- this.$element("speedPopup").hide()
- }
- },
在watchShowToolsTimeEvent中有兩個關鍵點,一個是clearTimeout,另一個是返回值不能與newV相同。
js中的setTimeOut是異步執行,不清理掉timer(setTimeOut)在外部賦值后就會導致數據異步賦值問題。
返回值與newV一致$watch就失效了(除非外部激發),所以執行了this.showToolsTime=newV后也要執行–this.showToolsTime。
這樣不管任何地方修改this.showToolsTime的值之后,都會進行每秒減1的運算(大于0的情況下)。
4.分布式能力
分布式能力參考官方文檔,無特殊處理。
文檔:https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-distributed-migration-0000001050024965
5.左右滑動切換視頻
首先通過touchmove事件先收集手指滑動的坐標。
然后在touchend事件中判斷手指滑動方向。
判斷代碼如下:
- getTouchMoveOrientation(source,num){
- let orientation=[]
- for(let i=1;i<source.length;i++){
- let startX=source[i-1].localX
- let startY=source[i-1].localY
- let moveEndX=source[i].localX
- let moveEndY=source[i].localY
- let X=moveEndX-startX;
- let Y=moveEndY-startY;
- if(Math.abs(X)>Math.abs(Y) && X>0){
- orientation.push("left2right")
- }else if(Math.abs(X)>Math.abs(Y) && X<0){
- orientation.push("right2left")
- }else if(Math.abs(X)<Math.abs(Y) && Y>0){
- orientation.push("top2bottom")
- }else if(Math.abs(X)<Math.abs(Y) && Y<0){
- orientation.push("bottom2top")
- }
- }
- let obj={},maxNum=0
- orientation.forEach((item,index)=>{
- if(orientation.indexOf(item)==index){
- obj[item]=1
- }else{
- obj[item]+=1
- }
- })
- for(let i in obj){
- if(obj[i]>maxNum){
- maxNum=obj[i]
- }
- }
- if(maxNum<num)
- return "none"
- for(let i in obj){
- if(obj[i]==maxNum)
- return i
- }
- },
先收集滑動方向,然后再找出滑動方向最多的值,再和**閾值(num)**進行比較,超過閾值則返回滑動方向信息,否則返回“none”。
接下來再根據返回的滑動方向進行一系列操作(如:視頻切換則改變this.playedIndexed值)。
接下來談談不足和感受:
1.list控件在滑動的過程中會觸發觸摸事件,需要開發者自行處理,不友好。
2.當video全屏時暫未找到怎么顯示我寫的自定義控件欄。
3.js組件開發暫無引入三方包功能(僅能引入純js),Java有對應的har,js的三方組件庫還未見引入身影。
4.為什么會提到js的三方組件庫尼,就是因為原生的組件不夠美觀。想通過引入三方庫來構建不同體系的組件(可能只是改改樣式)希望盡快安排相關文檔。