【前端】CSS实战之音乐播放器

devtools/2025/1/24 5:54:30/

目录

  • 播放器背景
  • 旋转音乐封面
  • 按钮
  • 进度条
  • 音量调节
  • 音乐信息
  • 按钮的效果
  • JavaScript部分
    • 播放和暂停
    • 音乐切换
    • 音乐信息
    • 进度条
  • 音量调节
    • 避免拖拽时的杂音
    • 音量调节条
    • 静音和解除静音
  • 自动下一首
  • 实现一个小效果
  • 最终效果

播放器背景

<div class="play_box"></div>

设置居中,圆角,阴影等参数

css">.play_box {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);width: 800px;height: 80px;display: flex;border-radius: 5px;box-shadow: 0 0 15px #666565;
}

用专辑封面作为播放器的背景填充,通过设置模糊做出一种毛玻璃效果

css">.play_box {background-image: url(./1.png);background-size: 100% 100%;filter: blur(10px);
}

我尝试加上了这三行代码,把背景图片填充后再用filter属性进行模糊,产生了如下的效果
在这里插入图片描述
可以看到,整个播放器都变模糊了,并不是我们所希望的毛玻璃效果,为了不让边缘模糊,可以采用下面的办法。

css">.play_box {position: absolute;top: 43%;left: 18%;width: 800px;height: 80px;display: flex;border-radius: 5px;overflow: hidden;background-image: url(./1.png);background-size: cover;box-shadow: 0 0 15px #666565;
} 
.play_box::after{position: absolute;width: 101%;height: 100%;content: "";backdrop-filter: blur(20px);z-index: 1; 
}

在播放器上覆盖了一个伪元素,并将内容设为了空,然后给这个伪元素添加模糊效果
在这里插入图片描述

这样就好看多了。
而由于后续需要在其上面添加其他元素,所以说必须设置这个"毛玻璃"的定位和z-index
至于为什么width设成了101%是因为一个小细节,当设成100%的时候仔细观察发现右边存在瑕疵,所以拉长一点点毛玻璃的宽度就能避免这个情况了。
在这里插入图片描述

旋转音乐封面

<div class="album_cover"></div>
css">.album_cover{width: 80px;height: 80px;border-radius: 360px;background-image: url(./1.png);background-size: contain;z-index: 2;box-shadow: 0 0 10px #ffffffa2;animation: rotate 5s linear infinite;
}
@keyframes rotate {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}
}

在这里插入图片描述

按钮

加入一些字体图标作为按钮
在这里插入图片描述

<span class="iconfont icon-pause pause_icon"></span>
<span class="iconfont icon-icon-test play_icon"></span>
<span class="iconfont icon-icon-test1 pre_song"></span>
<span class="iconfont icon-icon-test1 next_song"></span>

对于这些图标的样式就全部写在html文件的head里面了

<link rel="stylesheet" href="./font/iconfont.css">
<style>css">.iconfont{position: absolute;top: 50%;font-size: 25px;color: #f99f50;transform: translateY(50%);z-index: 2;text-shadow: 0 0 10px #eecc7d;}.pause_icon{left: 25px;transform: translateY(-50%);font-size: 30px;cursor: pointer;}.play_icon{left: 25px;transform: translateY(-50%);font-size: 30px;display:none;cursor: pointer;}.pre_song{left: 100px;rotate: 180deg;cursor: pointer;}.next_song{left: 160px;transform: translateY(-50%);cursor: pointer;}.louder_icon,.turn_of_icon,.quieter_icon{right: 25px;transform: translateY(-50%);color:white;cursor: pointer;text-shadow: 0 0 5px white;display: none;
}
.louder_icon{display: block;
}
</style>

在这里插入图片描述
效果如上

进度条

