小程序 mpvue 自定义音频播放器 (粗线条的进度条)思路和实现方式分享

 效果图是这样的,网上和线上小程序大部分都是用slider做的,方便快捷。
 我也有试过效果 发现都有些丢帧,因为slider + progrss 滑动不够流畅
 所以自己写了一个这样的东西分享记录一下,完成后样式可以随意自定义。

 

需求:

1、开始播放后,每秒钟自动变化进度条。根据音频时长,进度条长度计算位移多少。
2、手指拖拽时候,时间随拖拽变化,方便定位到那个时间点然后从那一刻开始播放。
3、适配机型宽度,整体效果一致。
4、可以暂停,可以暂停后拖拽后在播放。加载中的时候,显示加载icon。
5、显示缓存进度条,用户可以知道当前缓存多少。

 

 


实现思路和方式:

1、用三个div分别代表 进度条底儿、当前缓存(红色)、当前进度条位置,然后通过小程序动画 移动后两个,通过定时器去每秒钟移动,进度条用transform:translateX 比较流畅,缓存用width比较实用。

2、进度条div里面在来个div绑定上3个touch事件,然后让进度条整体translateX(-100%)

3、计算 rpx和和px的比值 如果你的设计稿是750 ,就用你   设备宽度/750 这样才可以计算位置。因为小程序的touch只能拿到px
也就是clientX的值。
4、计算好拖动时候的动画,和左右两边的限制。

 

代码:

<template>



<div class="audio_box">

<div class="currentTimeBack">{{currentTimeBack}}</div>

<div class="currentTimeForward">{{currentTimeForward}}</div>

<!-- <div class="audio_details_box">

<div class="words_count">单词数: {{audioData.wordsCount}}</div>

<div class="switch_en_ch">显示中文释义</div>

</div> -->

<!-- <text class="current-process">{{current_process}}</text> -->

<div class="sliderbox">

<div class="sliderbg"></div>

<div class="sliderbufferedbg" v-bind:style="{width:bufferedWidthVal}"></div>

<div class="sliderLine" id="linebox" :animation="animatedata">

<div class="sliderPoint" @touchstart="touchLineStart" @touchmove="touchLineMove" @touchend="toudLineEnd"></div>

</div>

</div>

<div class="audio_control_box">

<img @click="prev" mode="widthFix" src="/static/audio/audio_pre.png" class="audio_pre"/>

<img @click="audio_play" v-show="!waiting" mode="widthFix" :src="[is_play?'/static/audio/audio_pause.png': '/static/audio/audio_play.png']" class="audio_play_bt" />

<img mode="widthFix" v-show="waiting" src="/static/audio/audio_waiting.png" class="audio_play_bt waitingbt" />

<img @click="next" mode="widthFix" src="/static/audio/audio_next.png" class="audio_next"/>

</div>

</div>

</template>

<script>

