工作之余查了很多资料一步步摸索出来的,先感谢各位大佬。
仿QQ音乐做的,不是100%还原,不过基本原理都在这了,结合之前的歌词滚动组件一起使用
持续更新
效果图
页面布局拆分
总体分为
1顶部歌名+歌手的top-bar模块+模糊背景
2专辑+歌词滚动模块
3进度条+时间模块
4播放控件模块,播放,暂停,上一曲,下一曲。
需要注意音频初始化audioInit
mounted() {
const audio = document.getElementById("audio");
this.init();
this.audioInit();
this.swiper = new Swiper(this.activeClass, {
autoplay: false, //自动播放
loop: false, //循环播放
});
},
method: {
audioInit() {
let that = this;
// 音频或视频文件已经就绪可以开始
audio.addEventListener("canplay", () => {
console.log("canplay");
that.audioDuration = that.TimeToString(audio.duration);
let buffered = audio.buffered.end(0)
});
let progressL = this.$refs.track.offsetWidth; // 进度条总长
audio.addEventListener("timeupdate", () => {
// 当前播放时间
let compareTime = audio.currentTime;
let buffered = audio.buffered.end(0)
for (let i = 0; i < that.lyricInfo.length; i++) {
if (compareTime > parseInt(that.lyricInfo[i].time)) {
const index = that.lyricInfo[i].index;
if (i === parseInt(index)) {
that.lyricIndex = i; //获取当前播放歌曲的歌词index
}
}
}
that.currentTime = that.TimeToString(audio.currentTime);
that.audioPercent =
audio.currentTime / audio.duration.toFixed(3);
that.thumbTranslateX = that.audioPercent * 4;
});
audio.addEventListener("ended", () => {
that.playIndex =
that.playIndex + 1 >= that.songList.length
? 0
: that.playIndex + 1;
that.songInfo = that.songList[that.playIndex];
that.GetLyric(that.songInfo.id);
setTimeout(() => {
audio.play();
}, 100);
});
},
}
第一部分
需要定一个固定在页面的div,采用fixed布局
<template>
<div class="main-page" ref="MainRef">
<div
class="background-flitter"
:style="`background-image: url(${songInfo.cover})`"
></div>
<div class="top-bar">
<p>{{ songInfo.name }}</p>
<p style="font-size: 0.24rem;font-weight: 500">{{ songInfo.artistsName }}</p>
</div>
</div>
</template>
具体的歌曲信息如下
data: (){
return {
{
albumId: 93162249,
albumTitle: "STRAY SHEEP",
artistsName: "米津玄師",
cover:"https://p1.music.126.net/6mhlWCOOQkT0xDjjuCLW7g==/109951165181187586.jpg",
id: 1466598056,
index: 7,
name: "Lemon",
url:"https://music.163.com/song/media/outer/url?id=1466598056.mp3",
},
}
}
<style lang="less" scoped>
.main-page {
width: 100%;
height: 100vh;
position: relative;
background: rgba(15, 15, 15, 0.3);
.background-flitter {
position: fixed;
z-index: -2;
background-repeat: no-repeat;
width: 100%;
height: 100vh;
top: 0;
left: 0;
background-size: cover;
background-position: 50%;
filter: blur(0.16rem);
opacity: 0.7;
overflow: hidden;
box-sizing: border-box;
}
.top-bar {
width: 100%;
height: 1.2rem;
text-align: center;
color: #fff;
font-size: 0.32rem;
line-height: 0.6rem;
font-weight: 600;
}
}
</style>
至此完成整体背景和top-bar部分
第二部分,专辑+歌词滚动
左右滑动切换用到了swiper组件,专辑+歌词和纯歌词分别放在两个slide就可以了。
先固定一个歌词容器,与top-bar同级,具体代码可以参考歌词组件封装来看,都是复用的代码。
<div class="lyric-container">
<div class="swiper-contain">
<div class="swiper-wrapper">
<div class="swiper-slide">
<div class="disc-cover">
<div class="disc" ref="rotate">//ref="rotate"
<img :src="songInfo.cover" alt="" />
</div>
</div>
<div class="text-container">
<div class="text-list" :style="lyricMiniTop">
<p
v-for="(item, index) in lyricInfo"
:key="index"
:style="{
color:
lyricIndex === index
? colorLight
: color,
}"
>
{{ item.lyric }}
</p>
</div>
</div>
</div>
<div class="swiper-slide">
<l-scroll
ref="lyric"
:color="color"
:colorLight="colorLight"
:lineHeight="lineHeight"
:paddingTop="paddingTop"
:fontSize="fontSize"
:lyricIndex="lyricIndex"
:lyricsList="lyricInfo"
></l-scroll>
</div>
</div>
</div>
</div>
歌词滚动之前文章有讲过了,提一下这个唱片旋转的动画吧,给旋转部分添加ref="rotate"
.disc {
width: 5rem;
height: 5rem;
border-radius: 50%;
box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.4);
animation: animations1 12s linear infinite forwards;
animation-play-state: paused;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
@keyframes animations1 {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
在播放事件中添加
//播放与暂停
play() {
if (this.playing) {
// 播放中,点击则为暂停
this.playing = false;
this.$refs.rotate.style.animationPlayState = "paused";
audio.pause();
} else {
// 暂停中,点击则为播放
this.playing = true;
this.$refs.rotate.style.animationPlayState = "running";
audio.play();
}
},
到这完成了歌词滚动和切换
第三部分,进度条点击和拖拽控制播放进度(重点)
样式,时长比这些都讲烂了,讲讲点击和拖拽控制播放进度吧
拖拽功能:给拖拽按钮,也就是示例图的白色进度按钮添加三个事件
<div
class="play-point"
@touchstart.stop.prevent="touchstart"
@touchmove.stop.prevent="touchmove"
@touchend.stop.prevent="touchend"
:style="{
transform: 'translateX(' + thumbTranslateX + 'rem)',
}"
></div>
//控制播放进度
SetProgress(t) {
audio.currentTime = audio.duration * t;
},
touchstart(event) {
let progressL = this.$refs.track.offsetWidth ; // 进度条总长
let allL = this.$refs.MainRef.offsetWidth ; // 页面总长
let half = (allL-progressL)/2
console.log("开始", progressL, allL, half)
//记录开始的X轴坐标
if (this.startXFirst) {
this.startX = half;
this.startXFirst = false;
}
},
touchmove(event) {
let moveX = parseInt(
(event.changedTouches[0].clientX - this.startX)
);
let progressL = this.$refs.track.offsetWidth; // 进度条总长
let percent = (moveX / progressL).toFixed(2); //拖动%比
if(percent>1){
percent = 1
}
this.audioPercent = percent; //音频播放%
this.thumbTranslateX = this.audioPercent * 4; //计算滑块位移
},
touchend(event) {
console.log("结束",event)
this.finalX = parseInt(
event.changedTouches[0].clientX - this.startX
);
let progressL = this.$refs.track.offsetWidth; // 进度条总长
let time = (this.finalX / progressL).toFixed(2);
if(time>1){
time = 1
}
this.SetProgress(time);
},
点击实现进度则简单多了,
<div
class="progress"
@click="HandleProgressClick($event)"
ref="track"
>
<div
class="progress_box"
:style="{ width: audioProgressPercent }"
></div>
<div
class="play-point"
@touchstart.stop.prevent="touchstart"
@touchmove.stop.prevent="touchmove"
@touchend.stop.prevent="touchend"
:style="{
transform: 'translateX(' + thumbTranslateX + 'rem)',
}"
></div>
</div>
//点击进度条
HandleProgressClick(event) {
let progressL = this.$refs.track.offsetWidth; // 进度条总长
let clickX = event.offsetX;
let time = (clickX / progressL).toFixed(2);
this.SetProgress(time);
},
OK ,第三部分完成