亚伦影音工作室 发表于 2025-9-6 18:42

播放器进度百分比%

本帖最后由 亚伦影音工作室 于 2025-9-7 19:43 编辑 <br /><br /><style>
#prog {position: relative;z-index: 8;filter:invert(90%) hue-rotate(350deg);
      width: 600px;
      height: 8px;
      cursor: pointer;
         bottom: -200px;
left:6%;}
#tmsg {position: relative;z-index: 8;
      font: normal 12px sans-serif;
      color: #000;
         bottom: -115px;
      left:500px;}
#ptxt{width:100%;height:6px;font: normal 12px sans-serif;color: #000;line-height:0px;bottom: -130px;left:120px;position: relative;}


#bfq{   
      position:relative;
      width: 450px;
      height:350px;overflow: hidden;
      background:#0000;
transform:scale(.4);bottom: 80px;
   left: 120px;z-index: 20;}
#cp{   
      position:absolute;
      width: 240px;border-radius: 50%;
      height:240px;animation: rotating 6s infinite linear;
      top:18%;background:repeating-radial-gradient(black, black 5px, #1C1C1C 6px, #1C1C1C 7px);
   cursor: pointer;
   left: 12%;z-index: 1;box-shadow:0px 0px 0px 1px #fff,0px 0px 0px 0px #880000;}
@keyframes rotating { to { transform: rotate(360deg); } }
.overlay {
content: '';
left: 50%;
top: 50%; transform: translate(-50%, -50%);
position: absolute;
width: 238px;
height: 238px;
background: linear-gradient(45deg, transparent, 40%, rgba(255,255,255,0.25), 60%, transparent);
border-radius: 50%;
}


.inner {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 100px;
box-shadow:0px 0px 0px 1px #eee,0px 0px 0px 2px #444;
background:#880000 url('') no-repeat center / cover;

border-radius: 50%;
}
.inner::before {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 15px;
height: 15px;
background: #ccc;
border-radius: 50%;
}
#cz {position: absolute;
      top:6%; left:45%;z-index: 2;
      width: 150px;background: url('https://pic1.imgdb.cn/item/6688e0dad9c307b7e9a7a3e1.png')no-repeat center/85%;
      height: 300px;
      cursor: pointer;
}