export default {

porps: ['audioManager'],

data() {

return {

isIos: null,

waiting: false,

dpi: -1,

screenW: wx.getSystemInfoSync().screenWidth,

audioManager: null,

is_play: false,

currentTimeForward: '',

newCurentTime: -1,

currentTimeBack: '',

audioData: {

duration: 0

},

animatedata: {},

timer: null,

isTouching: false,

bufferedVal: 0,

bufferedWidthVal: 0

};

},

async onLoad() {

this.isIos = await global.isIos();

this.audioManager = wx.getBackgroundAudioManager();

this.dpi = parseFloat((this.screenW / 750).toFixed(3)); // dpi指数

const url =

'https://ssl-public.langlib.com/pe/aha/test/GRE_demo.mp3?jjj=44';

this.audioData.url = url;

this.audioData.title = '雅思托福串讲音频';

this.audioData.lessonName = 'langlib';

// 置灰上一首下一首

// if (response.preArticleId == 0) {

// that.setData({

// is_first_page: true

// });

// }

// if (response.nextArticleId == 0) {

// that.setData({

// is_last_page: true

// });

// }

this.audioManager.onSeeking(() => {

console.log('onSeek');

});

this.audioManager.onPlay(() => {

console.log('onplay');

clearInterval(this.timer);

this.is_play = true;

this.waiting = false;

this.setDuration();

this.timer = setInterval(() => {

this.setDuration();

}, 1000);

});

this.audioManager.onCanplay(() => {

console.log('oncanplay');

this.waiting = false;

});

// 背景音频播放进度更新事件

this.audioManager.onTimeUpdate(() => {

});

// 背景音频播放完毕

this.audioManager.onEnded(() => {

clearInterval(this.timer);

console.log('audioEnd');

this.lineAnimate('-2px');

});

this.audioManager.onPause(() => {

console.log('onPause');

this.is_play = false;

});

this.audioManager.onWaiting(() => {

console.log('onwating');

this.waiting = true;

});

},



methods: {

touchLineStart() {

console.log('touchLineStart');

this.isTouching = true;

clearInterval(this.timer);

},

touchLineMove(e) {

this.isTouching = true;

const rate = e.clientX / 750;

const moveLeftpx = e.clientX;

const boxWidth = 560 * this.dpi;

const limitLeftpx = (this.screenW - boxWidth) / 2;

if (

moveLeftpx <= limitLeftpx - 1.5 ||

e.clientX >= (limitLeftpx + boxWidth) - 2

) {

return;

}

const sliderValue = `${moveLeftpx - limitLeftpx - boxWidth}px`;

this.lineAnimate(sliderValue);

// 不仅要让滑块运动,在滑动过程中 当前时间也要跟随变动,这样在抬起时可以定点播放

// 1.计算当前滑动的点 占整个进度条长度(px)的百分比

// 2.百分比 * 当前总音频时长 = 当前点所在的时长

// 3.当比例小于1或者大于1 代表 超出了范围 就return吧

const movePercent = (moveLeftpx - limitLeftpx) / boxWidth;

this.newCurentTime =

movePercent * this.audioManager.duration > 0

? movePercent * this.audioManager.duration

: 1;



if (movePercent < 0 || movePercent > 1) return;

this.currentTimeForward = this.stotime(this.newCurentTime);

},

toudLineEnd(e) {

console.log(this.newCurentTime);

console.log(this.audioManager.buffered);

if (this.audioManager.paused && this.newCurentTime) {

this.isTouching = false;

this.is_play = false;

this.audio_play(this.newCurentTime);

return;

}

if (this.newCurentTime) {

this.audioManager.seek(this.newCurentTime);

this.is_play = true;

}

this.isTouching = false;

},

// 点击播放暂停

audio_play(newCurentTime) {

if (this.is_play) {

this.is_play = false;

this.audioManager.pause();

clearInterval(this.timer);

} else {

this.is_play = true;

if (typeof newCurentTime === 'number') {

this.audioManager.seek(newCurentTime);

this.audioManager.play();

return;

}

// if (this.audioManager.paused) {

// this.audioManager.play();

// return;

// }

this.playAudio();

}

},

// 上一首

prev() {



},

// 下一首

next() {



},

playAudio() {

if (this.isIos) this.waiting = true;

this.audioManager.title = this.audioData.title;

this.audioManager.src = this.audioData.url;

this.audioManager.play();

},

setDuration() {

// 显示 播放时间 音频时长

if (this.isTouching) return;

const currentTimeForward = this.audioManager.currentTime;

const currentTimeBack = this.audioManager.duration || 0;

const brffer = this.audioManager.buffered;

this.currentTimeForward = this.stotime(currentTimeForward);

this.currentTimeBack = this.stotime(currentTimeBack);

// 让当前的进度条 滑动到对应的位置

// 1、当前音频进行到了总音频的占比

// 2、计算进度条应该位移多少

// 3、使用动画让进度条移动

// 4、如果在我手动去滑动进度条的时候,就别走动画了

const linePointMovePercent = currentTimeForward / currentTimeBack;

const boxWidth = 560 * this.dpi;

const linePointMovePx = `${(linePointMovePercent * boxWidth).toFixed() -

boxWidth}px`;

// console.log(`${boxWidth}长px left:${linePointMovePx}`);

console.log(brffer);

if (brffer && this.audioManager.duration) {

this.bufferedVal = this.audioManager.buffered;

this.bufferedWidthVal =

`${parseFloat(brffer / currentTimeBack) *

600 *

this.dpi}px`;

}

this.lineAnimate(linePointMovePx);

},

lineAnimate(movepx) {

const animation = wx.createAnimation({

duration: 250,

timingFunction: 'ease-out'

});

const animate = animation.translate(`${movepx}`, '-50%').step();

this.animatedata = animate.export();

},

// 时间转换

stotime(s) {

let t = '';

let ss = s;

if (ss > -1) {

if (s > 0 && s < 1) {

ss = 1;

}

const min = Math.floor(ss / 60) % 60;

const sec = Math.floor(ss % 60);

if (min < 10) {

t += '0';

}

t += `${min}:`;

if (sec < 10) {

t += '0';

}

t += sec;

}

return t;

}

}

};

