算了不回顧了
直接搞起,打開JS1中寫的bvd.jscss
視頻開始播放
當點擊播放按鈕的時候,播放按鈕將會隱藏,播放視頻,這個不難,在JS1中咱們就已經實現。但咱們改變一下思惟,給視頻添加點擊tap事件,使視頻播放,再觸發播放事件,從而讓播放按鈕隱藏html
pro.initEvent = function(){ var that = this; //給播放按鈕圖片添加事件 this.vimg.addEventListener('tap',function(){ that.video.play(); }) //視頻點擊暫停或播放事件 this.video.addEventListener('tap',function(){ if(this.paused || this.ended) { //暫停時點擊就播放 if(this.ended) {//若是播放完畢,就重頭開始播放 this.currentTime = 0; } this.play(); } else { //播放時點擊就暫停 this.pause(); } }) //視頻播放事件 this.video.addEventListener('play',function(){ that.vimg.style.display = 'none'; }) //獲取到元數據 this.video.addEventListener('loadedmetadata',function(){ that.vC.querySelector('.duration').innerHTML = stom(this.duration); }); }
下方控制條漸漸隱藏
隱藏並非難點,重要的是漸漸的隱藏,在這裏咱們有這麼幾種解決方案:html5
在這裏咱們2種結合起來使用android
首先咱們先定義好一組動畫ios
@keyframes vhide {0% {opacity: 1;}100% {opacity: 0;}} @-webkit-keyframes vhide {0% {opacity: 1;}100% {opacity: 0;}} .vhidden { animation: vhide 3.5s ease-in; -webkit-animation: vhide 3.5s ease-in; }
其做用就是透明度3.5秒內1=>0,ease-in 就是 由慢到快 的過分效果。有不懂css動畫能夠問問度娘哦
而後咱們給視頻開始播放事件的時候給控制條添加vhidden樣式類css3
//視頻播放事件 this.video.addEventListener('play',function(){ that.vC.classList.add('vhidden'); })
測試效果,果真3.5s內,控制條 慢慢透明,問題是3.5s後,透明度又回到了1,這裏我講解一下,是由於動畫幀默認是回彈的,咱們能夠加個樣式git
.vhidden { animation: vhide 3.5s ease-in; -webkit-animation: vhide 3.5s ease-in; animation-fill-mode:forwards; -webkit-animation-fill-mode: forwards; }
CSS3 屬性 animation-fill-mode 用來定義元素在動畫結束後的樣子。web
animation-fill-mode 的默認值是 none,也就是在動畫結束以後不作任何改動,若是把animation-fill-mode 改爲 forwards 則動畫結束後元素的樣式會變成動畫最後一個關鍵幀所規定的樣式。vim
加上這個樣式後,果真3.5s後,動畫再也不回彈了,可是這裏要留意一下,控制條並非不在了而是透明瞭,若是這時咱們有寫控制條的點擊時間,那麼在控制條位置點擊,仍是會觸發事件,因此呢,咱們還能夠寫上一段setTimeout來,讓控制條3.5s後隱藏,這個你們能夠自行取捨segmentfault
//視頻播放事件 this.video.addEventListener('play',function(){ that.vimg.style.display = 'none'; that.vC.classList.add('vhidden'); that.vCt = setTimeout(function(){ that.vC.style.visibility = 'hidden'; },3400); })
爲何動畫過程是3.5s,然而js是是3.4s後執行,這裏只是在未寫animation-fill-mode:forwards的狀況下作個保險
嘿嘿,視頻能夠播放啦!那麼如今咱們該考慮一下播放中有哪些事要作呢?
1. 控制條進度條慢慢增加
咱們須要給視頻添加一條timeupdate音視頻播放位置發生改變時的事件
咱們先在獲取視頻元數據事件中,把視頻的長度給拿下來
//獲取到元數據 this.video.addEventListener('loadedmetadata',function(){ that.vDuration = this.duration; that.vC.querySelector('.duration').innerHTML = stom(that.vDuration); });
再從視頻播放進度更新事件中計算比例,設置進度條的寬度
//視頻播放中事件 this.video.addEventListener('timeupdate', function() { var currentPos = this.currentTime;//獲取當前播放的位置 //更新進度條 var percentage = 100 * currentPos / that.vDuration; //設置寬度 that.vC.querySelector('.timeBar').style.width = percentage + '%'; });
能夠看到咱們的進度條君愈來愈膨脹了。
2. 當前播放時間變化
同時,咱們的當前播放時間顯示也在timeupdate事件中設置
//視頻播放中事件 this.video.addEventListener('timeupdate', function() { var currentPos = this.currentTime;//獲取當前播放的位置 //更新進度條 var percentage = 100 * currentPos / that.vDuration; that.vC.querySelector('.timeBar').style.width = percentage + '%'; //更新當前播放時間 that.vC.querySelector('.current').innerHTML = stom(currentPos); });
當咱們點擊視頻時,若是是暫停,那就開始播放,並觸發播放事件,反之視頻在播放中,點擊視頻就會暫停,並觸發暫停事件。
0. 時間定格
啦啦啦,暫停播放,timeupdate事件天然就不觸發啦,因此進度條和當前播放時間就不會變啦。
1. 播放按鈕顯示
在暫停的時候,顯示出按鈕就行啦
//暫停or中止 this.video.addEventListener('pause',function(){ that.vimg.style.display = 'block'; });
2. 下方控制條顯示
控制條顯示,直接去除那個vhidden樣式類就好啦
//暫停or中止 this.video.addEventListener('pause',function(){ that.vimg.style.display = 'block'; that.vC.classList.remove('vhidden'); that.vC.style.visibility = 'visible'; });
這樣寫看樣子是沒錯啦,可是,若是你們在以前隱藏控制條的時候寫了setTimeout的話,這個時候就要清除掉哦。
//暫停or中止 this.video.addEventListener('pause',function(){ that.vimg.style.display = 'block'; that.vC.classList.remove('vhidden'); that.vC.style.visibility = 'visible'; that.vCt && clearTimeout(that.vCt); });
一個叼叼噠的小視頻播放器怎麼可能少的了可進可退能屈能伸呢?
來,咱們先爲video添加左滑右滑事件
//視頻手勢右滑動事件 this.video.addEventListener('swiperight',function(e){ this.currentTime += 5; }); //視頻手勢左滑動事件 this.video.addEventListener('swipeleft',function(e){ this.currentTime -= 5; });
可能在電腦上調試會直接進度變0,一開始我也納悶呢,後來發現手機上webview中好像是可行的。
關於 進度條拖動改變視頻進度 我暫時不打算寫,由於我還沒寫。
可能你們會比較關注這個吧:
ios端:去除video標籤webkit-playsinline屬性便可,由於ios對h5的video標籤支持仍是比較不錯的
//調用原生方式 全屏播放 pro.nativeMax = function(){ if(!window.plus){ //非html5+環境 return; } if($.os.ios){ console.log('ios') this.video.removeAttribute('webkit-playsinline'); }else if($.os.android){ console.log('android'); var url = this.video.querySelector('source').src; var Intent = plus.android.importClass("android.content.Intent"); var Uri = plus.android.importClass("android.net.Uri"); var main = plus.android.runtimeMainActivity(); var intent = new Intent(Intent.ACTION_VIEW); var uri = Uri.parse(url); intent.setDataAndType(uri, "video/*"); main.startActivity(intent); } }
在initEvent中添加點擊 全屏 事件
this.vC.querySelector('.fill').addEventListener('tap',function(){ that.nativeMax(); });
這樣作有點雞肋啊,就不能來點通用的?
確實這個問題我想了一夜,決定再拿點乾貨來。
先給個狀態,默認爲mini播放
var bvd = function(dom) { var that = this; $.ready(function() { //獲取視頻元素 that.video = document.querySelector(dom || 'video'); //獲取視頻父元素 that.vRoom = that.video.parentNode; //元素初始化 that.initEm(); //事件初始化 that.initEvent(); //記錄信息 that.initInfo(); //當前播放模式 false 爲 mini播放 that.isMax = false; }) } //記錄信息 pro.initInfo = function() { var that = this; //在onload狀態下,offsetHeight纔會獲取到正確的值 window.onload = function(){ that.miniInfo = {//mini狀態時的樣式 width: that.video.offsetWidth + 'px', height: that.video.offsetHeight + 'px', position: that.vRoom.style.position, transform: 'translate(0,0) rotate(0deg)' } var info = [ document.documentElement.clientWidth || document.body.clientWidth, document.documentElement.clientHeight || document.body.clientHeigth ], w = info[0], h = info[1], cha = Math.abs(h - w) / 2; that.maxInfo = {//max狀態時的樣式 width: h + 'px', height: w + 'px', position: 'fixed', transform: 'translate(-' + cha + 'px,' + cha + 'px) rotate(90deg)' } } } //全屏 mini 兩種模式切換 pro.switch = function() { var vR = this.vRoom; //獲取須要轉換的樣式信息 var info = this.isMax ? this.miniInfo : this.maxInfo; for(var i in info) { vR.style[i] = info[i]; } this.isMax = !this.isMax; } //全屏按鈕 this.vC.querySelector('.fill').addEventListener('tap', function() { //that.nativeMax(); that.switch(); });
瞧一瞧拉,看一看拉
看起來感受很不錯呢,利用css3的位移和旋轉,讓視頻全屏在了屏幕前,可是問題也隨之而來了
css
.bad-video { position: relative; /*overflow: hidden;*/ background-color: #CCCCCC; }
js
max配置當中,設置zIndex值
that.maxInfo = {//max狀態時的樣式 zIndex:99, width: h + 'px', height: w + 'px', position: 'fixed', transform: 'translate(-' + cha + 'px,' + cha + 'px) rotate(90deg)' }
//視頻手勢右滑動事件 this.video.addEventListener('swiperight', function(e) { console.log('right'); this.currentTime += 5; }); //視頻手勢左滑動事件 this.video.addEventListener('swipeleft', function(e) { console.log('left'); this.currentTime -= 5; });
這TM就很尷尬了,難道我全屏後,手機橫放,還去上下快進快退?
這時候怎麼辦呢,不要方
咱們先給video註冊一個事件列表
var events = {}; //增長 或者刪除事件 pro.eve = function(ename, callback, isF) { if(callback && typeof(callback) == 'function') { isF && arguments.callee(ename); events[ename] = callback; this.video.addEventListener(ename, events[ename]); console.log('添加事件:' + ename); return; } var fun = events[ename] || function(){}; this.video.removeEventListener(ename, fun); console.log('刪除事件:' + ename); return fun; }
給video事件添加一個代理來刪除添加事件,isF就是在新增這個事件是否刪除以前的這個相同的事件,由於添加事件用匿名函數的話,是不能刪除的,這樣設置一個代理就能夠把動態添加的事件記錄在events裏面,便於操做
這時咱們補上修改當前播放進度和音量的功能
//跳轉視頻進度 單位 秒 pro.setCurrentTime = function(t){ this.video.currentTime += t; } //設置音量大小 單位 百分比 如 0.1 pro.setVolume = function(v){ this.video.volume+= v; }
再經過代理給video添加左右上下滑動的事件
//視頻手勢右滑動事件 this.eve('swiperight',function(){ that.setCurrentTime(5); }); //視頻手勢左滑動事件 this.eve('swipeleft', function(e) { that.setCurrentTime(-5); }); //視頻手勢上滑動事件 this.eve('swipeup',function(){ that.setVolume(0.2); }); //視頻手勢下滑動事件 this.eve('swipedown', function(e) { that.setCurrentTime(-0.2); });
ok,四個方向的滑動事件已經添加過去了,但這是mini模式播放時的事件,在全屏播放下,四個方向事件並無跟着video元素方向的改變而改變,這下須要再經過最最最笨的方式判斷是否全屏從而觸發的事件
//視頻手勢右滑動事件 this.eve('swiperight',function(){ if(that.isMax){ return that.setVolume(0.2); } that.setCurrentTime(5); }); //視頻手勢左滑動事件 this.eve('swipeleft', function() { if(that.isMax){ return that.setVolume(-0.2); } that.setCurrentTime(-5); }); //視頻手勢上滑動事件 this.eve('swipeup',function(){ if(that.isMax){ return that.setCurrentTime(-5); } that.setVolume(0.2); }); //視頻手勢下滑動事件 this.eve('swipedown', function() { if(that.isMax){ return that.setCurrentTime(5); } that.setVolume(-0.2); });
怎麼樣,雖然看起來有點stupid,可是很實用呢
雖然說在5+客戶端,android能夠調用原生的方式播放,但仍是差強人意,咱們能夠再來看一套解決方案
初始化時,記錄mini時的樣式,全屏時,經過修改視頻寬度爲屏幕高度,視頻高度修改成視頻寬度,再利用5+的屏幕旋轉,設置全屏,隱藏狀態欄
0)去除手勢事件判斷
由於如今是準備改變移動設備的方向,因此,手勢方向會跟着設備方向改變
1)去除 css3 旋轉以及位移
//記錄信息 pro.initInfo = function() { var that = this; //在onload狀態下,offsetHeight纔會獲取到正確的值 window.onload = function() { that.miniInfo = { //mini狀態時的樣式 zIndex: 1, width: that.video.offsetWidth + 'px', height: that.video.offsetHeight + 'px', position: that.vRoom.style.position } that.maxInfo = { //max狀態時的樣式 zIndex: 99, width: '100%', height: that.sw + 'px', position: 'fixed' } } }
2)該用5+的設置全屏以及隱藏狀態欄
//全屏 mini 兩種模式切換 pro.switch = function() { var vR = this.vRoom; //獲取須要轉換的樣式信息 var info = this.isMax ? this.miniInfo : this.maxInfo; for(var i in info) { vR.style[i] = info[i]; } this.isMax = !this.isMax; plus.navigator.setFullscreen(this.isMax); if(this.isMax) { //橫屏 plus.screen.lockOrientation("landscape-primary"); } else { //豎屏 plus.screen.lockOrientation("portrait-primary"); } }
3)全屏狀態下,android端返回鍵,觸發退出全屏
pro.initEvent = function() { //.......省略其餘代碼 this.oback = $.back; //監聽安卓返回鍵 $.back = function() { if(that.isMax) { that.switch(); return; } that.oback(); } }
效果圖
嘿嘿,一個在移動端的播放器怎麼能少得了 自動切換 橫豎屏呢?
在個小節當中就講了如何手動切換全屏,接下來重力感應切換橫屏,須要用到5+的API Accelerometer 加速度感應
簡單說:重力加速度感應能夠想象成一個小球在座標系中 三個方向上的加速度。永遠以手機屏幕爲準
啥是加速度?額,就是物理書上的
手機水平放置向上是y軸正向 向右是x軸正向,向外是z軸正向
啥是xyz軸?額,就是高數書上的
哎呀,你把手機豎屏正直的放在地上,你人正直走上去,如今你站在你的手機的屏幕上,而後你的右手打開伸直,這就是x軸,你如今看着前面,這就是y軸,你的頭頂就是z軸。這樣講明白了不,可是並非真的要你踩手機,23333
您也能夠選擇查看其餘講解:Android-傳感器開發-方向判斷
手機屏幕向上水平放置時: (x,y,z) = (0, 0, -9.81)
當手機頂部擡起時: y減少,且爲負值
當手機底部擡起時: y增長,且爲正值
當手機右側擡起時: x減少,且爲負值
當手機左側擡起時: x增長,且爲正值
ok,咱們新增2個方法,用於打開和關閉設備監控
//開啓方向感應 pro.startWatchAcc = function(){ var that = this; this.watchAccFun = plus.accelerometer.watchAcceleration(function(a) { if(that.getIsMax()){ //當前爲全屏狀態 //判斷是否知足豎屏Mini狀態 a.yAxis>=5 && that.setIsMax(false); }else{ //當前爲Mini狀態 //判斷是否知足全屏Max狀態 Math.abs(a.xAxis) >=5 && that.setIsMax(true); } }, function(e) { //出錯了大不了 不自動旋轉唄 讓它手動 切換 console.log("Acceleration error: " + e.message); that.clearWatchAcc(); },{ frequency:1200 }); } //關閉方向感應 pro.clearWatchAcc = function(){ this.watchAccFun && plus.accelerometer.clearWatch(this.watchAccFun); }
而後在初始化的時候默認打開方向監控
var bvd = function(dom) { var that = this; $.ready(function() { //... }) $.plusReady(function() { that.startWatchAcc(); }) }
再把橫向全屏改成,可雙向橫屏
真機調試看看
嘿嘿,咱們再給全屏播放時添加一個鎖定按鈕,讓設備不監控 重力感應,也不響應視頻的點擊播放暫停事件
先作一個鎖定按鈕
固然,鎖定圖片,地址也改爲用base64,最好也用js動態生成標籤
設置它的基本樣式,靠右,上下垂直居中,默認隱藏
.lock { padding: .3rem; width: 3rem; height: 3rem; position: absolute; right: .5rem; top: 50%; transform: translateY(-50%); -webkit-transform: translateY(-50%); visibility: hidden; }
好,咱們來整理一下邏輯,
1)默認在mini播放時,lock隱藏
2)全屏播放時,lock顯示,可是也會跟着控制條 在4s內向右隱藏
3)全屏暫停時,lock也跟着控制條 一直顯示
4)點擊lock鎖定時,提示已鎖定,控制條當即隱藏,lock4s內向右隱藏,視頻點擊事件更換爲顯示lock圖標,android返回鍵事件改成不作任何,關閉重力監控
5)點擊lock解鎖時,提示已解鎖,android返回鍵改成 切換爲mini狀態,開啓重力監控
我擦,其實作起來仍是挺鬱悶的,主要是邏輯處理比較痛苦
0)添加一個向右移動的動畫,3s延遲後 1s內 執行完動畫
@keyframes lockhide {0% {transform: translate(0%,-50%);}100% {transform: translate(120%,-50%);}} webkit-keyframes lockhide {0% {transform: translate(0%,-50%);}100% {transform: translate(120%,-50%);}} .lockhidden { animation: lockhide 1s 3s linear; -webkit-animation: lockhide 1s 3s linear; animation-fill-mode:forwards; -webkit-animation-fill-mode: forwards; }
1)全屏時顯示lock
pro.switch = function() { //... //全屏時 顯示鎖定 圖標 this.vlock.style.visibility = this.isMax ? 'visible' : 'hidden'; }
2)全屏播放時,lock顯示,可是也會跟着控制條 在4s內向右隱藏
咱們在播放時添加lock的隱藏動畫,
3)全屏暫停時,lock也跟着控制條 一直顯示
4)點擊lock鎖定時,提示已鎖定,控制條當即隱藏,lock4s內向右隱藏,視頻點擊事件更換爲顯示lock圖標,android返回鍵事件改成不作任何,關閉重力監控
5)點擊lock解鎖時,提示已解鎖,android返回鍵改成 切換爲mini狀態,開啓重力監控
//鎖定屏幕 pro.lockScreen = function() { $.toast('鎖定屏幕'); var that = this; //更換video點擊事件爲 顯示 lock圖標,並保存 video以前的事件 this.videoTapFn = this.eve('tap', function() { that.lockT = setTimeout(function(){ that.vlock.classList.add('lockhidden'); },500); //從新開始播放樣式 that.vlock.classList.remove('lockhidden'); that.vlock.style.visibility = 'visible'; }, true); //隱藏控制條 this.vC.style.visibility = 'hidden'; //給Lock圖標增長 隱藏樣式類 this.vlock.classList.add('lockhidden'); //鎖定屏幕時,不監控重力感應 this.clearWatchAcc(); //標識當前更改的Lock狀態 this.isLock = true; } //解鎖屏幕 pro.unlockScreen = function() { $.toast('解鎖屏幕'); //替換回video以前的點擊事件 this.eve('tap', this.videoTapFn, true); //給Lock圖標清楚 隱藏樣式類 this.vlock.classList.remove('lockhidden'); //不鎖定屏幕時,監控重力感應 this.startWatchAcc(); //標識當前更改的Lock狀態 this.isLock = false; }
666)最後給咱們親愛的lock圖標增長一枚撫摸事件,以及android返回鍵的事件更改
//全屏 時 鎖定點擊事件 this.vlock.addEventListener('tap', function() { if(that.isLock) { that.unlockScreen(); return; } that.lockScreen(); }); this.oback = $.back; //監聽安卓返回鍵 $.back = function(){ if(that.isMax){ if(!that.isLock){ //全屏狀態下 按下返回鍵 時,1s內不監控重力,防止返回Mini狀態時和重力感應併發事件 setTimeout(function(){ that.startWatchAcc(); },1000); that.clearWatchAcc(); that.switch(); } return; } that.oback(); } }
好了!本文5+全屏demo 源碼地址
寫博客不易,可是那種分享的心情是很不錯的,未嘗不是另外一種溫習和進步呢?
謝謝各位。
本文相關文章:H5打造屬於本身的視頻播放器 專欄