視頻插件vue-video-player的使用及注意事項
官方文檔
video.js:https://docs.videojs.com/docs/api/player.html
vue-video-player:https://github.com/surmon-china/vue-video-player
1、包的安裝
npm install vue-video-player --save
2、包的引入
import VideoPlayer from 'vue-video-player'
require('video.js/dist/video-js.css') //若此行報錯無法找到,則改為require('vue-video-player/node_modules/video.js/dist/video-js.css')
require('vue-video-player/src/custom-theme.css')
Vue.use(VideoPlayer)
3、插件使用
html部分
<div style="overflow:hidden"> //移動端當屏幕寬度較小時,此視頻控件調節音量時會使得屏幕出現橫向滾動條,故在父級盒子使用overflow:hidden來阻止滾動條出現,以免影響用戶體驗。
<video-player class="video-player vjs-custom-skin"
ref="videoPlayer"
:playsinline="true"
:options="playerOptions" //相關配置
@play="onPlayerPlay($event)"//監聽開始狀態(非必需),這是事件,按需使用
@pause="onPlayerPause($event)"//監聽暫停狀態(非必需),這是事件,按需使用
>
</video-player>
</div>
js部分
data(){
return{
playerOptions : {
playbackRates: [0.5, 1.0, 1.5, 2.0], //播放速度
autoplay: false, //如果true,瀏覽器准備好時開始回放。
muted: false, // 默認情況下將會消除任何音頻。
loop: false, // 導致視頻一結束就重新開始。
preload: 'auto', // 建議瀏覽器在<video>加載元素后是否應該開始下載視頻數據。auto瀏覽器選擇最佳行為,立即開始加載視頻(如果瀏覽器支持)
language: 'zh-CN',
aspectRatio: '16:9', // 將播放器置於流暢模式,並在計算播放器的動態大小時使用該值。值應該代表一個比例 - 用冒號分隔的兩個數字(例如"16:9"或"4:3")
fluid: true, // 當true時,Video.js player將擁有流體大小。換句話說,它將按比例縮放以適應其容器。
sources: [{
type: "",//這里的種類支持很多種:基本視頻格式、直播、流媒體等,具體可以參看git網址項目
src: "" //url地址
}],
poster: "../../static/images/test.jpg", //你的封面地址
// width: document.documentElement.clientWidth, //播放器寬度
notSupportedMessage: '此視頻暫無法播放,請稍后再試', //允許覆蓋Video.js無法播放媒體源時顯示的默認信息。
controlBar: {
timeDivider: true, //當前時間和持續時間的分隔符
durationDisplay: true, //顯示持續時間
remainingTimeDisplay: false, //是否顯示剩余時間功能
fullscreenToggle: true //全屏按鈕
}
}
}
},
//掛載視頻組件(非必須)不寫這一步也可以實現播放,添加這個是為了自定義按鈕使用
computed: {
player() {
return this.$refs.videoPlayer.player//自定義播放
}
},
視頻插件xgplayer(西瓜播放器)的使用及注意事項
更多詳情見官方文檔:http://h5player.bytedance.com/
貼代碼:
1、安裝包:npm install xgplayer
2、代碼部分:
<template>
<div id="mse"></div> //1、標簽,必須要有
</template>
<script>
import * as axios from 'axios'
import common from '../../kits/common.js'
import Player from 'xgplayer'; //2、引入視頻組件
export default{
name:'Demo',
data(){
return{
playerConfig:{ //3、動態設置相關值
id: 'mse',
normalUrl:'', //普清
midUrl:'', //高清
hightUrl: '', //超清
poster: '', //封面
}
}
},
created(){
this.getVideoInfo()
},
methods:{
playerInit(){ //4、設置視頻配置(注意:playerInit應放在異步函數里或mounted之后,不可在created里直接加載,否則不生效)
let player = new Player({ //初始化player實例對象
id:'mse',
url: this.playerConfig.highUrl, //視頻鏈接
poster:this.playerConfig.poster, //封面圖片
fluid: true, //流式布局
volume: 0.6, //初始音量
whitelist: ['iPhone','Android'], //白名單
playsinline: true, //內聯模式
videoInit: true,
'x5-video-player-type': 'h5',
});
player.emit('resourceReady', [{name: '超清', url: this.playerConfig.highUrl}, {name: '高清', url: this.playerConfig.highUrl},{name: '標清', url: this.playerConfig.highUrl},]);
}
},
getVideoInfo(){
axios.get(`${common.videoApi}/getVideo`).then(res=>{
this.playerConfig.highUrl=res.data.data.url1 //獲取超清視頻鏈接
this.playerConfig.midUrl=res.data.data.url2 //獲取高清視頻鏈接
this.playerConfig.poster=res.data.data.vimage //獲取視頻封面
this.playerInit() //為什么在這里調用playerInit函數而不在mounted生命周期中調用?因為接口請求是異步的,在mounted中接口調用還未返回數據,故playerConfig中的數據仍然為空。而視頻組件依賴於playerConfig中的數據
})
}
}
</script>
【【【直播】】】:直播與視頻播放差不多,就以下幾點不同
1、引入不同:
import 'xgplayer'; //先引入xgplayer
import HlsJsPlayer from 'xgplayer-hls.js'; //再引入組件
2、初始化不同:
let player=new HlsJsPlayer({ //初始化實例對象HlsJsPlayer
id:'hlsmse',
url: this.hlsPlayerConfig.url,
poster:this.hlsPlayerConfig.poster,
})
【【【注意事項:西瓜播放器xgplayer我用的時候有個Bug,就是在當前頁面,xgplayer的視頻鏈接url每重新賦值一次,xgplayer都會在原有的<video>標簽上再生成一個新的<video>標簽,並去覆蓋原有的標簽,通過這種方式實現當前視頻的更新,但這也導致一個問題,因為原有的<video>標簽不會被清空。就是說會出現多個video標簽在同一個頁面上的同一個位置。不僅影響性能,還會導致視頻組件占位沖突。目前我想到的解決辦法:在url重新賦值后,利用provide / inject 組合 刷新當前頁面,以達到重新加載xgplayer組件的目的。(如何刷新,見下方)】】】
【【【09.10.24,想到了以上bug第二種解決辦法:在重新初始化播放器之前,利用js先刪除舊的已經渲染過的<div id="mse"></div>,緊接在再創建一個新的<div id="mse"></div>即可,代碼如下
<div class="videoBody" id="videoBody">
<div v-show="islive==false" id="mse" class="mse"></div>
<div v-show="islive!==true" id="hlsmse" class="hlsmse"></div>
<div id="msedemo"></div>
</div>
重新創建mse:
document.getElementById('videoBody').removeChild(document.getElementById('mse'))
document.getElementById('videoBody').insertBefore(document.createElement('div'),document.getElementById('hlsmse'))
document.getElementById('hlsmse').previousElementSibling.setAttribute('id','mse')
重新創建hlsmse:
document.getElementById('videoBody').removeChild(document.getElementById('hlsmse'))
document.getElementById('videoBody').insertBefore(document.createElement('div'),document.getElementById('msedemo'))
document.getElementById('msedemo').previousElementSibling.setAttribute('id','hlsmse')
】】】
【【【注冊事件,如監聽播放暫停事件】】】:
playerInit(){ //xgplayer配置
let player = new Player({
id:'mse',
url: this.playerConfig.url,
});
player.on('play',function(){ //注冊並監聽播放事件
consol.log('播放了')
})
player.on('pause',function(){ //注冊並監聽暫停事件
console.log('暫停了')
})
},
具體可查看官網:http://h5player.bytedance.com/api/#%E4%BA%8B%E4%BB%B6
【【【上次播放到這里】】】:
1、在playInit的new Player里添加:
progressDot: [{'time': this.playerConfig.lastTime,'text':'您上次看到了這里'}],
2、在playInit函數最后添加以下代碼
if(this.playerConfig.currentTime>0){
player.currentTime=this.playerConfig.lastTime //將上次播放的時間配置到當前播放器的播放時間
}
this.postTime=setInterval(()=>{
if(player.hasStart){
axios.post(`${common.videoapi}/amstc/addVideoTime?vid=${this.vid}&uid=${this.uid}¤ttime=${parseInt(player.currentTime)}`) //每過20秒將當前播放時間上傳至服務器,下次進入此頁面獲取上次播放時間。
}
},20000)
3、在頁面銷毀后,清除定時器
destroyed(){
clearInterval(this.postTime)
},
注意,此功能適用於pc端,移動端安卓ios會有兼容問題
【【【安卓手機微信端里,組件出現黑屏情況,視頻加載不出來,並報錯The play() request was interrupted by a new load request】】】:
提示:在安卓端的微信內嵌的瀏覽器里,視頻組件是可以實現自動播放的,在其他地方瀏覽器則不可播放,除非初始時是靜音狀態。因為瀏覽器視頻播放協議的原因,video在用戶交互之前,一般不允許自動播放。
原因看這:http://link.sov5.cn/l/LKioRMRBQX
解決:
let player=new HlsJsPlayer({
id:'hlsmse',
autoplay:false, //這里取消自動播放
url: this.hlsPlayerConfig.url,
poster:this.posterImg,
fluid: true,
volume: 0.6,
useHls: true,
whitelist: ['iPhone','Android'],
playsinline: true,
ignores: ['time','volume'],
'x5-video-player-type': 'h5',
})
+ player.start(this.hlsPlayerConfig.url) //調用此函數即可,調用此函數在安卓微信端會自動播放
vue如何刷新當前頁面
轉自:https://www.cnblogs.com/mmzuo-798/p/9356253.html
重新刷新當前頁面,首先想到的就是路由重新導入當前頁面,比如這樣:
this.$router.push({name:'goodsList',params:{vid:vid}})
但是:我們會發現用vue-router在當前頁面再重定向路由到頁面本身,就算改變id,頁面也是不進行刷新的,根本沒有任何作用~所以這個方法out
下面提供兩種方法:
1、強制刷新法,頁面會類似於ctrl+f5的那種刷新,會有一段時間的空白出現,用戶體驗很不好,不建議使用
location. reload()
this.$router.go(0)
2、provide / inject 組合 方式是我試過最實用的
① 首先,要修改下你的app.vue,如圖
② 通過聲明reload方法,控制router-view的顯示或隱藏,從而控制頁面的再次加載,這邊定義了
isRouterAlive //true or false
來控制,然后在需要當前頁面刷新的頁面中注入App.vue組件提供(provide)的 reload 依賴,然后直接用this.reload來調用就行
vue利用watch監聽路由變化,可配合上面刷新方法使用
watch: {
$route() {
console.log("路由發生變化了")
this.goodId=this.$route.params.vid //2、獲取新id重新請求數據
axios.get(`${common.goodsapi}/getgoods?id=${this.goodId}`).then(res=>{.....})
this.reload() //3、監聽到路由變化后刷新頁面,同時完成頁面數據更新
}
},
methods:{
goToGoodsList(){
this.$router.push({name:'goodsList',params:{vid:vid}}) //1、點擊后路由跳轉仍在本頁面,路由僅所攜帶的id發生改變。
}
}
回到頂部
document.documentElement.scrollTo(0,0)
轉換時間格式
transCommentDate(dateNum){
const date=new Date(dateNum)
let Y=date.getFullYear()
let M=date.getMonth()+1
M=M<10?('0'+M):M
let D=date.getDate()
D=D<10?('0'+D):D
let h=date.getHours()
h=h<10?('0'+h):h
let m=date.getMinutes()
m=m<10?('0'+m):m
let s=date.getSeconds()
s=s<10?('0'+s):s
return `${Y}-${M}-${D} ${h}:${m}:${s}`
},
切換輸入框時的獲取焦點問題
需求:有兩個input框(評論和回復),在點擊“回個話”按鈕后,評論input框切換成回復input框,同時回復input框獲取焦點
評論框:<input class="comment" id="comment" v-if="!isReply" /> 回復框:<input class="reply" id="reply" v-slse-if="isReply"/>
思路:當點擊“回個話”按鈕后,執行函數使this.isReply=true,回復框顯示。再使用document.getElementById("reply").focus()使其獲取焦點。
問題:以上思路執行后會報錯,錯誤為document.getElementById("reply")找不到。為什么呢,因為回復框原本是不存在的,只有在頁面mounted之后,回復框dom才會加載出來,而document.getElementById("reply")是在頁面加載之前就執行了,故找不到。
【【【解決辦法:將兩個輸入框的id設置成一樣的(如id="inputLabel"),再使用document.getElementById("inputLabel")即可。】】】
利用websocket實現直播實時評論
直接貼代碼
<template>
<div>
<div>
<ul>
<li v-for="(item,index) in messages" :key="index">{{item.ccontent}}</li> //渲染從socket獲取到的評論數據
</ul>
</div>
<input v-model="commentValue" />
<div @click="sendComment">發表評論</div> //發送評論數據到socket
<div>
</template>
<script>
import * as axios from 'axios'
import common from '../../sets/common.js'
import {getCookie} from '../../sets/cookie.js' //引入cookie
data(){
return{
usertoken:null, //用戶信息
wspath:'', //websocket的path
socket:null, //socket實例對象
messages:[], //從socket獲取的評論數據
commentValue:'', //用戶輸入的評論內容
}
}
created(){
this.usertoken = getCookie('Token')
this.chatInit() //初始化socket
},
methods:{
//1、實例化socket
chatInit(){
if(typeof(WebSocket) === 'undefined'){
this.$toast({
message:'您的瀏覽器不支持socket',
duration:2000,
})
}else{
// 實例化socket
if(this.usertoken){
this.wspath = `${common.socketlink}${this.roomid}?token=${this.usertoken}` //socketlink由后端提供給我,roomid從直播接口獲取,token自己去設置
} else {
this.wspath = `${common.socketlink}${this.roomid}`
}
//初始化socket實例對象
this.socket = new WebSocket(this.wspath)
// 監聽socket連接
this.socket.onopen = ()=>{
console.log('socket連接成功')
}
// 監聽socket錯誤信息
this.socket.onerror = ()=>{
console.log('socket連接錯誤')
}
//監聽socket關閉
this.socket.onclose=()=>{
console.log('socket連接關閉')
}
// 監聽socket消息
this.socket.onmessage = this.getComment //這里面調用函數時不能帶(),若this.getComment()會報錯
}
},
//2、監聽socket消息
getComment(msg){
this.messages=this.messages.concat(JSON.parse(msg.data))
},
//3、發送評論
sendComment(){
this.socket.send(`{"ccontent":"${this.commentValue}"}`) //這里注意上傳時前面參數如ccontent要與后端的一致,不然socket會檢測上傳錯誤,從而自動關閉連接
this.commentValue=''
document.getElementById('commentScrollHidden').scrollTop=document.getElementById('scrollBottom').offsetHeight //發送評論后評論區回到底部
}
},
}
</script>
實現簡單的微信分享功能(若想使用js-sdk實現分享功能請移步 https://www.cnblogs.com/huihuihero/p/12132952.html)
①先動態設置每個頁面的title(方法見下方),
②有些頁面需要自定義標題的也可以通過 document.title= 設置(方法見下方)。
③最后就是ios微信瀏覽器兼容問題,解決了即可(方法見下方)
ios微信瀏覽器分享問題
【問題】:ios端微信內分享的鏈接url不改變,總是同一個頁面
【原因】:
IOS:微信IOS設備,所記錄的url是進入頁的url,所以無論分享哪個頁面,總是會分享成初始進入頁的url
Android:微信安卓設備,所記錄的是當前頁的url,所以分享出去的也會是當前頁的url
【詳細解釋】:Vue的路由默認的mode是hash,這個模式帶來的bug不是一般的多,在hash模式下url地址會出現#,這個#號會使window.location.href()方法失效,因此location.href方式這里最好不要用。並且ios微信支付的調起也會因為#的存在而導致jsap調起失敗,但是Android並沒有問題。因此mode的模式最好是history,這種模式下微信支付調起完全沒問題。接下來就是微信分享的問題,在history模式下android還是能夠正常分享,但是ios系統在這種模式下在微信瀏覽器下的url地址就是一開始進入頁面的url,不會改變,無論路由如何的切換(android則不存在這種情況)。
【解決思路】:思路1、用js-sdk,詳見微信文檔。思路2、判斷若是微信端,則在進入頁面后將頁面的路由拼接給url,已達到重新定義url的效果
【解決方法】:在main.js中添加以下代碼(利用Vue Router的afterEach方法)
router.afterEach((to, from) => {
const u = navigator.userAgent.toLowerCase()
//下面這句是做判斷,是否是ios。當然你也可以做一些其他判斷,如這里我通過to.path=='/demo/demoReply'將一些頁面排除在外
if(u.indexOf("like mac os x") < 0 || u.match(/MicroMessenger/i) != 'micromessenger' || u.match(/WebP/i) == "webp"||to.path=='/pArticle/pArcReply'||to.path=='/demo/demoReply') return
if (to.path !== global.location.pathname) {
location.assign(to.fullPath) //因為vue路由的hash模式,這里最好不要用 location.href= 或 location.replace() ,以避免url中#帶來的問題
}
})
【提個醒】:1、使用以上方法時,遇到了個問題,一是路由傳參使用params時,當參數超過2個就會出現bug,然后以query方式傳參解決了,但參數再多時,query方式也不好用了,最終只好把這些頁面在上述代碼中通過判斷方式排除了,暫未知曉原因。
2、這個只能在線上用可以,線下時,需要先注釋掉,不能使用。
微信分享自定義標題
在當前頁面設置title即可,分享出去會自帶頁面title
通過document.title="中國進入5G時代"方式設置
getVideoInfo(){
axios.get(`${common.videoapi}/getvideoinfo`).then(res=>{
document.title=res.data.data.name
})
}
vue根據路由動態改變頁面標題
在main.js中添加以下代碼
router.beforeEach((to, from, next) => {
/* 路由發生變化修改頁面title */
if (to.meta.title) {
document.title = to.meta.title;
}
next();
})
在router.js路由頁面
{
path:'/demo/demoReply',
component:demoReply,
name:'DemoReply',
meta:{
title:'評論回復' //設置標題
}
},
改變v-html內的指定標簽的樣式,這里以img為例,設置img最大寬度100%,高度自適應。(注:一定要設置高度自適應)
方式1:js設置
updated(){
let elem=document.getElementById('demo').getElementsByTagName('img') //獲取指定標簽里的img
let elem2=Array.from(elem) //因為getElementsByTagName()返回的是一個偽數組,所以這里使用Array.from轉換為數組
for(let item of elem2){
item.style.maxWidth="100%"
item.style.height="auto"
}
},
方式2:css穿透模式下設置
/deep/ htmlcontent{
img{
max-width:100%!important;
height:auto!important;
}
}
vue2.0項目上線屏蔽console.log()
在build/webpack.prod.conf.js文件里的UglifyJsPlugin里加上這樣兩行代碼即可
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false,
drop_debugger: true, //屏蔽debugger
drop_console: true, //屏蔽console
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
減少,壓縮項目打包后體積
在 config/index.js 中修改參數,
找到 productionSourceMap: true,改為 false 即可。
時間轉換
transDate(dateNum){
const date=new Date(dateNum)
let Y=date.getFullYear()
let M=date.getMonth()+1
M=M<10?('0'+M):M
let D=date.getDate()
D=D<10?('0'+D):D
let h=date.getHours()
h=h<10?('0'+h):h
let m=date.getMinutes()
m=m<10?('0'+m):m
let s=date.getSeconds()
s=s<10?('0'+s):s
return `${Y}-${M}-${D} ${h}:${m}:${s}`
},
解決格林尼治時間在ios端和android端的兼容問題
格林尼治時間格式:2019-03-05T09:02:24.000+0000
android端:
new Date("2019-03-05T09:02:24.000+0000") 可正常轉換。
new Date("2019-03-05T09:02:24.000+0000".substr(0,19)) 轉換出來的時間會早8個小時
ios端:
new Date("2019-03-05T09:02:24.000+0000") 輸出為NaN,不可用。
new Date("2019-03-05T09:02:24.000+0000".substr(0,19)) 可正常轉換
故通過判斷是否是ios端,再選擇轉換方式。具體代碼如下:
transDate(dateNum){
let u = navigator.userAgent;
if (!!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) { //ios端
const date=new Date(dateNum.substr(0,19)) //截取前19位后再轉換
let Y=date.getFullYear()
let M=date.getMonth()+1
M=M<10?('0'+M):M
let D=date.getDate()
D=D<10?('0'+D):D
let h=date.getHours()
h=h<10?('0'+h):h
let m=date.getMinutes()
m=m<10?('0'+m):m
let s=date.getSeconds()
s=s<10?('0'+s):s
return `${Y}-${M}-${D} ${h}:${m}:${s}`
}else{ //非ios端
const date=new Date(dateNum) //不截取,正常轉換
let Y=date.getFullYear()
let M=date.getMonth()+1
M=M<10?('0'+M):M
let D=date.getDate()
D=D<10?('0'+D):D
let h=date.getHours()
h=h<10?('0'+h):h
let m=date.getMinutes()
m=m<10?('0'+m):m
let s=date.getSeconds()
s=s<10?('0'+s):s
return `${Y}-${M}-${D} ${h}:${m}:${s}`
}
},
判斷手機操作系統是ios端還是android端
checkPort(){
let u = navigator.userAgent, app = navigator.appVersion;
let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android終端
let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios終端
if (isAndroid) {
//這個是安卓操作系統
}
if (isIOS) {
//這個是ios操作系統
}
}