<div class="progress-bar"><div class="progress-fill"><div class="progress-thumb"></div></div>
</div>
css">.progress-bar {position:absolute;z-index: 3;top: 50%;left: 60%;transform: translate(-50%, -50%);width: 400px;height: 12px;background-color: white;border-radius: 50px;box-shadow: 0 0 15px #edebeb;
}
.progress-fill {position:absolute;z-index: 3;width: 50%;height: 12px;background: linear-gradient(to right, #fdd162,#faab35,#f94f47);border-radius: 50px;
}
.progress-thumb{position: absolute;top: 50%;left: 100%;transform: translate(-50%, -50%);width: 20px;height: 20px;border-radius: 50%;background-color: white;cursor: pointer;box-shadow: 0 0 5px #939393;
}

用两个<div>来展示进度条的外壳和填充条,在填充条的最右侧再用一个<div>显示可拖拽的进度条圆点。
在这里插入图片描述
效果如上

音量调节

通过设置overflow:visible可以让超出盒子的部分显示出来,所以我们可以把音量图标专门放进一个<div>中然后在里面放上一个滑动条,以及显示百分比的<p>标签。这样子就不用担心缩放页面导致音量条的位置错位了。当鼠标悬停在音量图标上的时候,通过一些衔接动画来改变透明度并设置display属性,从而让音量调节条能够显示出来。通过:active可以实现当鼠标按下后即使离开了hover的区域,依然能让音量调节框显示出来。

<div class="volume-button"><span class="iconfont icon-louder louder_icon"></span><span class="iconfont icon-turn_of turn_of_icon"></span><span class="iconfont icon-quieter quieter_icon"></span><div class="volume-box"><div class="volume-bar"><div class="volume-fill"><div class="volume-thumb"></div></div></div><p class="volume-percent">50%</p></div>
</div>
css">.volume-button{position: absolute;top: 0;right: 0;width: 80px;height: 80px;cursor: pointer;z-index:4;
}.volume-box{position: absolute;bottom: 90%;right:20%;width:40px;height: 0px;z-index: 4;background-color: white;border-radius: 15px;box-shadow: 0 0 10px #818080;border: solid 1px #ffffff;display:none;opacity: 1;
}.volume-button:hover,.volume-button:active{transform: scale(1.5);transition: all 0.3s ease;
}.volume-button:hover .volume-box{height: 140px;display: block;animation: show_volume_bar.5s ease forwards;
}
.volume-button:active .volume-box{height: 140px;display: block;
}@keyframes show_volume_bar {0% {opacity: 0;transform: scale(0);transform-origin: bottom;}100% {opacity: 1;transform: scale(1);transform-origin: bottom;}
}
.volume-bar {position:absolute;top: 55%;left: 50%;transform: translate(-50%, -55%);width: 10px;height: 100px;background-color: white;border-radius: 50px;box-shadow: 0 0 15px #edebeb;
}
.volume-fill {position:absolute;bottom: 0;width: 12px;height: 50%;background: linear-gradient(to top, #fdd162,#faab35,#f94f47);border-radius: 50px;
}
.volume-thumb{position: absolute;top:0;left:50%;width: 13px;transform: translateX(-50%);height: 13px;border-radius: 50%;background-color: white;cursor: pointer;box-shadow: 0 0 5px #939393;
}.volume-thumb:hover{border: 0.5px solid #f1b04e;box-shadow: 0 0 5px #f1b04e;
}.volume-percent{position: absolute;bottom:2%;width: 20px;left: 50%; transform: translateX(-60%); font-size: 11px;color:#464545;
}

在这里插入图片描述

音乐信息

<div class="song-info"><p class="song-name">歌曲名</p><p class="song-time">1:30/3:00</p>
</div>
css">.song-info{position: absolute;top: 50%;left: 50%;transform: translate(-32%, -50%);width: 400px;height: 70px;z-index:2;color:#464545;
}
.song-name{position: absolute;top:0;left:50%;transform: translateX(-50%);text-shadow: 2px 2px 5px #ffffff, -2px -2px 5px #f1f1f1;
}
.song-time{position: absolute;bottom: 0px;left: 50%;transform: translateX(-50%);text-shadow: 2px 2px 5px #ffffff, -2px -2px 5px #f1f1f1;
}

在这里插入图片描述

按钮的效果

给两个滑动点加一些效果

css">.progress-thumb:hover{border: 1px solid #f1b04e;box-shadow: 0 0 5px #f1b04e;
}
.volume-thumb:hover{border: 0.5px solid #f1b04e;box-shadow: 0 0 5px #f1b04e;
}

在这里插入图片描述
通过:not()伪类选择器,把出了音量图标以外的图标鼠标悬停时设置成下面的样式

css">.iconfont:hover:not(.louder_icon,.turn_of_icon,.quieter_icon){color: #f38524;text-shadow: 0 0 10px #e28e44;
}

在这里插入图片描述
给音量滑动条显示出来时加一个动画

css">.volume-button:hover .volume-box{height: 140px;display: block;animation: show_volume_bar .5s ease forwards;
}
@keyframes show_volume_bar {0% {opacity: 0;transform: scale(0);transform-origin: bottom;}100% {opacity: 1;transform-origin: bottom;transform: scale(1);}
}

在这里插入图片描述

