需求与bug解决
做 H5开发的一个需求:页面内有一个按钮点击可以播放语音,产品提供的素材是多段语音,并配有对应文字;
bug1: 切换到后台时,语音还在播放,
解决方法:增加 visibilitychange 监听事件,用 document.hidden 判断是否处于后台。
mounted() {
this.audioTemp = new Audio(AthenaData.audioList[0].voice);
// 切到后台停止语音
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.musicDisabled = false; // 按钮状态
this.audioTemp.pause();
}
});
},
bug2: API can only be initiated by a user gesture
原因:部分安卓机要求调用 audio.play() 这个接口必须要用户手势触发,也就是必须是真实的用户操场类似 click之类的事件去触发才行,用js主动去触发dom的click的话也是无效的!
解决方案有二:1. 安卓端上有对应的 api 设置可以取消这个限制,但一定会有合规方面的风险(法务检测那边可能无法通过)2.将多段语音合成一段。
功能实现
template:
// 播放按钮
v-image.normal="musicDisabled ? AthenaData.horn1 : AthenaData.horn2"
class="horn1"
@click="onBgmMusicClick"
/>
// 滚动文字
ref="box"
class="tips-box"
>
ref="content"
class="tips-content"
:style="tranlateDo?{
transition: `all ${tranlateTime}s linear`,
transform: `translateX(-${tranlatex}px)`,
}:{
transform: 'translateX(0)'
}"
>
{{ AthenaData.audioList[voiceIndex].label }}
data:
data() {
return {
AthenaData, // 数据
musicDisabled: false, // 播放按钮状态
audioTemp: null, // 语音对象
voiceIndex: 0, // 文字索引
tranlateDo: false, // 文字滚动状态
tranlatex: 0, // 文字滚动距离
timeoutTicket: null, // 当前文字
voiceTimeoutTicket: null, // 下一段文字
};
},
mounted:
mounted() {
// 设置语音播放列表
this.audioTemp = new Audio(AthenaData.audioList[0].voice);
this.audioTemp.onended = () => {
this.musicDisabled = false;
this.audioTemp.pause();
this.audioTemp.load();
this.tranlateDo = false;
this.voiceIndex = 0;
clearTimeout(this.voiceTimeoutTicket);
};
// 切到后台停止语音
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.musicDisabled = false;
this.audioTemp.pause();
this.tranlateDo = false;
this.voiceIndex = 0;
this.audioTemp.load();
if (this.timeoutTicket) {
clearTimeout(this.timeoutTicket);
}
clearTimeout(this.voiceTimeoutTicket);
}
});
},
computed: {
// 读当前文字滚动的时间
tranlateTime() {
// 三目运算符容错
return AthenaData.audioList[this.voiceIndex]
? AthenaData.audioList[this.voiceIndex].time - 1// 每段语音的首末有半秒背景音乐
: 1;
},
},
methods:
methods: {
// 播放语音按钮回调
onBgmMusicClick() {
this.musicDisabled = !this.musicDisabled;
if (this.musicDisabled && AthenaData.audioList.length) {
this.audioTemp.play();
this.setLabelPlay();
}
if (!this.musicDisabled) {
this.audioTemp.pause();
this.tranlateDo = false;
this.voiceIndex = 0;
this.audioTemp.load();
if (this.timeoutTicket) {
clearTimeout(this.timeoutTicket);
}
clearTimeout(this.voiceTimeoutTicket);
}
},
//设置文字动画
setLabelPlay() {
// 语音有半秒前奏播放完后 再开始文字动画
this.timeoutTicket = setTimeout(() => {
this.tranlateDo = true;
this.tranlatex = this.$refs.content.offsetWidth - this.$refs.box.offsetWidth;
}, 500);
clearTimeout(this.voiceTimeoutTicket);
this.voiceTimeoutTicket = setTimeout(() => {
// 目前只有三段文字,若索引为2说明到最后一段,置0停掉动画;否则处理下一段(+1后则显示下一段文字)
this.voiceIndex = this.voiceIndex === 2 ? 0 : this.voiceIndex + 1;
// 以下为控制下一段文字动画的逻辑
if (this.voiceIndex !== 0) {
// 停掉动画 从头滚动
this.tranlateDo = false;
this.tranlatex = 0;
// 开始滚动
this.setLabelPlay();
} else {
clearTimeout(this.voiceTimeoutTicket);
}
}, parseInt(AthenaData.audioList[this.voiceIndex].time, 10) * 1000); // 这个时间为当前动画持续的时间
},
}
最终效果如下,资源链接
屏幕录制2022-07-28 18.51.06
其实,不难发现,语音和文字的单独控制,只不过是卡住时间让效果看起来是同步。除此之外,还可以扩充暂停功能:播放语音中途点击按钮让语音和文字滚动暂停,再次点击时接着上述效果。原生实现方法只需增加一个变量记录点击时文字已滚动的距离,然后再次点击时进行判断,让动画接着滚动;如果引入其他动画库也许会有直接的API控制动画的暂停,实现起来更加简单。
Audio介绍
什么?你不知道audio是什么?它是h5新特性之一,audio可以理解为普通的dom对象,相关属性方法事件如下:
Audio对象的属性
Audio对象的方法
Audio对象的事件
事件和方法很像,只不过是可以传入回调函数增加更多逻辑
好文阅读
本文由 用户 于 2023-12-25 发布在 夸智网,如有疑问,请联系我们。
本文链接:https://www.kuazhi.com/post/712834637.html
发表评论