<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
@import url(slider.css);
body {
margin: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #333333;
}
.player {
width: 1000px;
}
.music-item {
width: 100%;
height: 60px;
color: #fff;
font-weight: bolder;
background-color: #333333;
display: flex;
align-items: center;
}
.music-item>div:nth-child(1) {
width: 300px;
padding-left: 30px;
}
.music-item>div:nth-child(2) {
padding-left: 8px;
width: 200px;
color: #656565;
}
.music-item:nth-child(2n) {
background-color: #2d2d2d;
}
.player-controls {
height: 100px;
background-color: #222222;
display: flex;
align-items: center;
}
.btns {
width: 150px;
height: 100%;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.btns>img {
height: 24px;
}
.btns>img:nth-child(2) {
height: 30px;
}
.cover>.img {
width: 80px;
height: 80px;
background-image: url('./img/cover/山根ミチル - Beginning(アイテムBGM1・悪魔城伝説より) [VAMPIRE KILLER (MD) ].jpg');
background-size: cover;
background-position: center;
}
.name-row {
height: 40px;
display: flex;
justify-content: space-between;
align-items: center;
}
.left {
display: flex;
}
.left>div {
width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tool-bar {
width: 76px;
display: flex;
align-items: center;
justify-content: space-between;
}
.tool-bar>img {
width: 20px;
}
.progress-bar {
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
}
.progress {
width: 495px;
}
.volumn {
width: 100px;
}
.p-container {
position: relative;
}
input[type=range] {
position: relative;
}
.pro-bg {
width: 100%;
height: calc(100% - 8px);
position: absolute;
top: 5px;
left: 0;
background-color: #6b6b6c;
border-radius: 5px;
background-image: linear-gradient(to right, #6b6b6c, #6b6b6c 50%, #131313 50%);
}
</style>
</head>
<body>
<div class="player">
<!-- 播放列表 -->
<div class="music-list">
<!-- 列表项 -->
<div class="music-item">
<div>知足</div>
<div>五月天</div>
<div>热门华语85</div>
</div>
<div class="music-item">
<div>知足</div>
<div>五月天</div>
<div>热门华语85</div>
</div>
<div class="music-item">
<div>知足</div>
<div>五月天</div>
<div>热门华语85</div>
</div>
<div class="music-item">
<div>知足</div>
<div>五月天</div>
<div>热门华语85</div>
</div>
<div class="music-item">
<div>知足</div>
<div>五月天</div>
<div>热门华语85</div>
</div>
</div>
<!-- 播放器 -->
<div class="player-controls">
<!-- 播放按钮组合 -->
<div class="btns">
<img src="./img/bg-backward.svg" />
<img src="./img/play.svg" />
<img src="./img/bg-forward.svg" />
</div>
<!-- 专辑封面图 -->
<div class="cover">
<div class="img"></div>
</div>
<div style="flex-grow: 1; padding-left: 20px; padding-right: 15px;">
<!-- 歌名行 -->
<div class="name-row">
<!-- 左侧 -->
<div class="left">
<div class="music-name" style="color: #fff">山根ミチル - Beginning(アイテムBGM1・悪魔城伝説より) [VAMPIRE KILLER
(MD) ]</div>
<span class="music-author" style="color: #6b6b6c">山根ミチル</span>
<img style="width: 24px;" src="./img/link.svg" alt="">
</div>
<!-- 右侧工具栏 -->
<div class="tool-bar">
<img src="./img/open.svg" />
<img style="width: 30px" src="./img/random.svg" />
<img src="./img/list.svg" />
</div>
</div>
<!-- 进度条行 -->
<div class="progress-bar">
<!-- 进度条 -->
<div class="p-container progress">
<div class="pro-bg"></div>
<input type="range" min="0" max="1" step="any">
</div>
<!-- 进度值 -->
<div>
<span style="color: #6b6b6c;" class="current-time">00:00</span><span
style="color: #5b5b5b;">/<span class="total-time">00:00</span></span>
</div>
<img class="muted" style="width: 20px;" src="img/volumn.svg" />
<!-- 音量进度条 -->
<div class="p-container volumn">
<div class="pro-bg"></div>
<input type="range" min="0" max="1" step="any">
</div>
</div>
</div>
</div>
</div>
</body>
<script>
const proInp = document.querySelector('.progress input')
const volInp = document.querySelector('.volumn input')
const proBg = document.querySelector('.progress>.pro-bg')
const volBg = document.querySelector('.volumn>.pro-bg')
const ML = document.querySelector('.music-list')
const playBtn = document.querySelector('.btns>img:nth-child(2)')
const prevBtn = document.querySelector('.btns>img:nth-child(1)')
const nextBtn = document.querySelector('.btns>img:nth-child(3)')
const coverEl = document.querySelector('.cover>.img')
const musicName = document.querySelector('.music-name')
const musicAuthor = document.querySelector('.music-author')
const currentTime = document.querySelector('.current-time')
const totalTime = document.querySelector('.total-time')
const mutedBtn = document.querySelector('.muted')
// 音乐播放器
let audio = new Audio()
// 当前正被加载到播放器中的音乐索引
let currentIndex = 0
let timer
// 音乐列表数据
let musicList = [
{
name: 'Beginning(アイテムBGM1・悪魔城伝説より) [VAMPIRE KILLER (MD) ]',
author: '山根ミチル',
group: '悪魔城伝説より',
cover: './img/cover/山根ミチル - Beginning(アイテムBGM1・悪魔城伝説より) [VAMPIRE KILLER (MD) ].jpg',
src: './audio/山根ミチル - Beginning(アイテムBGM1・悪魔城伝説より) [VAMPIRE KILLER (MD) ].mp3'
},
{
name: 'Bloddy Tears(アイテムBGM2・ドラキュラ2より) [VAMPIRE KILLER (MD) ]',
author: '山根ミチル',
group: 'ドラキュラ2より',
cover: './img/cover/山根ミチル - Bloddy Tears(アイテムBGM2・ドラキュラ2より) [VAMPIRE KILLER (MD) ].jpg',
src: './audio/山根ミチル - Bloddy Tears(アイテムBGM2・ドラキュラ2より) [VAMPIRE KILLER (MD) ].mp3'
},
{
name: 'Amber Ale',
author: 'Borislav Slavov',
group: 'Divinity: Original Sin 2',
cover: './img/cover/divinity-original-sin2.jpg',
src: './audio/Borislav Slavov - Amber Ale.mp3'
},
{
name: 'Welcome to Fort Joy',
author: 'Borislav Slavov',
group: 'Divinity: Original Sin 2',
cover: './img/cover/divinity-original-sin2-2.jpg',
src: './audio/Borislav Slavov - Welcome to Fort Joy.mp3'
},
]
// ------------------------------------- 事件 -------------------------------------------
// audio 相关事件: https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/audio#%E4%BA%8B%E4%BB%B6
// 总时长发生变化事件
audio.addEventListener('durationchange', () => {
// 设置总时长
totalTime.textContent = getMusicTime(audio.duration)
})
// 音乐播放完成事件
audio.addEventListener('ended', () => {
audio.pause()
playBtn.src = './img/play.svg'
})
// 给进度条绑定input事件
proInp.addEventListener('input', () => {
// 设置播放器的当前时间
audio.currentTime = audio.duration * Number(proInp.value)
setProBg()
})
volInp.addEventListener('input', () => {
audio.volume = Number(volInp.value)
// 根据音量判断是否静音
audio.muted = audio.volume === 0
// 修改图标
mutedBtn.src = audio.muted ? './img/muted.svg' : './img/volumn.svg'
setVolBg()
})
playBtn.addEventListener('click', () => {
// audio.paused 代表音乐是否暂停
if (audio.paused) {
audio.play()
// 开始更新播放进度
clearInterval(timer)
timer = setInterval(() => {
// 计算进度值
proInp.value = audio.currentTime / audio.duration
setProBg()
// 更新当前时间
currentTime.textContent = getMusicTime(audio.currentTime)
}, 200)
playBtn.src = './img/pause.svg'
} else {
audio.pause()
playBtn.src = './img/play.svg'
}
})
// 上一首
prevBtn.addEventListener('click', () => {
let i = currentIndex - 1
i = i < 0 ? musicList.length - 1 : i
play(i)
})
// 下一首
nextBtn.addEventListener('click', () => {
let i = currentIndex + 1
i = i > musicList.length - 1 ? 0 : i
play(i)
})
// 静音按钮点击事件
mutedBtn.addEventListener('click', () => {
// 静音
audio.muted = !audio.muted
// 修改图标
mutedBtn.src = audio.muted ? './img/muted.svg' : './img/volumn.svg'
// 设置进度条
volInp.value = audio.muted ? 0 : audio.volume
setVolBg()
})
// -------------------------------------- 函数 -----------------------------------------------
// 获取音乐事件的格式化
function getMusicTime(time) {
let m = Math.floor(time / 60)
let s = Math.round(time % 60)
return `${m < 10 ? '0' + m : m}:${s < 10 ? '0' + s : s}`
}
// 播放音乐
// index: 音乐的索引
function play(index) {
// 若要播放的音乐就是当前音乐 则返回
if (index === currentIndex) return
audio.pause()
// 切换当前索引
currentIndex = index
initAudio(currentIndex)
// 播放音乐
audio.play()
// 开始更新播放进度
clearInterval(timer)
timer = setInterval(() => {
// 计算进度值
proInp.value = audio.currentTime / audio.duration
setProBg()
// 更新当前时间
currentTime.textContent = getMusicTime(audio.currentTime)
}, 200)
playBtn.src = './img/pause.svg'
}
// 设置进度条
function setProBg() {
let value = Number(proInp.value)
proBg.style.backgroundImage = `linear-gradient(to right, #6b6b6c, #6b6b6c ${value * 100}%, #131313 ${value * 100}%)`
}
// 设置音量
function setVolBg() {
let value = Number(volInp.value)
volBg.style.backgroundImage = `linear-gradient(to right, #6b6b6c, #6b6b6c ${value * 100}%, #131313 ${value * 100}%)`
}
// 创建音乐列表
function buildMusicList() {
// 数组的map函数可以将一个数组中每个成员映射到另一个数组中
// 映射的内容取决于函数内的返回值
// map函数的返回值是一个新的数组
let r = musicList.map(music => {
return `
<div class="music-item">
<div>${music.name}</div>
<div>${music.author}</div>
<div>${music.group}</div>
</div>
`
})
// 使用数组的join函数将字符串数组成员用一个字符连接起来
r = r.join('')
ML.innerHTML = r
// 查询元素绑定点击事件
let musicItems = document.querySelectorAll('.music-item')
for (let i = 0; i < musicItems.length; i++) {
const musicItem = musicItems[i];
musicItem.addEventListener('dblclick', ev => {
play(i)
})
}
}
// 初始化音乐播放器
function initAudio(index) {
let music = musicList[index]
audio.src = music.src
// 初始化封面图等音乐信息
coverEl.style.backgroundImage = `url('${music.cover}')`
musicName.textContent = music.name
musicAuthor.textContent = music.author
}
// 初始化函数
function init() {
proInp.value = 0
volInp.value = 1
setProBg()
setVolBg()
buildMusicList()
initAudio(currentIndex)
}
init()
</script>
</html>
音乐播放器
最新推荐文章于 2024-08-12 22:06:26 发布