JavaScript部分

引入js代码

<script src="./script.js"></script>

播放和暂停

默认显示三角形播放图标,暂停图标则隐藏

css">.pause_icon{left: 25px;transform: translateY(-50%);font-size: 30px;cursor: pointer;display:none;
}.play_icon{left: 25px;transform: translateY(-50%);font-size: 30px;cursor: pointer;
}

默认图片是不旋转的

css">.album_cover{animation: rotate 5s linear infinite;animation-play-state: paused;
}

对于两个图标,分别设置相应的点击事件

const pauseIcon = document.querySelector('.pause_icon');
const playIcon = document.querySelector('.play_icon');
const albumCover = document.querySelector('.album_cover');
pauseIcon.addEventListener('click', function() {pauseIcon.style.display = 'none';playIcon.style.display = 'block';albumCover.style.animationPlayState = 'paused';
});
playIcon.addEventListener('click', function() {pauseIcon.style.display = 'block';playIcon.style.display = 'none';albumCover.style.animationPlayState = 'running';
});

在这里插入图片描述
加入一个用于播放音乐的标签<audio><body>开头

<audio id="audioPlayer"></audio>

优化一下代码结构

const pauseIcon = document.querySelector('.pause_icon');
const playIcon = document.querySelector('.play_icon');
const albumCover = document.querySelector('.album_cover');
const audioPlayer = document.getElementById('audioPlayer');pauseIcon.addEventListener('click', pauseSong);
playIcon.addEventListener('click', playSong);// 播放歌曲
function playSong() {audioPlayer.play();pauseIcon.style.display = 'block';playIcon.style.display = 'none';albumCover.style.animationPlayState = 'running';
}// 暂停歌曲
function pauseSong() {audioPlayer.pause();pauseIcon.style.display = 'none';playIcon.style.display = 'block';albumCover.style.animationPlayState = 'paused';
}

音乐切换

通过一个加载歌曲的函数,就可以实现歌曲切换了,用一个列表存放一下所有的歌曲名,然后用一个变量作为这个列表的索引传入加载歌曲的函数,在点击切歌按钮的时候就可以加载对应的歌曲,通过取模运算避免索引越界。

const songList = ["花月","honey"];// 歌曲列表
let currentIndex = 0;// 当前歌曲索引const playBox = document.querySelector('.play_box');
// 加载歌曲
function loadSong(index) {const songName = songList[index];audioPlayer.src="./music/"+songName+".m4a";playBox.style.backgroundImage = `url('./music/${songName}.png')`;albumCover.style.backgroundImage = `url('./music/${songName}.png')`;
}const nextIcon = document.querySelector('.next_song');
const preIcon = document.querySelector('.pre_song');
// 为 pre_song 和 next_song元素添加点击事件监听器
preIcon.addEventListener('click', preSong);
nextIcon.addEventListener('click', nextSong);// 下一首歌曲
function nextSong() {currentIndex = (currentIndex + 1) % songList.length;loadSong(currentIndex);playSong();
}
// 上一首歌曲
function preSong() {currentIndex = (currentIndex - 1 + songList.length) % songList.length;loadSong(currentIndex);playSong();
}

在这里插入图片描述

音乐信息

通过audioPlayer.currentTime, audioPlayer.duration这两个参数就能轻松获得音乐的当前时长和总时长了。可以加一个特判避免没有获取到数据的时候就显示出Na。

const songTime = document.querySelector('.song-time');
const songNameLabel = document.querySelector('.song-name');// 加载歌曲
function loadSong(index) {const songName = songList[index];audioPlayer.src="./music/"+songName+".m4a";playBox.style.backgroundImage = `url('./music/${songName}.png')`;albumCover.style.backgroundImage = `url('./music/${songName}.png')`;updateTimeDisplay(0, audioPlayer.duration);songNameLabel.textContent = songName;
}
// 格式化时间为分钟:秒的形式
function formatTime(time) {const minutes = Math.floor(time / 60);const seconds = Math.floor(time % 60);return `${minutes}:${seconds < 10? '0' + seconds : seconds}`;
}// 更新时间显示
function updateTimeDisplay(currentTime, totalTime) {if (isNaN(currentTime) || isNaN(totalTime)) {return;}songTime.textContent = `${formatTime(currentTime)}/${formatTime(totalTime)}`;
}// 监听音频播放进度更新
audioPlayer.ontimeupdate = function() {updateTimeDisplay(audioPlayer.currentTime, audioPlayer.duration);
};

在这里插入图片描述

进度条

