javascript
<template>
<div>
<div class="audio-progress" v-if="isShowAudioProgress">
<!-- 顶部按钮-进度条 -->
<div class="play-icon-box" @click="playAudio">
<em class="play-icon" :class="{ 'active': isPlay }"></em>
</div>
<div class="currentTime">{{ audioCurrentTime }}</div>
<div class="progress-box">
<div class="progress-bar">
<span class="blue-progress" :style="{ 'width': progress + '%' }"></span>
<span class="dot" v-show="isDot" :style="{ 'left': dotProgress + '%' }"
@touchstart.prevent="startDrag"></span>
<span class="progress"></span>
</div>
</div>
<div class="allTime">{{ audioAllTime }}</div>
<p class="audio-tip" v-show="isAudioTip">点击这里开始收听新闻</p>
</div>
<!-- 悬浮按钮-圆环 -->
<div v-show="isShowCircle" class="audio-circle" :class="{ 'active': isCirclePlay }">
<div class="close-icon" @click="rePlayFn">
<img src="./images/close-icon.png" alt="">
</div>
<div class="circle-box" @click="playAudio">
<canvas id="audioCanvas" style="width:100%"></canvas>
</div>
</div>
</div>
</template>
<script>
import { setCookie, getCookie } from "@/util/common/cookie";
export default {
name: "audioProgress",
props: ["isShowAudioProgress"],
data() {
return {
dotProgress: -5,
progress: 0, //音频进度百分比
audioCurrentTime: '00:00', //音频当前播放时间
audioAllTime: '', //音频总播放时间
isPlay: false, //是否正在播放
isAudioTip: false,
isWidth: 0,
timer: null,
isDot: true, ///测试 先显示出来
//圆环
isCirclePlay: false, // 圆环是否正在播放
isShowCircle: false,
circleTimer: null,
ctx: null,
isDragging: false, // 是否正在拖动
dragStartX: 0, // 拖动开始时的X坐标
moveMinX: 0,
moveMaxX: 0,
progressBarWidth: 0,
audioPlayer: null,
}
},
mounted() {
this.$nextTick(() => {
this.showTip();
// 获取音频时间
this.changeProgress();
this.clearFill();
})
},
methods: {
rePlayFn() {
this.dotProgress = -5;
this.progress = 0;
this.isPlay = false;
this.isCirclePlay = false;
this.isShowCircle = false;
this.audioCurrentTime = "00:00";
this.audioPlayer.currentTime = 0;
//暂停app的语音播放
clearInterval(this.timer);
this.audioPlayer.pause();
},
startDrag(event) {
// 确保音频已加载
this.audioPlayer = document.getElementById('detailAudioPlayer');
if (!this.audioPlayer || isNaN(this.audioPlayer.duration)) return;
// 获取进度条宽度(在拖动开始时获取)
const progressBar = document.querySelector('.progress-bar');
if (!progressBar) return;
this.progressBarWidth = progressBar.offsetWidth;
this.moveMinX = progressBar.getBoundingClientRect().left;
this.moveMaxX = progressBar.getBoundingClientRect().width + this.moveMinX;
this.dragStartX = event.touches[0].clientX;
this.isDragging = true;
// 阻止默认行为(防止选中文本)
event.preventDefault();
// 添加全局事件监听
document.addEventListener('touchmove', this.dragging);
document.addEventListener('touchend', this.stopDrag);
},
dragging(event) {
if (!this.isDragging) return;
// 计算新的进度百分比
const mouseX = event.touches[0].clientX;
// console.log(mouseX, 'mousex', event);
if (mouseX < this.moveMinX) {
this.progress = 0;
this.dotProgress = 0 - 5;
} else if (mouseX > this.moveMaxX) {
this.progress = 100;
this.dotProgress = 100 - 5;
} else {
const newProgress = ((mouseX - this.moveMinX) / this.progressBarWidth) * 100;
this.progress = newProgress;
this.dotProgress = newProgress - 5;
}
this.audioCurrentTime = this.realFormatSecond((this.audioPlayer.duration * (this.progress / 100)).toFixed(2));
this.drawCircle(this.progress)
},
stopDrag() {
this.isDragging = false;
document.removeEventListener('touchmove', this.dragging);
document.removeEventListener('touchend', this.stopDrag);
// 设置音频实际播放位置
if (this.audioPlayer && this.audioPlayer.duration > 0) {
this.audioPlayer.currentTime = (this.audioPlayer.duration * (this.progress / 100)).toFixed(2);
}
},
showTip() {
let isFirst = getCookie("audio-isFirst");
if (!isFirst) {
this.isAudioTip = true; //首次才显示tip
setTimeout(() => {
this.isAudioTip = false;
}, 4000);
setCookie("audio-isFirst", true, '365');
} else {
this.isAudioTip = false;
}
},
//设置定时检测
setAudioInterval() {
let audioPlayer = document.getElementById('detailAudioPlayer');
/*语音播放结束*/
audioPlayer.addEventListener("ended",
() => {
this.dotProgress = -5;
this.progress = 0;
this.isPlay = false;
this.isCirclePlay = false;
this.drawCircle(0)
this.audioCurrentTime = "00:00";
}
);
this.timer = setInterval(() => {
const n = audioPlayer.currentTime / audioPlayer.duration;
let i = (n * 100).toFixed(2);
if (i >= 100) {
clearInterval(this.timer);
}
if (!this.isDragging) {
this.drawCircle(i);
this.progress = i;
this.dotProgress = i - 5;
this.audioCurrentTime = this.realFormatSecond(audioPlayer.currentTime);
this.audioAllTime = this.realFormatSecond(audioPlayer.duration);
}
}, 30);
},
//播放
playAudio() {
this.isPlay = !this.isPlay;
this.isCirclePlay = !this.isCirclePlay;
this.isDot = true;
let audioPlayer = document.getElementById('detailAudioPlayer');
this.isShowCircle = true; //点击播放按钮才显示悬浮按钮
if (this.isPlay) {
this.setAudioInterval();
audioPlayer.play();
} else {
//暂停
clearInterval(this.timer);
audioPlayer.pause();
}
},
//获取播放时间
changeProgress() {
let audioPlayer = document.getElementById('detailAudioPlayer');
this.audioAllTime = this.realFormatSecond(audioPlayer.duration);
this.audioCurrentTime = this.realFormatSecond(audioPlayer.currentTime);
},
//格式化秒
realFormatSecond(time) {
//分钟
if (isNaN(time)) {
return "";
}
let minute = parseInt(time / 60);
if (minute < 10) {
minute = "0" + minute;
}
//秒
let second = Math.round(time % 60);
if (second < 10) {
second = "0" + second;
}
return minute + ":" + second;
},
//绘制圆环
drawCircle(i) {
let canvas = document.getElementById('audioCanvas');
//起始一条路径
this.ctx = canvas.getContext('2d');
canvas.width = 100;
canvas.height = 100;
this.ctx.beginPath();
//连接处样式
this.ctx.lineCap = 'round';
//设置当前线条的宽度
this.ctx.lineWidth = 10; //10px
//设置笔触的颜色
this.ctx.strokeStyle = '#ff4343';
//arc(圆心x,圆心y,半径,开始角度,结束角度)
//设置开始处为0点钟方向(-90 * Math.PI / 180)
//n为百分比值(0-100)
this.ctx.arc(50, 50, 42, -90 * Math.PI / 180, (i * 3.6 - 90) * Math.PI / 180);
//绘制已定义的路径
this.ctx.stroke(); //绘制
this.ctx.closePath(); //路径结束
this.ctx.restore();
},
clearFill() {
this.circleTimer = null;
const canvas = document.getElementById('audioCanvas');
canvas.width = 0;
canvas.height = 0;
}
}
}
</script>
<style scoped>
.audio-circle {
position: fixed;
bottom: 8rem;
right: 4vw;
display: flex;
align-items: center;
justify-content: center;
gap: 4.2vw;
width: 23.644vw;
height: 12.8vw;
background-color: #ffffff;
border-radius: 1.067vw;
box-shadow: 0vw 0vw 1.778vw 0vw rgba(0, 0, 0, 0.2);
z-index: 99;
}
.audio-circle .close-icon {
width: 5.333vw;
height: 5.333vw;
display: flex;
align-items: center;
justify-content: center;
}
.audio-circle .close-icon img {
width: 3.733vw;
height: auto;
}
.audio-circle .circle-box {
width: 7.111vw;
height: 7.111vw;
background: url("./images/news-pause1.png") no-repeat center;
background-size: 100%;
}
.audio-circle.active .circle-box {
background: url("./images/news-play1.png") no-repeat center;
background-size: 100%;
}
.audio-tip {
width: 11.1rem;
height: 2.66rem;
border-radius: .33rem;
background: rgba(0, 0, 0, .8);
position: absolute;
left: 2%;
bottom: -2.8rem;
font-size: 0.933rem;
color: #fff;
text-align: center;
line-height: 2.66rem;
z-index: 99;
}
.audio-tip::after {
position: absolute;
top: -0.4rem;
left: 10%;
border-left: .4rem solid transparent;
border-right: .4rem solid transparent;
border-bottom: .4rem solid rgba(0, 0, 0, .8);
content: " ";
display: block;
width: 0;
height: 0;
}
.audio-progress {
position: relative;
width: 92vw;
height: 10.667vw;
margin-left: 4vw;
background-color: #ffffff;
border-radius: 5.333vw;
border: solid 1px #e5e5e5;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 5vw;
}
.play-icon-box {
width: 6.489vw;
height: 6.489vw;
margin-right: 4.3vw;
}
.play-icon-box .play-icon {
display: block;
width: 6.489vw;
height: 6.489vw;
background: url("./images/news-pause.png") no-repeat center;
background-size: 100%;
flex-shrink: 0;
margin: 0 auto 0.13rem;
cursor: pointer;
}
.play-icon.active {
background: url("./images/news-play.png") no-repeat center;
background-size: 100%;
}
.currentTime,
.allTime {
font-size: 2.667vw;
color: #333333;
}
.progress-box {
width: 56.089vw;
height: 0.8vw;
margin: 0 2.6vw;
}
.progress-bar {
width: 100%;
position: relative;
height: 0.8vw;
}
.blue-progress {
position: absolute;
width: 0;
height: 0.8vw;
border-radius: 0.8vw;
background-color: #ff4343;
z-index: 99;
}
.dot {
position: absolute;
left: -2%;
top: 50%;
width: 8vw;
height: 8vw;
background: url("./images/news-dot.png") no-repeat center center;
background-size: 50%;
margin-top: -4vw;
z-index: 100;
}
.progress {
position: absolute;
width: 100%;
height: 0.8vw;
border-radius: 0.8vw;
background-color: #f7e3e5;
box-shadow: 0px 0px 0.13rem 0px rgba(0, 0, 0, .3);
z-index: 98;
}
.progress-text {
display: flex;
display: -webkit-flex;
align-items: center;
justify-content: space-between;
margin-top: .4rem;
}
.progress-text span {
font-size: .6rem;
color: #000;
}
</style>


底部圆环是联动的进度