.pink { transform:rotate(5deg);transform-origin: 100% 0%;}
.purple {transform-origin: 80% 0%;margin: -4px -18px;transform:rotate(-9deg);}
#gc{margin: 120px 0%;position: relative;z-index: 50;width: 750px;}
.lyrics{margin: 0;
            top: -160px;
            left: 47%;
    transform: translate(-50%, -50%);
            height: 100px; /* 调整高度,只容纳当前歌词 */
                 text-align: center;
            position: absolute;z-index: 50;
      }
            .lyric-line{
            width: 100%;
            position: relative;
            height: 60px;
            overflow: visible;
   font: 900 40px '宋体', sans-serif;
            line-height: 60px;
         text-align: left;
            white-space: nowrap; /* 禁止换行 */
            
            filter: drop-shadow(#fff 1px 0 0) drop-shadow(#fff 0 1px 0) drop-shadow(#fff -1px 0 0) drop-shadow(#fff 0 -1px 0);
      }

      .lyric-mask {
            position: absolute;
            top: 0;
            left: 0;
            width: 0;
            overflow: hidden;
      color: #ff0000;
            height: 100%;
            white-space: nowrap;
      }

      .lyric-original {
             color: #000;
            white-space: nowrap;
            
      }   

</style>
<audio id="aud" src="https://www.oldkids.cn/upload/2025/09/04/blog_260806313_20250904231600405.mp3" autoplay loop></audio>

<progress id="prog"></progress>
<div id="ptxt">0%</div>
<div id="tmsg">00:00/00:00</div>
<div id="bfq">
<div id="cz"class="pink"></div>
<div id="cp"><div class="inner"></div> <div class="overlay"></div></div>
</div>
<div id="gc" >
<div class="lyrics" >
            <div class="lyric-line">
                  <div class="lyric-mask"></div>
                  <div class="lyric-original"></div>
                </div>
            </div>
</div>
<script >
aud.ontimeupdate = () => prog.value=aud.currentTime/aud.duration;
prog.onclick = (e) => aud.currentTime=aud.duration*e.offsetX/prog.offsetWidth;


aud.addEventListener('timeupdate', () => {
            tmsg.innerText = toMin(aud.currentTime) + '/' + toMin(aud.duration);
      });
      
      function toMin(val) {
            if (!val) return '00:00';
            val = Math.floor(val);
            let min = parseInt(val / 60);
            let sec = parseFloat(val % 60);
            if (min < 10) min = '0' + min;
            if (sec < 10) sec = '0' + sec;
            return min + ':' + sec;
      }
      aud.addEventListener("timeupdate",function(){
                                var percent= aud.currentTime / aud.duration
                                var sp = 600 / 100 ;
                                var swidth =(percent * 100 * sp) + "px";
                                console.log(percent*100,swidth)
                                //保留2位小数
                                document.getElementById("ptxt").innerText = ((percent*100).toFixed(2))+"%"
                               
                        })
cp.onclick = cz.onclick = () => aud.paused ? (aud.play(), cz.classList.remove('purple'),intro.style.animationPlayState = 'running') : (aud.pause(),cz.classList.add('purple'),intro.style.animationPlayState = 'paused');
cp.style.animationPlayState = aud.paused ? 'paused' : 'running';
aud.addEventListener('playing', () => cp.style.animationPlayState = 'running');
      aud.addEventListener('pause', () => cp.style.animationPlayState = 'paused');
      const intro= document.querySelector('.intro');
</script>
<script>
      // 歌词解析ksc歌词或lrc歌词
      const lrc = `karaoke.add('00:03.042', '00:06.295', '《黄梅悠悠》', '405,408,440,456,464,1080');
karaoke.add('00:06.503', '00:12.087', '词/陈频 曲/雷远生', '472,528,504,449,560,448,480,495,568,1080');
karaoke.add('00:12.680', '00:16.607', '演唱/斯兰', '944,688,640,575,1080');
karaoke.add('00:22.703', '00:29.392', '对花春风里观灯黄昏后', '368,504,456,496,1848,433,472,448,584,1080');
karaoke.add('00:30.240', '00:34.047', '黄梅悠悠情悠悠', '368,488,520,488,456,471,1016');
karaoke.add('00:34.047', '00:40.759', '魂都被牵走哟魂都被牵走', '408,480,472,512,1424,440,448,480,440,528,1080');
karaoke.add('00:49.400', '00:56.031', '春风啊绿了田畴', '272,983,569,767,1064,1896,1080');
karaoke.add('00:56.855', '01:03.696', '山花红了村头', '512,1448,1176,688,1937,1080');
karaoke.add('01:04.503', '01:10.295', '跟着唱跟着扭', '369,504,3087,296,456,1080');
karaoke.add('01:12.718', '01:18.903', '歌声伴着脚步走', '369,976,480,1360,1032,888,1080');
karaoke.add('01:20.200', '01:32.183', '一曲黄梅寄乡愁寄乡愁', '392,1055,1849,1967,480,1416,1792,480,1472,1080');
karaoke.add('01:34.991', '01:41.344', '字幕制作/翁奕童苑', '672,625,632,560,624,624,536,504,496,1080');
karaoke.add('01:52.135', '01:58.848', '仙女下凡来槐荫结鸾俦', '440,480,425,512,1911,449,472,424,520,1080');
karaoke.add('01:59.680', '02:03.615', '黄梅悠悠情悠悠', '423,536,504,480,456,512,1024');
karaoke.add('02:03.615', '02:10.311', '谁能听得够哟谁能听得够', '408,472,416,512,1384,496,440,504,432,552,1080');
karaoke.add('02:19.015', '02:25.487', '蜂蜜啊甜了歌喉', '272,872,448,696,1272,1832,1080');
karaoke.add('02:26.391', '02:33.168', '美酒醉了心头', '384,1457,1272,624,1960,1080');
karaoke.add('02:33.960', '02:39.839', '传四海传五洲', '408,584,3047,336,424,1080');
karaoke.add('02:42.167', '02:48.424', '揣着乡音四方走', '448,944,544,1392,969,880,1080');
karaoke.add('02:49.657', '03:01.689', '一曲黄梅寄乡愁寄乡愁', '432,1096,1896,1808,512,1463,1856,576,1313,1080');
karaoke.add('03:02.641', '03:09.265', '蜂蜜啊甜了歌喉', '400,864,528,552,1272,1928,1080');
karaoke.add('03:10.104', '03:16.936', '美酒醉了心头', '457,1536,1056,887,1816,1080');
karaoke.add('03:17.713', '03:23.689', '传四海传五洲', '472,568,2976,320,560,1080');
karaoke.add('03:25.929', '03:32.201', '揣着乡音四方走', '464,984,456,1472,920,896,1080');
karaoke.add('03:33.506', '03:45.625', '一曲黄梅寄乡愁寄乡愁', '416,1024,1927,1832,504,1520,1792,496,1528,1080');
karaoke.add('03:46.970', '03:51.426', '一曲黄梅寄乡愁', '528,896,736,296,400,520,1080');
karaoke.add('03:54.818', '03:57.579', '寄乡愁', '512,1169,1080');
karaoke.add('04:09.842', '04:13.651', '谢谢欣赏', '768,1016,945,1080');
`;
      const lyrics = parseLyrics(lrc);
      const lyricMask = document.querySelector('.lyric-mask');
      const lyricOriginal = document.querySelector('.lyric-original');
      
      let currentIndex = -1;
      let currentLyric = null;
      
      // 解析歌词(支持两种格式)
      function parseLyrics(lrcText) {
            const lyrics = [];
            if (lrcText.includes('karaoke.add')) {
                const lineRegex = /karaoke\.add\('([^']+)', '([^']+)', '([^']+)', '([^']+)'\);/g;
                let match;
                while ((match = lineRegex.exec(lrcText)) !== null) {
                  const startTime = timeToMs(match);
                  const endTime = timeToMs(match);
                  const text = match.replace(/\[|\]/g, '').trim();
                  const durations = match.split(',').map(Number);
                  if (text) {
                        lyrics.push({startTime, endTime, text, durations});
                  }
                }
            }
            else if (lrcText.includes('[')) {
                const lines = lrcText.split('\n').filter(line => line.trim());
                lines.forEach((line, index) => {
                  const timeMatch = line.match(/\[(\d+:\d+\.\d+)\]/);
                  if (timeMatch) {
                        const timeStr = timeMatch;
                        const text = line.replace(/\[.*?\]/, '').trim();
                        if (text) {
                            const startTime = timeToMs(timeStr);
                            const nextLine = lines;
                            const nextTimeMatch = nextLine ? nextLine.match(/\[(\d+:\d+\.\d+)\]/) : null;
                            const endTime = nextTimeMatch ? timeToMs(nextTimeMatch) : startTime + 5000;
                            lyrics.push({
                              startTime,
                              endTime,
                              text,
                              durations: calculateCharDurations(text, startTime, endTime)
                            });
                        }
                  }
                });
            }
            return lyrics;
      }
      function calculateCharDurations(text, startTime, endTime) {
            const totalDuration = endTime - startTime;
            const charCount = text.length;
            const baseDur = Math.floor(totalDuration / charCount);
            const durations = new Array(charCount).fill(baseDur);
            const remainder = totalDuration % charCount;
            for (let i = 0; i < remainder; i++) {
                durations++;
            }
            return durations;
      }
      function timeToMs(timeStr) {
            const parts = timeStr.split(':');
            const minutes = parseInt(parts, 10);
            const secondsAndMs = parts.split('.');
            const seconds = parseInt(secondsAndMs, 10);
            const ms = parseInt(secondsAndMs || 0, 10);
            return minutes * 60 * 1000 + seconds * 1000 + ms;
      }
      function getCurrentLyricIndex(lyrics, currentTimeMs) {
            for (let i = 0; i < lyrics.length; i++) {
                if (currentTimeMs >= lyrics.startTime && currentTimeMs <= lyrics.endTime) {
                  return i;
                }
            }
            return -1;
      }
      function updateLyricDisplay(index) {
            if (index < 0 || index >= lyrics.length) return;
            currentIndex = index;
            currentLyric = lyrics;
            lyricOriginal.textContent = currentLyric.text;
            lyricMask.textContent = currentLyric.text;
            lyricMask.style.width = '0%';
      }
      function updateLyricMask(currentTimeMs) {
            if (!currentLyric) return;
            const lyricStartTime = currentLyric.startTime;
            const elapsed = currentTimeMs - lyricStartTime;
            const totalDuration = currentLyric.durations.reduce((sum, d) => sum + d, 0);
            let charIndex = 0;
            let accumulatedTime = 0;
            
            for (let i = 0; i < currentLyric.durations.length; i++) {
                accumulatedTime += currentLyric.durations;
                if (elapsed <= accumulatedTime) {
                  charIndex = i + 1;
                  break;
                }
            }
            
            if (elapsed >= totalDuration) {
                charIndex = currentLyric.text.length;
            }
            
            charIndex = Math.min(charIndex, currentLyric.text.length);
            
            const tempSpan = document.createElement('span');
            tempSpan.style.visibility = 'hidden';
            tempSpan.style.position = 'absolute';
            tempSpan.style.fontSize = '50px';
            tempSpan.style.fontWeight = '800';
            document.body.appendChild(tempSpan);
            
            const visibleText = currentLyric.text.substring(0, charIndex);
            tempSpan.textContent = visibleText;
            const width = tempSpan.offsetWidth;
            document.body.removeChild(tempSpan);
            
            lyricMask.style.width = `${width}px`;
      }
      
      // 监听更新歌词
      aud.addEventListener('timeupdate', () => {
            const currentTimeMs = aud.currentTime * 1000;
            const index = getCurrentLyricIndex(lyrics, currentTimeMs);
            
            if (index !== currentIndex) {
                updateLyricDisplay(index);
            }
            
            updateLyricMask(currentTimeMs);
      });
      updateLyricDisplay(0);
</script>

红影 发表于 2025-9-6 20:41

这个在前面有播放进度的百分比展示,很奇妙{:4_199:}

红影 发表于 2025-9-6 20:41

还有漂亮的歌词同步。
欣赏亚伦老师好帖{:4_199:}

杨帆 发表于 2025-9-6 21:00

简洁顺畅,美观适用,祝贺亚伦老师再创新作{:4_191:}
页: [1]
查看完整版本: 播放器进度百分比%