由于之前在css中设置了小圆点的定位一定在进度条fill的最右侧,所以只需要更新fill的长度就行了,而这个长度需要实时更新,所以在音频监听的部分加上一条

audioPlayer.ontimeupdate = function() {updateTimeDisplay(audioPlayer.currentTime, audioPlayer.duration);updateProgressBar();
};

操控小圆点移动的部分主要通过获取鼠标点下和松开时的坐标以及相应的播放进度从而改变fill的长度,具体看下面的代码。

const progressBar = document.querySelector('.progress-bar');
const progressFill = document.querySelector('.progress-fill');
const progressThumb = document.querySelector('.progress-thumb');// 更新进度条
function updateProgressBar() {const progress = (audioPlayer.currentTime / audioPlayer.duration) * 100;progressFill.style.width = `${progress}%`;
}// 处理进度条控点的拖动
let isDragging = false;
let startX;
let startProgress;progressThumb.addEventListener('mousedown', function(e) {isDragging = true;startX = e.clientX;// 鼠标按下时的坐标startProgress = audioPlayer.currentTime;// 鼠标按下时的进度document.body.style.userSelect = 'none';// 禁止选中文本
});document.addEventListener('mousemove', function(e) {if (isDragging) {const dx = e.clientX - startX;// 鼠标移动的距离const progressBarWidth = progressBar.offsetWidth - progressThumb.offsetWidth;// 进度条宽度const progress = (startProgress + (dx / progressBarWidth) * audioPlayer.duration*0.95);// 新的进度audioPlayer.currentTime = progress;// 更新播放进度}
});document.addEventListener('mouseup', function() {isDragging = false;document.body.style.userSelect = 'auto';// 允许选中文本
});

在这里插入图片描述

音量调节

避免拖拽时的杂音

通过audioPlayer.volume即可获取音乐当前的音量
为了避免拖拽进度条的时候产生声音,可以做如下调整:
新建一个变量let volumeBeforeMute;
当鼠标按到进度条控点上时记录下当前音量

volumeBeforeMute = audioPlayer.volume;

拖拽过程中音量设为0

audioPlayer.volume = 0;

鼠标松开后重新社会一开始的音量

audioPlayer.volume = volumeBeforeMute;

音量调节条

和进度条类似的做法

const volumeButton = document.querySelector('.volume-button');
const volumeBar = document.querySelector('.volume-bar');
const volumeFill = document.querySelector('.volume-fill');
const volumeThumb = document.querySelector('.volume-thumb');
const volumePercent = document.querySelector('.volume-percent');
// 更新音量条
function updateVolumeBar() {curVolume = audioPlayer.volume;volumeFill.style.height = (curVolume*volumeBar.offsetHeight+volumeThumb.offsetHeight)+'px';volumePercent.textContent = `${Math.round(curVolume * 100)}%`;
}// 处理音量条控点的拖动
let isDraggingVolume = false;
let startY;
let startVolume;volumeThumb.addEventListener('mousedown', function(e) {isDraggingVolume = true;startY = e.clientY;startVolume = audioPlayer.volume;document.body.style.userSelect = 'none';
});document.addEventListener('mousemove', function(e) {if (isDraggingVolume) {const dy = e.clientY - startY;// 鼠标移动的距离const volumeBarHeight = volumeBar.offsetHeight;let volume = startVolume - (dy / volumeBarHeight*0.7);volume = Math.max(0, Math.min(1, volume)); // 确保音量在 0 到 1 之间audioPlayer.volume = volume;curVolume = volume;if (volume == 0){mute()}else if(volume < 0.5){muteIcon.style.display = 'none';louderIcon.style.display = 'none';quieterIcon.style.display = 'block';}else{muteIcon.style.display = 'none';louderIcon.style.display = 'block';quieterIcon.style.display = 'none';}}updateVolumeBar();
});document.addEventListener('mouseup', function() {isDraggingVolume = false;document.body.style.userSelect = 'auto';
});

静音和解除静音

const louderIcon = document.querySelector('.louder_icon');
const muteIcon = document.querySelector('.turn_of_icon');
// 为 louder 和 turn_of_icon 元素添加点击事件监听器louderIcon.addEventListener('click', mute);
muteIcon.addEventListener('click', louder);
let volumeAfterMute;
//静音
function mute() {muteIcon.style.display = 'block';louderIcon.style.display = 'none';quieterIcon.style.display = 'none';volumeBeforeMute = audioPlayer.volume;audioPlayer.volume = 0;updateVolumeBar();
}
//解除静音
function louder() {if (volumeBeforeMute == 0){mute()}else if(volumeBeforeMute < 0.5){muteIcon.style.display = 'none';louderIcon.style.display = 'none';quieterIcon.style.display = 'block';}else{muteIcon.style.display = 'none';louderIcon.style.display = 'block';quieterIcon.style.display = 'none';}audioPlayer.volume =volumeBeforeMute;updateVolumeBar();
}

自动下一首

在监听函数中加上一个判断语句就行了

// 监听音频播放进度更新
audioPlayer.ontimeupdate = function() {updateTimeDisplay(audioPlayer.currentTime, audioPlayer.duration);updateProgressBar();if (audioPlayer.ended) {nextSong();}
};

在这里插入图片描述

实现一个小效果

我想实现将鼠标放到播放或暂停键上时,音乐图片角度归零,放大, 并停止旋转。
搞了很久一直不起作用,最后只能用!important来提升属性的优先级从而在CSS中实现。

css">.play_icon:hover ~.album_cover,.pause_icon:hover ~.album_cover {width: 100px;height: 100px;margin-left: -10px;margin-top: -10px;border-radius: 5%;rotate: 0deg;animation-play-state: paused!important;transform: rotate(0deg)!important;
}

在这里插入图片描述

最终效果

至此整个项目就完成啦!
在这里插入图片描述


http://www.ppmy.cn/devtools/153058.html

相关文章

从0到1:.NET Core微服务的Docker容器奇幻冒险

扬帆起航&#xff1a;开启技术新征程 在当今软件开发的广袤海洋中&#xff0c;技术浪潮此起彼伏&#xff0c;不断推动着行业的变革与发展。.NET Core 微服务与 Docker 容器化技术的结合&#xff0c;宛如一艘强大的战舰&#xff0c;引领着开发者驶向高效、灵活、可扩展的软件开…

2025年美国大学生数学建模竞赛赛前准备计划

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击文末的卡片&#xff0c;那是获取资料的入口&#xff01; 知识技能&#xff0c;火速巩固 01 数学模型&#xff1a; 回顾核心理论&#xff1a;快速回顾数学建模的基础理论&#xff0c;如线性代数、概率统计、微分方…

Linux的软件安装

一、Mysql的在线与离线安装 进入Mysql的官网按照下图步骤可以看到安装的方式&#xff1a;MySQL 1.1、在线安装 yum list | grep "^mysql"在上图中选择自己想要的版本进行下载即可&#xff08;这里推荐mysql-server.x86_64 &#xff09;&#xff0c;安装好后需要看看…

Alibaba Spring Cloud 四 Seata 的核心组件:TC

Seata 的 Transaction Coordinator (TC) 是分布式事务架构中的核心组件之一&#xff0c;它负责管理全局事务的生命周期&#xff0c;包括事务的创建、状态维护以及协调各分支事务的提交和回滚。以下是有关 TC 的详细解析及其配置和使用方法&#xff1a; 1. TC 的核心功能 全局事…

高性能队列 Disruptor 在 IM 系统中的实战

高性能队列 Disruptor 在 IM 系统中的实战 前三期我们介绍了Disruptor的典型使用场景和相关高性能原理&#xff0c;本期我介绍一下Disruptor在IM系统用的应用实战&#xff0c;IM系统即社交聊天系统&#xff0c;对实时性的要求非常高&#xff0c;非常符合Disruptor的使用场景。 …

Linux 如何使用dd命令来复制和转换数据?

简介 Linux 中的 dd 命令是一个功能强大的数据复制和转换实用程序。它以较低级别运行&#xff0c;通常用于创建可启动的 USB 驱动器、克隆磁盘和生成随机数据等任务。 dd 全称可以为&#xff1a;data duplicator、disk destroyer 和 Data Definition 功能和能力 磁盘映像&am…

leetcode 面试经典 150 题:简化路径

链接简化路径题序号71题型字符串解法栈难度中等熟练度✅✅✅ 题目 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 ‘/’ 开头&#xff09;&#xff0c;请你将其转化为 更加简洁的规范路径。 在 Unix 风格的文件系统中规则如下…

【docker-1】快速入门docker

docker搭建 1、下载二进制文件 wget https://mirror.nju.edu.cn/docker-ce/linux/static/stable/aarch64/docker-20.10.7.tgz解压docker目录 tar -xzvf docker-20.10.7.tgz .安装文件到/usr/local/bin mv ./docker/* /usr/local/bin2、配置docker.service [Unit] Descript…