<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>音乐播放器与猫猫按钮</title>
<!-- 引入Font Awesome图标库 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
/* 音乐播放器样式 */
#music-player-container {
position: fixed; /* 固定定位 */
bottom: 120px; /* 距离底部20px */
right: 400px; /* 距离右侧20px */
width: 430px; /* 宽度 */
height: 100px; /* 高度 */
z-index: 9999; /* 层级非常高 */
opacity: 0; /* 初始透明 */
transform: translateY(20px); /* 初始下移20px */
transition: all 0.3s ease; /* 过渡动画 */
}
/* 显示时的音乐播放器样式 */
#music-player-container.show {
opacity: 1; /* 完全不透明 */
transform: translateY(0); /* 回到原位 */
}
/* 专辑背景图样式 */
#player-bg-artwork {
position: fixed; /* 固定定位 */
top: -30px; /* 上边距 */
right: -30px; /* 右边距 */
bottom: -30px; /* 下边距 */
left: -30px; /* 左边距 */
background-image: url("https://raw.githubusercontent.com/himalayasingh/music-player-1/master/img/_1.jpg");
background-repeat: no-repeat; /* 不重复 */
background-size: cover; /* 覆盖整个区域 */
background-position: 50%; /* 居中 */
filter: blur(40px); /* 模糊效果 */
-webkit-filter: blur(40px); /* 兼容webkit */
z-index: 1; /* 层级 */
}
/* 背景遮罩层 */
#player-bg-layer {
position: fixed; /* 固定定位 */
top: 0; /* 顶部对齐 */
right: 0; /* 右侧对齐 */
bottom: 0; /* 底部对齐 */
left: 0; /* 左侧对齐 */
background-color: #fff; /* 白色背景 */
opacity: 0.5; /* 半透明 */
z-index: 2; /* 层级 */
}
/* 播放器主容器 */
#player {
position: relative; /* 相对定位 */
height: 100%; /* 高度100% */
z-index: 3; /* 层级 */
}
/* 播放器轨道信息区域 */
#player-track {
position: absolute; /* 绝对定位 */
top: 0; /* 顶部对齐 */
right: 15px; /* 右边距 */
left: 15px; /* 左边距 */
padding: 13px 22px 10px 184px; /* 内边距 */
background-color: #fff7f7; /* 背景色 */
border-radius: 15px 15px 0 0; /* 圆角 */
transition: 0.3s ease top; /* 顶部过渡动画 */
z-index: 1; /* 层级 */
}
/* 激活状态的轨道信息区域 */
#player-track.active {
top: -92px; /* 上移92px */
}
/* 专辑名称样式 */
#album-name {
color: #54576f; /* 文字颜色 */
font-size: 17px; /* 字体大小 */
font-weight: bold; /* 加粗 */
}
/* 曲目名称样式 */
#track-name {
color: #acaebd; /* 文字颜色 */
font-size: 13px; /* 字体大小 */
margin: 2px 0 13px 0; /* 外边距 */
}
/* 时间显示区域 */
#track-time {
height: 12px; /* 高度 */
margin-bottom: 3px; /* 下边距 */
overflow: hidden; /* 溢出隐藏 */
}
/* 当前时间 */
#current-time {
float: left; /* 左浮动 */
}
/* 总时长 */
#track-length {
float: right; /* 右浮动 */
}
/* 时间显示通用样式 */
#current-time,
#track-length {
color: transparent; /* 文字透明 */
font-size: 11px; /* 字体大小 */
background-color: #ffe8ee; /* 背景色 */
border-radius: 10px; /* 圆角 */
transition: 0.3s ease all; /* 过渡动画 */
}
/* 激活状态的时间显示 */
#track-time.active #current-time,
#track-time.active #track-length {
color: #f86d92; /* 文字颜色 */
background-color: transparent; /* 透明背景 */
}
/* 进度条容器和进度条 */
#seek-bar-container,
#seek-bar {
position: relative; /* 相对定位 */
height: 4px; /* 高度 */
border-radius: 4px; /* 圆角 */
}
/* 进度条容器 */
#seek-bar-container {
background-color: #ffe8ee; /* 背景色 */
cursor: pointer; /* 鼠标指针 */
}
/* 悬停时间提示 */
#seek-time {
position: absolute; /* 绝对定位 */
top: -29px; /* 上边距 */
color: #fff; /* 白色文字 */
font-size: 12px; /* 字体大小 */
white-space: pre; /* 保留空白 */
padding: 5px 6px; /* 内边距 */
border-radius: 4px; /* 圆角 */
display: none; /* 默认隐藏 */
}
/* 悬停区域 */
#s-hover {
position: absolute; /* 绝对定位 */
top: 0; /* 顶部对齐 */
bottom: 0; /* 底部对齐 */
left: 0; /* 左侧对齐 */
opacity: 0.2; /* 透明度 */
z-index: 2; /* 层级 */
}
/* 悬停时间和悬停区域共用样式 */
#seek-time,
#s-hover {
background-color: #3b3d50; /* 背景色 */
}
/* 进度条 */
#seek-bar {
content: ""; /* 伪元素内容 */
position: absolute; /* 绝对定位 */
top: 0; /* 顶部对齐 */
bottom: 0; /* 底部对齐 */
left: 0; /* 左侧对齐 */
width: 0; /* 初始宽度0 */
background-color: #fd6d94; /* 背景色 */
transition: 0.2s ease width; /* 宽度过渡动画 */
z-index: 1; /* 层级 */
}
/* 播放器内容区域 */
#player-content {
position: relative; /* 相对定位 */
height: 100%; /* 高度100% */
background-color: #fff; /* 白色背景 */
box-shadow: 0 30px 80px #656565; /* 阴影 */
border-radius: 15px; /* 圆角 */
z-index: 2; /* 层级 */
}
/* 专辑封面 */
#album-art {
position: absolute; /* 绝对定位 */
top: -40px; /* 上移40px */
width: 115px; /* 宽度 */
height: 115px; /* 高度 */
margin-left: 40px; /* 左边距 */
transform: rotateZ(0); /* 初始旋转角度 */
transition: 0.3s ease all; /* 过渡动画 */
box-shadow: 0 0 0 10px #fff; /* 阴影 */
border-radius: 50%; /* 圆形 */
overflow: hidden; /* 溢出隐藏 */
}
/* 激活状态的专辑封面 */
#album-art.active {
top: -60px; /* 上移60px */
box-shadow: 0 0 0 4px #fff7f7, 0 30px 50px -15px #afb7c1; /* 阴影 */
}
/* 专辑封面中心点 */
#album-art:before {
content: ""; /* 伪元素内容 */
position: absolute; /* 绝对定位 */
top: 50%; /* 垂直居中 */
right: 0; /* 右侧对齐 */
left: 0; /* 左侧对齐 */
width: 20px; /* 宽度 */
height: 20px; /* 高度 */
margin: -10px auto 0 auto; /* 外边距 */
background-color: #d6dee7; /* 背景色 */
border-radius: 50%; /* 圆形 */
box-shadow: inset 0 0 0 2px #fff; /* 内阴影 */
z-index: 2; /* 层级 */
}
/* 专辑封面图片 */
#album-art img {
display: block; /* 块级元素 */
position: absolute; /* 绝对定位 */
top: 0; /* 顶部对齐 */
left: 0; /* 左侧对齐 */
width: 100%; /* 宽度100% */
height: 100%; /* 高度100% */
opacity: 0; /* 初始透明 */
z-index: -1; /* 层级 */
}
/* 激活状态的专辑封面图片 */
#album-art img.active {
opacity: 1; /* 完全不透明 */
z-index: 1; /* 层级 */
}
/* 激活状态下的旋转动画 */
#album-art.active img.active {
z-index: 1; /* 层级 */
animation: rotateAlbumArt 3s linear 0s infinite forwards; /* 旋转动画 */
}
/* 旋转动画定义 */
@keyframes rotateAlbumArt {
0% {
transform: rotateZ(0); /* 初始角度 */
}
100% {
transform: rotateZ(360deg); /* 旋转360度 */
}
}
/* 缓冲提示框 */
#buffer-box {
position: absolute; /* 绝对定位 */
top: 50%; /* 垂直居中 */
right: 0; /* 右侧对齐 */
left: 0; /* 左侧对齐 */
height: 13px; /* 高度 */
color: #1f1f1f; /* 文字颜色 */
font-size: 13px; /* 字体大小 */
font-family: Helvetica; /* 字体 */
text-align: center; /* 文字居中 */
font-weight: bold; /* 加粗 */
line-height: 1; /* 行高 */
padding: 6px; /* 内边距 */
margin: -12px auto 0 auto; /* 外边距 */
background-color: rgba(255, 255, 255, 0.19); /* 背景色 */
opacity: 0; /* 初始透明 */
z-index: 2; /* 层级 */
}
/* 专辑图片和缓冲提示框共用过渡效果 */
#album-art img,
#buffer-box {
transition: 0.1s linear all; /* 过渡动画 */
}
/* 缓冲状态的专辑图片 */
#album-art.buffering img {
opacity: 0.25; /* 透明度 */
}
/* 缓冲状态的激活专辑图片 */
#album-art.buffering img.active {
opacity: 0.8; /* 透明度 */
filter: blur(2px); /* 模糊效果 */
-webkit-filter: blur(2px); /* 兼容webkit */
}
/* 缓冲状态的缓冲提示框 */
#album-art.buffering #buffer-box {
opacity: 1; /* 完全不透明 */
}
/* 播放控制区域 */
#player-controls {
width: 250px; /* 宽度 */
height: 100%; /* 高度100% */
margin: 0 5px 0 141px; /* 外边距 */
float: right; /* 右浮动 */
overflow: hidden; /* 溢出隐藏 */
}
/* 控制按钮容器 */
.control {
width: 33.333%; /* 宽度1/3 */
float: left; /* 左浮动 */
padding: 12px 0; /* 内边距 */
}
/* 控制按钮 */
.button {
width: 26px; /* 宽度 */
height: 26px; /* 高度 */
padding: 25px; /* 内边距 */
background-color: #fff; /* 白色背景 */
border-radius: 6px; /* 圆角 */
cursor: pointer; /* 鼠标指针 */
}
/* 按钮图标 */
.button i {
display: block; /* 块级元素 */
color: #d6dee7; /* 图标颜色 */
font-size: 26px; /* 图标大小 */
text-align: center; /* 居中 */
line-height: 1; /* 行高 */
}
/* 按钮和图标共用过渡效果 */
.button,
.button i {
transition: 0.2s ease all; /* 过渡动画 */
}
/* 按钮悬停效果 */
.button:hover {
background-color: #d6d6de; /* 背景色变化 */
}
/* 按钮悬停时图标效果 */
.button:hover i {
color: #fff; /* 图标变白 */
}
/* 猫猫按钮样式 */
#maomao {
position: fixed; /* 固定定位 */
bottom: 500px; /* 距离底部 */
right: calc(50% - -305px); /* 水平居中偏移 */
width: 57px; /* 宽度 */
height: 70px; /* 高度 */
z-index: 0; /* 层级 */
pointer-events: auto; /* 允许鼠标事件 */
transition: all 0.5s ease; /* 过渡动画 */
cursor: pointer; /* 鼠标指针 */
}
/* 猫猫SVG图标 */
#maomao svg {
width: 100%; /* 宽度100% */
height: 100%; /* 高度100% */
}
/* 猫猫按钮悬停效果 */
#maomao:hover {
transform: scale(1.1); /* 放大效果 */
}
</style>
</head>
<body>
<!-- 猫猫按钮 -->
<div id="maomao">
<svg width="228px" height="281px" viewBox="0 0 228 281" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>猫猫</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="mao" transform="translate(2.000000, 2.000000)">
<!-- 猫猫身体路径 -->
<path d="M208,50 C122.666667,10 74.6666667,-6 64,2 C48,14 76,50 76,66 C76,82 68,82 60,106 C52,130 56,130 52,146 C48,162 0,166 0,190 C0,214 122.87565,230.238243 152,242 C171.416233,249.841171 190.0829,260.507838 208,274 L208,50 Z" stroke="#000000" stroke-width="4" fill="#000000"></path>
<!-- 第一只眼睛(带动画) -->
<circle id="eye" stroke="#FFFFFF" stroke-width="4" fill="#FFFFFF" cx="138" cy="82" r="18" opacity="0">
<!-- 眨眼动画 -->
<animate attributeName="opacity" values="1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 0; 0; 0; 0; 0; 0;" dur="1s" repeatCount="indefinite" />
</circle>
<!-- 第二只眼睛(带动画) -->
<circle id="eye" stroke="#FFFFFF" stroke-width="4" fill="#FFFFFF" cx="110" cy="172" r="18" opacity="0">
<!-- 眨眼动画 -->
<animate attributeName="opacity" values="1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 0; 0; 0; 0; 0; 0;" dur="1s" repeatCount="indefinite" />
</circle>
<!-- 第一只眼睛的眼线 -->
<line x1="120" y1="82" x2="156" y2="82" id="-y-" stroke="#FFFFFF" stroke-width="4" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" transform="translate(138.000000, 82.000000) rotate(105.000000) translate(-138.000000, -82.000000) "></line>
<!-- 第二只眼睛的眼线 -->
<line x1="92" y1="172" x2="128" y2="172" id="-y-" stroke="#FFFFFF" stroke-width="4" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" transform="translate(110.000000, 172.000000) rotate(105.000000) translate(-110.000000, -172.000000) "></line>
</g>
</g>
</svg>
</div>
<!-- 音乐播放器 -->
<div id="music-player-container">
<!-- 专辑背景图 -->
<div id="player-bg-artwork"></div>
<!-- 背景遮罩层 -->
<div id="player-bg-layer"></div>
<!-- 播放器主容器 -->
<div id="player">
<!-- 轨道信息区域 -->
<div id="player-track">
<!-- 专辑名称 -->
<div id="album-name"></div>
<!-- 曲目名称 -->
<div id="track-name"></div>
<!-- 时间显示 -->
<div id="track-time">
<div id="current-time"></div>
<div id="track-length"></div>
</div>
<!-- 进度条区域 -->
<div id="seek-bar-container">
<div id="seek-time"></div>
<div id="s-hover"></div>
<div id="seek-bar"></div>
</div>
</div>
<!-- 播放器内容区域 -->
<div id="player-content">
<!-- 专辑封面 -->
<div id="album-art">
<!-- 专辑图片(多张) -->
<img src="https://singhimalaya.github.io/Codepen/assets/img/album-arts/1.jpg" class="active" id="_1" />
<img src="https://singhimalaya.github.io/Codepen/assets/img/album-arts/2.jpg" id="_2" />
<img src="https://singhimalaya.github.io/Codepen/assets/img/album-arts/3.jpg" id="_3" />
<img src="https://singhimalaya.github.io/Codepen/assets/img/album-arts/4.jpg" id="_4" />
<img src="https://singhimalaya.github.io/Codepen/assets/img/album-arts/5.jpg" id="_5" />
<!-- 缓冲提示 -->
<div id="buffer-box">缓冲中...</div>
</div>
<!-- 播放控制按钮 -->
<div id="player-controls">
<!-- 上一首按钮 -->
<div class="control">
<div class="button" id="play-previous">
<i class="fas fa-backward"></i>
</div>
</div>
<!-- 播放/暂停按钮 -->
<div class="control">
<div class="button" id="play-pause-button">
<i class="fas fa-play"></i>
</div>
</div>
<!-- 下一首按钮 -->
<div class="control">
<div class="button" id="play-next">
<i class="fas fa-forward"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 引入jQuery库 -->
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script>
// 音乐播放器功能实现
// 获取DOM元素
const playerTrack = $("#player-track");
const bgArtwork = $("#player-bg-artwork");
const albumName = $("#album-name");
const trackName = $("#track-name");
const albumArt = $("#album-art");
const sArea = $("#seek-bar-container");
const seekBar = $("#seek-bar");
const trackTime = $("#track-time");
const seekTime = $("#seek-time");
const sHover = $("#s-hover");
const playPauseButton = $("#play-pause-button");
const tProgress = $("#current-time");
const tTime = $("#track-length");
const playPreviousTrackButton = $("#play-previous");
const playNextTrackButton = $("#play-next");
const musicPlayerContainer = $("#music-player-container");
// 专辑数据
const albums = [
"Me & You",
"Dawn",
"Electro Boy",
"Home",
"Proxy (Original Mix)"
];
// 曲目数据
const trackNames = [
"Alex Skrindo - Me & You",
"Skylike - Dawn",
"Kaaze - Electro Boy",
"Jordan Schor - Home",
"Martin Garrix - Proxy"
];
// 专辑封面标识
const albumArtworks = ["_1", "_2", "_3", "_4", "_5"];
// 曲目URL
const trackUrl = [
"https://singhimalaya.github.io/Codepen/assets/music/1.mp3",
"https://singhimalaya.github.io/Codepen/assets/music/2.mp3",
"https://singhimalaya.github.io/Codepen/assets/music/3.mp3",
"https://singhimalaya.github.io/Codepen/assets/music/4.mp3",
"https://singhimalaya.github.io/Codepen/assets/music/5.mp3"
];
// 播放器状态变量
let bgArtworkUrl,
i = playPauseButton.find("i"),
seekT,
seekLoc,
seekBarPos,
cM,
ctMinutes,
ctSeconds,
curMinutes,
curSeconds,
durMinutes,
durSeconds,
playProgress,
bTime,
nTime = 0,
buffInterval = null,
tFlag = false,
currIndex = -1,
audio;
// 播放/暂停功能
function playPause() {
setTimeout(function () {
if (audio.paused) {
// 如果当前是暂停状态,则播放
playerTrack.addClass("active");
albumArt.addClass("active");
checkBuffering();
i.attr("class", "fas fa-pause");
audio.play();
musicPlayerContainer.addClass("show");
} else {
// 如果当前是播放状态,则暂停
playerTrack.removeClass("active");
albumArt.removeClass("active");
clearInterval(buffInterval);
albumArt.removeClass("buffering");
i.attr("class", "fas fa-play");
audio.pause();
musicPlayerContainer.removeClass("show");
}
}, 300);
}
// 显示悬停时间
function showHover(event) {
seekBarPos = sArea.offset();
seekT = event.clientX - seekBarPos.left;
seekLoc = audio.duration * (seekT / sArea.outerWidth());
sHover.width(seekT);
cM = seekLoc / 60;
ctMinutes = Math.floor(cM);
ctSeconds = Math.floor(seekLoc - ctMinutes * 60);
if (ctMinutes < 0 || ctSeconds < 0) return;
if (ctMinutes < 0 || ctSeconds < 0) return;
if (ctMinutes < 10) ctMinutes = "0" + ctMinutes;
if (ctSeconds < 10) ctSeconds = "0" + ctSeconds;
if (isNaN(ctMinutes) || isNaN(ctSeconds)) seekTime.text("--:--");
else seekTime.text(ctMinutes + ":" + ctSeconds);
seekTime.css({ left: seekT, "margin-left": "-21px" }).fadeIn(0);
}
// 隐藏悬停时间
function hideHover() {
sHover.width(0);
seekTime
.text("00:00")
.css({ left: "0px", "margin-left": "0px" })
.fadeOut(0);
}
// 从点击位置播放
function playFromClickedPos() {
audio.currentTime = seekLoc;
seekBar.width(seekT);
hideHover();
}
// 更新当前时间
function updateCurrTime() {
nTime = new Date();
nTime = nTime.getTime();
if (!tFlag) {
tFlag = true;
trackTime.addClass("active");
}
curMinutes = Math.floor(audio.currentTime / 60);
curSeconds = Math.floor(audio.currentTime - curMinutes * 60);
durMinutes = Math.floor(audio.duration / 60);
durSeconds = Math.floor(audio.duration - durMinutes * 60);
playProgress = (audio.currentTime / audio.duration) * 100;
if (curMinutes < 10) curMinutes = "0" + curMinutes;
if (curSeconds < 10) curSeconds = "0" + curSeconds;
if (durMinutes < 10) durMinutes = "0" + durMinutes;
if (durSeconds < 10) durSeconds = "0" + durSeconds;
if (isNaN(curMinutes) || isNaN(curSeconds)) tProgress.text("00:00");
else tProgress.text(curMinutes + ":" + curSeconds);
if (isNaN(durMinutes) || isNaN(durSeconds)) tTime.text("00:00");
else tTime.text(durMinutes + ":" + durSeconds);
if (
isNaN(curMinutes) ||
isNaN(curSeconds) ||
isNaN(durMinutes) ||
isNaN(durSeconds)
)
trackTime.removeClass("active");
else trackTime.addClass("active");
seekBar.width(playProgress + "%");
if (playProgress == 100) {
i.attr("class", "fa fa-play");
seekBar.width(0);
tProgress.text("00:00");
albumArt.removeClass("buffering").removeClass("active");
clearInterval(buffInterval);
}
}
// 检查缓冲状态
function checkBuffering() {
clearInterval(buffInterval);
buffInterval = setInterval(function () {
if (nTime == 0 || bTime - nTime > 1000) albumArt.addClass("buffering");
else albumArt.removeClass("buffering");
bTime = new Date();
bTime = bTime.getTime();
}, 100);
}
// 选择曲目
function selectTrack(flag) {
if (flag == 0 || flag == 1) ++currIndex;
else --currIndex;
if (currIndex > -1 && currIndex < albumArtworks.length) {
if (flag == 0) i.attr("class", "fa fa-play");
else {
albumArt.removeClass("buffering");
i.attr("class", "fa fa-pause");
}
seekBar.width(0);
trackTime.removeClass("active");
tProgress.text("00:00");
tTime.text("00:00");
currAlbum = albums[currIndex];
currTrackName = trackNames[currIndex];
currArtwork = albumArtworks[currIndex];
audio.src = trackUrl[currIndex];
nTime = 0;
bTime = new Date();
bTime = bTime.getTime();
if (flag != 0) {
audio.play();
playerTrack.addClass("active");
albumArt.addClass("active");
clearInterval(buffInterval);
checkBuffering();
musicPlayerContainer.addClass("show");
}
albumName.text(currAlbum);
trackName.text(currTrackName);
albumArt.find("img.active").removeClass("active");
$("#" + currArtwork).addClass("active");
bgArtworkUrl = $("#" + currArtwork).attr("src");
bgArtwork.css({ "background-image": "url(" + bgArtworkUrl + ")" });
} else {
if (flag == 0 || flag == 1) --currIndex;
else ++currIndex;
}
}
// 初始化播放器
function initPlayer() {
audio = new Audio();
selectTrack(0);
audio.loop = false;
// 绑定事件
playPauseButton.on("click", playPause);
sArea.mousemove(function (event) {
showHover(event);
});
sArea.mouseout(hideHover);
sArea.on("click", playFromClickedPos);
$(audio).on("timeupdate", updateCurrTime);
// 上一首按钮事件
playPreviousTrackButton.on("click", function () {
selectTrack(-1);
});
// 下一首按钮事件
playNextTrackButton.on("click", function () {
selectTrack(1);
});
}
// 初始化播放器
initPlayer();
// 猫猫按钮点击事件 - 控制音乐播放/暂停
document.getElementById('maomao').addEventListener('click', function() {
playPause();
});
</script>
</body>
</html>
index.html