ios微信下vue项目组件切换并自动播放音频的解决方案
最近在做一个英语答题项目 , 项目需求是通过答题取的成绩 , 答题的题型是分为 , 听音选图 , 看图选词 , 和填空题 . 项目总共分为了3个页面 , 开始页 ,答题页 和结束页面 ,答题页关于每种题型 , 我做了相应的组件 , 每次切换题目的时候 ,显示对应的的组件 , 要求听音选图的时候会自动播放音频 .
惯例 , ios下的safari和微信内置浏览器都不支持audio的自动播放 , 通常的解决方案都是通过document.addEventListener('WeixinJSBridgeReady',function(){audio.play()},false)
来实现的自动播放 , 但是
WeixinJSBridgeReady
事件从页面加载开始到结束只会执行一次 , 而且通过removeEventListener来移除后就无法再次监听到,除非刷新页面, 所以会有两个问题:
1.从首页面到答题页面是路由切换的 , 但是处于开始页面时候 , WeixinJSBridgeReady这个事件已经加载完了 , 跳转到答疑页面的时候已经无法监听到该事件了;
2.首页就是答题页面的时候,在题型组件切换的时候只会在第一次加载听力题型的时候会自动播放 , 再次切换到这种题目的时候无法再次自动播放
经过不断的测试 , 发现ios下不支持的原因是 不支持动态创建的audio标签自动播放 , 包括页面第一次加载 , 所以转化了思路 ,把audio标签放在顶层的App.vue里面 , 不随着组件的切换创建和销毁 , 然后在App.vue里面写一个的方法 , 接受src和callback的参数 , 在main.js挂在全局上, 每次切换的时候 ,调用该方法传入对应的src , 代码如下:App.vue
(关于为什么用document.getElementById , 其实本来是准备用 r e f s 的 , 但 是 不 知 为 何 在 a p p . v u e 中 无 法 获 取 , 控 制 台 打 印 的 refs的 , 但是不知为何在app.vue中无法获取 , 控制台打印的 refs的,但是不知为何在app.vue中无法获取,控制台打印的ref能看到有audio 了, 但是不可读)
main.js
// 音频自动播放
Vue.prototype.$autoPlayQues = (src, cb, rate) => {
App.methods.autoPlayQues(src, cb, rate)
}
组件内
import autoPlayer from '$common/js/autoPlayer.js'
...
computed: {
isIOS(){
return device.ios
},
isCanPlayM3u8() {
let video = document.createElement('video')
return video.canPlayType('application/vnd.apple.mpegurl') && this.isIOS
},
}
/**
* 是否使用M3U8音频播放器
*/
isM3U8() {
let use_m3u8 = this.isCanPlayM3u8
return use_m3u8;
},
methods: {
loadingData(data) {
if (data.isIOS) {
url = 'audio url'
data["src"] = [url]
data["player"] = ['m3u8']
this.audioPlayer = new autoPlayer(data, this.loginInfo.userId, this);
} else {
url = 'audio url'
data["src"] = [url]
data["player"] = ['mp3']
this.audioPlayer = new autoPlayer(data, this.loginInfo.userId, this);
}
let _this = this
this.$autoPlayQues(url, () => { console.log('切换下一首曲子') })
}
}
autoPlayer.js
/**
* 仿照 Howler.js 接口实现m3u8播放器
*/
import _ from 'lodash'
let thatV = null; //当前vue实例
let playerThis = null;
/**
* 将秒数格式化时间 M:ss
* Format the time from seconds to M:SS.
* @param {Number} secs Seconds to format.
* @return {String} Formatted time.
*/
function formatTime(secs) {
var minutes = Math.floor(secs / 60) || 0;
var seconds = secs - minutes * 60 || 0;
return minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
}
class autoPlayer {
//对传入的数据做处理
constructor(props, userId, that) {
props = props || {}
playerThis = this
playerThis._props = props
playerThis.userId = userId
let _video = document.getElementById('mmwkAudioPlay')
playerThis._video = _video;
// 音频开始播放
_video.addEventListener("playing", playerThis.playStarting, false)
// 音频播放时
_video.addEventListener("timeupdate", playerThis.timeupdateing, false)
// 音频暂停
_video.addEventListener("pause", playerThis.pauseing, false)
// 音频可以播放
_video.addEventListener("canplay", playerThis.canplaying, false)
// 加载音频
_video.addEventListener("loadstart", playerThis.loadStarting, false)
//传入当前vue实例
thatV = that
thatV.currentTime = playerThis._video.currentTime
}
/**
* 音频开始播放
*/
playStarting() {
console.log("开始播放")
thatV.playerElement.isPlaying = 'play'
thatV.playerElement.duration = playerThis._video.duration
// 听课记录
let audioRecordArrayStr = localStorage.getItem("audioRecord")
let audioRecordArray = JSON.parse(audioRecordArrayStr)
if (audioRecordArray) {
let eduId = playerThis._props.eduId
let currentIndex = -1
for (let i = 0; i < audioRecordArray.length; i++) {
if (audioRecordArray[i].eduId == eduId) {
currentIndex = i
break;
}
}
if (currentIndex >= 0) {
if (audioRecordArray[currentIndex].step > 0 && playerThis._video.currentTime < audioRecordArray[currentIndex].step) {
playerThis.seek(Number((audioRecordArray[currentIndex].step / playerThis._video.duration).toFixed(2)))
}
}
}
}
/**
* 音频播放时
*/
timeupdateing() {
playerThis.step()
}
/**
* 音频暂停
*/
pauseing() {
thatV.playerElement.isPlaying = 'stop'
}
/**
* 可以播放
*/
canplaying() {
console.log("可以播放...")
}
/**
* 加载音频
*/
loadStarting() {
console.log("加载音频...")
}
/**
* 获取当前音频url
* @returns {*|string}
* @private
*/
_src() {
let src = playerThis._props.src
return _.first(src) || ''
}
//验证浏览器是否支持
static isSupport() {
let video = document.createElement('video')
return video.canPlayType('application/vnd.apple.mpegurl')
}
//播放
play(rate) {
console.log("M3u8Player playing... ")
thatV.playerElement.progress = 0;
let _video = playerThis._video
if (playerThis._props.disabled) {
return thatV.$toast({
message: "请购买课程后再播放"
})
}
//判断是否未购
if (!playerThis._props.purchased) {
if (!playerThis._props.isAudition) { //不可以试听
return thatV.$toast({
message: "请购买课程后再播放"
})
}
}
let duration = Math.round(playerThis.duration())
console.log('duration', duration)
try {
if (!rate) {
let rateNum = localStorage.getItem("rateNum")
_video.playbackRate = rateNum || 1.0
}
var obj = _video.play();
obj.then(data => {
thatV.playerElement.isPlaying = 'play';
}).catch(e => {
console.log(e)
thatV.playerElement.isPlaying = 'stop';
})
} catch (e) {
console.log(e)
}
console.log("_video.play(); end")
thatV.playerElement.duration = duration
thatV.playerElement.totalTime = formatTime(duration);
thatV.playerElement.playTime = formatTime(Math.round(playerThis._video.currentTime));
}
//暂停
pause() {
console.log("pause")
thatV.playerElement.isPlaying = 'stop'
playerThis._video.pause();
}
//停止
stop() {
console.log("stop")
let _video = playerThis._video
_video.pause()
_video.currentTime = 0
// _video.removeAttribute('src')
// 音频开始播放
_video.removeEventListener("playing", playerThis.playStarting, false)
// 音频播放时
_video.removeEventListener("timeupdate", playerThis.timeupdateing, false)
// 音频暂停
_video.removeEventListener("pause", playerThis.pauseing, false)
// 音频可以播放
_video.removeEventListener("canplay", playerThis.canplaying, false)
// 加载音频
_video.removeEventListener("loadstart", playerThis.loadStarting, false)
thatV.playerElement.isPlaying = 'stop'
thatV.playerElement.progress = 0;
thatV.playerElement.playTime = '00:00'
}
/**
* 停止状态
*/
stopFlag() {
let _video = playerThis._video
console.log("_video.paused = [" + _video.paused + "]")
let index = 0
let timer11 = setInterval(() => {
if (_video.paused) {
window.clearInterval(timer11)
return
}
if (index > 50) {
window.clearInterval(timer11)
}
_video.pause()
// _video.removeAttribute('src')
index++
}, 500)
}
//滑动、跳过 per:当前比例
seek(per) {
// 将百分比转换为寻找位置。
if (playerThis._props.src.length > 0) {
let seek = Math.round(playerThis.duration() * per)
playerThis._video.currentTime = seek
playerThis.setSeek(seek)
thatV.playerElement.progress = per;
thatV.playerElement.playTime = formatTime(playerThis._video.currentTime)
}
}
/**
* 获取/设置声音的播放速率。此方法可选地采用0,1或2个参数。
* @param {Number} rateNum 播放速度, 0.5 ~ 4.0, 1.0是正常速度。
*/
rate(rateNum) {
if (!rateNum) {
rateNum = 1.0
} else {
if (rateNum < 0.5) {
rateNum = 0.5
}
if (rateNum > 4) {
rateNum = 4.0
}
}
localStorage.setItem("rateNum", rateNum)
if (playerThis._props.src.length > 0) {
console.log(playerThis._video)
playerThis._video.playbackRate = rateNum;
}
}
// 获取正在播放的当前秒数
step() {
var seek = playerThis._video.currentTime || 0;
if (!playerThis._props.purchased) { //未购买
if (playerThis._props.isAudition == 1) {
if (seek > playerThis._props.auditionSeconds) {
playerThis.stop()
return thatV.$toast({ message: "试听结束,请购买课程听完整版"})
}
} else {
//不可以试听
playerThis.stop()
return thatV.$toast({ message: "请购买课程听完整版"})
}
}
thatV.playerElement.totalTime = formatTime(Math.round(playerThis.duration()))
//设置播放时间
thatV.playerElement.playTime = formatTime(Math.round(seek));
//设置播放进度
thatV.playerElement.progress = (seek / playerThis._props.ecSeconds) || 0;
// 保存用户听课记录
if (playerThis._video.currentTime <= 0) {
return
}
playerThis.setSeek(playerThis._video.currentTime)
}
/**
* 设置听课历史记录
* @param {int} seek 听课位置
*/
setSeek(seek) {
let audioRecordArrayStr = localStorage.getItem("audioRecord")
let audioRecordArray = JSON.parse(audioRecordArrayStr)
let nowDate = Date.now()
let currentTime = seek
let eduId = playerThis._props.eduId
if (!audioRecordArray) {
audioRecordArray = [{ "eduId": eduId, "step": Math.round(currentTime), 'updateTime': nowDate }]
} else {
audioRecordArray.sort(function(a1, a2) {
return a1.updateTime - a2.updateTime
})
let currentIndex = -1
for(let i = 0; i < audioRecordArray.length; i++) {
if (audioRecordArray[i].eduId == eduId) {
currentIndex = i
break;
}
}
if (currentIndex == -1) {
if (audioRecordArray.length >= 5) {
audioRecordArray.shift()
audioRecordArray.push({ "eduId": eduId, "step": Math.round(currentTime), 'updateTime': nowDate })
} else {
audioRecordArray.push({ "eduId": eduId, "step": Math.round(currentTime), 'updateTime': nowDate })
}
} else {
if (currentTime / playerThis._video.duration <= 0.98) {
audioRecordArray[currentIndex].step = Math.round(currentTime)
} else {
audioRecordArray[currentIndex].step = 0
}
audioRecordArray[currentIndex].updateTime = nowDate
}
}
localStorage.setItem("audioRecord", JSON.stringify(audioRecordArray))
}
// 音频播放状态
playing() {
let video = playerThis._video
return !(video.paused || video.ended || video.seeking || video.readyState < video.HAVE_FUTURE_DATA)
}
/**
* 获取时长
*/
duration() {
if (playerThis._video.duration) {
return playerThis._video.duration
}
return 0;
}
ended() {
return playerThis._video.ended
}
// unloaded, loading or loaded, like howler.js
state() {
return playerThis._state || 'unloaded'
}
}
export default autoPlayer
作者:TimeFly
链接:https://juejin.im/post/5a65ad38518825732b19f595
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。