|
|

楼主 |
发表于 2025-2-5 10:35
|
显示全部楼层
本帖最后由 马黑黑 于 2025-2-5 12:17 编辑
2月5日更新内容:改进音乐列表滚动条样式(支持chrome和Firefox)
- <style>
- /* 父元素 */
- #papa {
- margin: 30px auto 0;
- padding: 10px;
- width: 800px;
- height: 400px;
- font-size: 16px;
- background: linear-gradient(to bottom right, #000, #ffc);
- box-shadow: 3px 6px 20px #000;
- border-radius: 6px;
- display: grid;
- place-items: center;
- position: relative;
- }
- /* 选择本地音乐父元素 */
- #openFile {
- position: absolute;
- left: 15px;
- top: 15px;
- width: calc(100% - 30px);
- }
- /* 选择音乐控件 */
- #mfile { display: none; }
- /* 选择音乐按钮 */
- #selectSong {
- margin-right: 8px;
- border: 2px solid #ccc;
- border-radius: 6px;
- outline: none;
- background: none;
- color: snow;
- cursor: pointer;
- }
- /* 背景图片地址栏 */
- #bgurl {
- position: absolute;
- right: 0;
- display: none;
- }
- #openFile:hover #bgurl {
- display: inline;
- }
- /* 选择音乐按钮指针经过 */
- #selectSong:hover { background: darkred; }
- /* 当前播放曲目 */
- #curSong { color: #eee; }
- /* 播放器 */
- #mplayer {
- --bg1: teal;
- --bg2: snow;
- --ppLen: 4px;
- --prog: white;
- --track: silver;
- --prg: 0%;
- --ppCap: white;
- position: absolute;
- right: 180px;
- width: 100px;
- height: 100px;
- border-radius: 50%;
- background: linear-gradient(
- to right,
- var(--prog) var(--prg),
- var(--track) var(--prg),
- var(--track) 0
- ) no-repeat 0 50% / 100% 2px;
- cursor: pointer;
- display: grid;
- place-items: center;
- }
- /* 播放器指针经过 */
- #mplayer:hover {
- filter: hue-rotate(90deg) drop-shadow(0 0 26px black);
- }
- /* 播放器伪元素 :音频时间信息 */
- #mplayer::before, #mplayer::after {
- position: absolute;
- color: snow;
- }
- /* 播放器伪元素一 :当前播放时间 */
- #mplayer::before {
- content: attr(data-cu);
- top: 20%;
- }
- /* 播放器伪元素二 :音频时长 */
- #mplayer::after {
- content: attr(data-du);
- top: 56%;
- }
- /* 频谱条 */
- .pp {
- position: absolute;
- left: calc(50% - 2px);
- bottom: 50%;
- width: var(--ppLen);
- height: 20px;
- background: linear-gradient(to top, var(--bg1), var(--bg2));
- transform-origin: 50% 100%;
- transform: rotate(var(--deg)) translate(-50px, 0);
- display: grid;
- place-items: center;
- }
- /* 频谱条伪元素 :频谱帽 */
- .pp::after {
- position: absolute;
- content: '';
- width: calc(var(--ppLen) + 4px);
- height: calc(var(--ppLen) + 4px);
- top: 0px;
- background: var(--bg2);
- border-radius: 50%;
- }
- /* 音乐列表 */
- #mlist {
- position: absolute;
- left: 20px;
- top: 60px;
- min-width: 40%;
- max-width: 50%;
- min-height: 20%;
- max-height: calc(100% - 80px);
- color: silver;
- font-size: 14px;
- line-height: 20px;
- opacity: .4;
- /* 滚动条 */
- overflow: hidden;
- scrollbar-width: thin;
- scrollbar-color: teal rgba(255,255,255,.2);
- }
- /* 音乐列表鼠标经过 */
- #mlist:hover {
- overflow: auto;
- opacity: 1;
- }
- /* 音乐列表项目一 :待播放曲目 */
- .list1 { cursor: pointer; }
- /* 音乐列表项目二 :正在播放曲目 */
- .list2 {
- color: cyan;
- cursor:default;
- }
- /* 音乐列表项目一指针经过 */
- .list1:hover { color: white; }
- </style>
- <div id="papa">
- <div id="openFile">
- <input id="selectSong" type="button" value="选择音乐" />
- <input type="file" id="mfile" accept=".mp3,.ogg,.wav,.acc,.webm,.flac" multiple />
- <span id="curSong"></span>
- <input id="bgurl" type="text" placeholder="背景 : 网络图片地址" value="" />
- </div>
- <div id="mplayer" class="mplayer"></div>
- <div id="mlist"></div>
- <audio id="aud"></audio>
- </div>
- <p style="text-align:right"><br>更新 :2025年2月5日 <br></p>
- <script>
- //选择的文件, 播放列表, 波形数据, 频谱
- let files=[], playAr = [], output = [], pps = [];
- //打开文件次数, 频谱条总数
- let openIdx = 0, total = 30;
- //获取波形数据
- const getDatas = () => {
- if(openIdx > 0) return;
- openIdx ++;
- Ac = new AudioContext;
- source = Ac.createMediaElementSource(aud);
- analyser = Ac.createAnalyser();
- source.connect(analyser);
- analyser.connect(Ac.destination);
- output = new Uint8Array(total);
- };
- //生成频谱条
- Array(total).fill(0).forEach((_, k) => {
- let pp = document.createElement('span');
- pp.className = 'pp';
- pp.style.cssText += `--deg: ${360 / total * k}deg`;
- mplayer.appendChild(pp);
- pps.push(pp);
- });
- //波形数据刷新
- (function update() {
- if(aud.src) analyser.getByteFrequencyData(output);
- for(let j = 0; j < total ; j++) {
- pps[j].style.height = output[j] / 2 + 'px';
- }
- window.requestAnimationFrame(update);
- })();
- //播放 :idx为空时随机播放
- const mplay = (idx = null) => {
- if(files.length === 0) return;
- let isScrolling = false;
- if(idx === null) {
- if(playAr.length === 0) playAr = ranNum(files.length);
- let tmpIdx = Math.floor(Math.random() * playAr.length);
- idx = playAr[tmpIdx];
- playAr.splice(tmpIdx, 1);
- isScrolling = true;
- }
- aud.src = URL.createObjectURL(files[idx]);
- let name = files[idx].name;
- curSong.innerText = name.substring(0, name.lastIndexOf('.')) + `(${files.length}/${idx+1})`;
- aud.play();
- mlist.innerHTML = showList(files, idx);
- if(isScrolling) mlist.scrollTop = 20 * idx; //scrolling(20, idx);
- };
- //显示音乐列表
- const showList = (ar, idx) => {
- let res = '';
- for(let j = 0; j < ar.length; j ++) {
- let item = (j + 1) + '. ';
- item += j === idx ?
- `<span class="list2">${ar[j].name}</span>` :
- `<span class="list1" onclick="mplay(${j})">${ar[j].name}</span>`;
- res += item + '<br>';
- }
- return res;
- };
- //生成不重复随机数组
- const ranNum = (total) => {
- let ar = Array(total).fill().map((_,key) => key);
- ar.sort(() => 0.5 - Math.random());
- return ar;
- };
- //秒转分
- const s2m = (seconds) => {
- if (!seconds) return '00:00';
- let min = parseInt(seconds / 60), sec = parseFloat(Math.floor(seconds) % 60);
- if(min < 10) min = '0' + min;
- if(sec < 10) sec = '0' + sec;
- return min + ':' + sec;
- };
- //判断进度条区域
- const innerH = (e, h) => e.offsetY > h / 2 - 5 && e.offsetY < h / 2 + 5;
- //audio timeupdate监听事件
- aud.addEventListener('timeupdate', () => {
- mplayer.style.setProperty('--prg', aud.currentTime / aud.duration * 100 + '%');
- mplayer.dataset.cu = s2m(aud.currentTime);
- mplayer.dataset.du = s2m(aud.duration);
- });
- //单曲播放结束
- aud.addEventListener('ended',() => mplay());
- //选择歌曲
- selectSong.onclick = () => mfile.click();
- //文件选择器改变
- mfile.onchange = () => {
- let filelist = mfile.files;
- if(filelist.length === 0) return;
- files.length = 0;
- for(let j = 0; j < filelist.length; j ++) {
- files.push(filelist[j]);
- }
- playAr = ranNum(files.length);
- mplay();
- getDatas();
- }
- //播放器点击
- mplayer.onclick = (e) => {
- if(files.length < 1) return;
- if(innerH(e,mplayer.clientHeight)) {
- aud.currentTime = aud.duration * e.offsetX / mplayer.offsetWidth;
- }else{
- aud.paused ? aud.play() : aud.pause();
- }
- };
- //播放器鼠标移过
- mplayer.onmousemove = (e) => {
- mplayer.title = innerH(e,mplayer.clientHeight) ?
- s2m(aud.duration * e.offsetX / mplayer.offsetWidth) :
- (aud.paused ? '点击播放' : '点击暂停');
- };
- //背景图地址栏输入事件
- bgurl.oninput = () => {
- let img = new Image(), src = bgurl.value.trim();
- img.src = src;
- img.onload = () => {
- papa.style.cssText += `background: url(${src}) no-repeat center/cover;`;
- localStorage.setItem('player_url', src)
- };
- };
- //背景图地址栏鼠标经过事件
- bgurl.onmouseover = () => {
- bgurl.focus();
- bgurl.setSelectionRange(0,bgurl.value.length);
- };
- //加载背景图
- const player_url = bgurl.value = localStorage.getItem('player_url');
- if(player_url) papa.style.cssText += `background: url(${player_url}) no-repeat center/cover;`;
- </script>
复制代码
|
评分
-
| 参与人数 1 | 威望 +30 |
金钱 +60 |
经验 +30 |
收起
理由
|
花飞飞
| + 30 |
+ 60 |
+ 30 |
很给力! |
查看全部评分
|