</script>



<style scoped>

.box {

width: 200rpx;

height: 20rpx;

background: red;

position: absolute;

top: 50%;

left: 0;

}

.audio_box {

width: 100vw;

height: 400rpx;

position: absolute;

bottom: 0;

left: 0;

background: #fff;

}

.audio_details_box {

width: 750rpx;

height: 90rpx;

background: #ccc;

display: flex;

align-items: center;

justify-content: space-between;

}

.words_count,

.switch_en_ch {

font-size: 30rpx;

color: rgba(51, 51, 51);

}

.words_count {

margin-left: 40rpx;

}

.switch_en_ch {

margin-right: 40rpx;

}

.sliderbox {

width: 600rpx;

height: 50rpx;

margin: 20rpx auto;

position: relative;

overflow-x: hidden;

}

.slider,

.sliderbg,

.sliderbufferedbg,

.sliderLine {

width: 600rpx;

height: 20rpx;

position: absolute;

top: 50%;

margin: 0;

transform: translateY(-50%);

left: 0;

}

.sliderbufferedbg {

background: red;

width: 0;

z-index: 4;

}

.slider {

top: 60rpx;

}

.sliderbg {

background: rgba(206, 206, 206, 1);

z-index: 3;

}

.sliderLine {

background: rgba(64, 64, 64, 1);

transform: translate(calc(-100% + 36rpx), -50%);

z-index: 4;

}

.sliderPoint {

position: absolute;

right: -2rpx;

top: -10rpx;

width: 40rpx;

height: 40rpx;

background: rgba(64, 64, 64, 1);

border-radius: 50%;

}

.audio_control_box{

width: 80%;

height: 120rpx;

margin:0 auto;

display: flex;

align-items: center;

justify-content:space-around;

}

.audio_play_bt,.audio_pre,.audio_next {

width: 40rpx;

height: 40rpx;

}

.currentTimeBack,

.currentTimeForward {

position: absolute;

right: 30rpx;

top: 60rpx;

font-size: 32rpx;

font-weight: 500;

color: black;

width: 100rpx;

height: 50rpx;

text-align: center;

}

.currentTimeForward {

left: 30rpx;

}

.waitingbt{

animation:rotate 3s linear infinite;



}

@keyframes rotate2{

from{transform: rotate(0deg)}

to{transform: rotate(359deg)}

}

</style>

 


最后的样子:


音频播放实现暂停开始等 }); btnStop.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub musicStop(); } }); player.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { // TODO Auto-generated method stub if(currentPlayMusicNum<musicList.size()){ currentPlayMusicNum+=1; }else{ currentPlayMusicNum=0; } musicStart(currentPlayMusicNum); } }); lvShowMusicList.setOnItemClickListener(new OnItemClickListener() { @SuppressLint("ResourceAsColor") @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if(saveView==view){ saveView.setBackgroundResource(R.color.nocolor); currentPlayMusicNum=0; } else{ if(saveView!=null) saveView.setBackgroundResource(R.color.nocolor); view.setBackgroundResource(R.color.colorblue); saveView=view; currentPlayMusicNum=position; musicStart(currentPlayMusicNum); } //id_this=position; } }); sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // TODO Auto-generated method stub if(fromUser){ sb.setProgress(progress); //musicPause(); MusicModel music=musicList.get(currentPlayMusicNum); music.setPlayTime(music.getAllTime()*progress/100); //pausePosition=(int) (music.getAllTime()*progress/100); player.seekTo((int) music.getPlayTime()); player.start(); } } }); }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值