亚伦影音工作室 发表于 2025-8-21 13:17

ksc歌词播放器界面设计

本帖最后由 亚伦影音工作室 于 2025-8-21 13:25 编辑 <br /><br /><style>
        #bfq{ transform:scale(0.6);
   position: relative;
      width: 450px;
      height:350px;overflow: hidden;
      top:0%;background:#0000;
   left: -10%;z-index: 20;--state:paused;}
#tmsg{position: absolute;width: 100px; height: 60px;font:500 12px sans-serif;color: #eee;top:20px;left: 200px;}
#tu{   
      position:absolute;
      width: 300px;box-shadow:0px 0px 0px 1px #fff,0px 0px 0px 0px #880000;
      height:300px;border-radius: 10px;
      top:10%;background:#000 url('https://singerimg.kugou.com/uploadpic/softhead/120/20241213/20241213201009201777.jpg') no-repeat center / cover;
   left: 2%;z-index: 2;}
#cp{   
      position:absolute;
      width: 260px;border-radius: 50%;
      height:260px;animation: rotating 6s infinite linear var(--state);
      top:15%;background:repeating-radial-gradient(black, black 5px, #1C1C1C 6px, #1C1C1C 7px);
   transition: all 1s;
   left: 41%;z-index: 1;box-shadow:0px 0px 0px 1px #222,0px 0px 0px 0px #880000;}
   @keyframes rotating { to { transform: rotate(360deg); } }
.overlay {
content: '';
left: 0px;
top: 0px;
position: absolute;
width: 260px;
height: 260px;
background: linear-gradient(45deg, transparent, 40%, rgba(255,255,255,0.25), 65%, transparent);
border-radius: 50%;
}


.inner {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 140px;
height: 140px;
box-shadow:0px 0px 0px 1px #eee,0px 0px 0px 0px #eee;
background:#000 url('https://singerimg.kugou.com/uploadpic/softhead/120/20241213/20241213201009201777.jpg') 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: #000;
border-radius: 50%;
}
#bnt{margin: 240px 20px ; width: 30px; height: 30px;position: relative; cursor: pointer; }
#cndpt{position: absolute; width: 30px; height: 30px;
background:#eee;transition: all 1s;
clip-path: polygon(0% 0%, 0% 100%, 25% 100%, 25% 0, 50% 0, 50% 100%, 75% 100%, 75% 0);}
#enopg{ position: absolute;width: 30px; height: 30px;
opacity:0; background:#eee;transition: all 1s;
   clip-path: polygon(75% 50%, 0 0, 0 100%)
}

#wz{width: 540px;height:80px;position: relative;top:-300px; left:40%; color: #000000;font:300 30px 'FZYaoti', sans-serif;transition: all 1s;}
#lyrics{width: 550px;height:80px;position: relative;top:-110px; left:20%; background:#0000;z-index: 6;}
.lyrics{margin: 0;
            top: 0%;
                left: 50%;
    transform: translate(-50%, -50%);
            height: 100px; /* 调整高度,只容纳当前歌词 */
                 text-align: center;
             position: absolute;
      }
            .lyric-line{
            width: 100%;
            position: relative;
            height: 60px;
            overflow: visible;
   font:600 45px 'FZYaoti', 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: #55aa00;
            white-space: nowrap;
            
      }

</style>
<div id="bfq">
<div id="tu">
<div id="tmsg">00:00 | 00:00</div>
<div id="bnt" >
<div id="cndpt"></div>
<div id="enopg" ></div>
</div>
</div>
<div id="cp"><div class="inner"></div><div class="overlay"></div></div>
</div>
<audio id="audio" src="https://img2.oldkids.cn/upload/2025/08/17/blog_260806313_20250817113904265.mp3" loop autoplay ></audio>

<div id="wz" >演唱*雷佳*雨中</div>
<div id="lyrics">
<div class="lyrics">
            <div class="lyric-line">
                  <div class="lyric-mask"></div>
                  <div class="lyric-original"></div>
                </div>
            </div>
</div>
<script>
audio.addEventListener('timeupdate', () => {
                tmsg.innerText = toMin(audio.currentTime) + ' | ' + toMin(audio.duration);});
let toMin = (val) => {
                if (!val) return '00:00';
                val = Math.floor(val);
                let min = parseInt(val / 60),
                        sec = parseFloat(val % 60);
                if (min < 10) min = '0' + min;
                if (sec < 10) sec = '0' + sec;
                return min + ':' + sec;
      };      

var mState = () => audio.paused ?
( bfq.style.setProperty('--state', 'paused'),bnt.title = '点击播放' ) :
( bfq.style.setProperty('--state', 'running'),bnt.title = '点击暂停' );

audio.addEventListener('pause', () => mState());
audio.addEventListener('playing', () => mState());
bnt.onclick = () => audio.paused ? (audio.play(),enopg.style.opacity= '0',cndpt.style.opacity = '1',document.getElementById("cp").style.left = '41%') : (audio.pause(),enopg.style.opacity = '1',cndpt.style.opacity = '0',document.getElementById("cp").style.left = '15%');

</script>
<script>
      // 歌词解析ksc歌词或lrc歌词
      const lrc = `karaoke.add('00:02.563', '00:04.731', '《雨中》', '320,384,384,1080');
karaoke.add('00:14.464', '00:18.375', '词曲/江南词作', '496,432,536,455,456,456,1080');
karaoke.add('00:21.703', '00:24.375', '演唱/雷佳', '392,400,376,424,1080');
karaoke.add('00:28.908', '00:35.444', '如烟的江南雨细呀细如丝', '336,280,312,400,496,1768,368,488,432,576,1080');
karaoke.add('00:36.372', '00:39.812', '飘到东来飘到西', '520,256,376,496,384,512,896');
karaoke.add('00:39.812', '00:42.772', '飘到我的心里', '264,200,224,280,912,1080');
karaoke.add('00:43.540', '00:49.924', '雨是那情呀雨是那意', '369,464,776,1007,936,648,616,488,1080');
karaoke.add('00:50.596', '00:57.236', '雨中的故事呀多呀多缠绵', '568,656,496,224,752,928,488,456,416,576,1080');
karaoke.add('00:57.956', '01:04.485', '雨水呀浇在我们的身上', '377,1056,431,833,904,376,384,264,824,1080');
karaoke.add('01:05.282', '01:11.705', '轻轻柔柔甜蜜蜜甜蜜蜜', '368,504,336,544,368,496,928,968,831,1080');
karaoke.add('01:12.561', '01:19.401', '闻说江南风景美呀', '520,1296,384,1280,960,936,384,1080');
karaoke.add('01:19.849', '01:26.329', '我只在乎雨中雨中的那个你', '376,432,368,536,376,1113,800,303,313,320,463,1080');
karaoke.add('01:28.606', '01:34.606', '字幕制作/翁奕童苑', '632,479,481,503,577,551,592,457,648,1080');
karaoke.add('01:42.014', '01:48.222', '宁城的江南雨润呀润如玉', '240,296,351,457,232,1543,601,408,496,504,1080');
karaoke.add('01:48.934', '01:52.518', '洒向你来洒向我', '640,296,352,440,472,512,872');
karaoke.add('01:52.547', '01:55.442', '洒在这段雨巷里', '256,207,264,256,352,480,1080');
karaoke.add('01:56.186', '02:02.691', '雨是那歌呀雨是那诗', '480,784,480,1008,904,913,400,456,1080');
karaoke.add('02:03.359', '02:09.999', '雨中的浪漫呀多呀多传奇', '424,592,584,216,432,1224,688,448,320,632,1080');
karaoke.add('02:10.799', '02:17.231', '雨水呀打湿我们的衣裳', '376,873,383,783,1081,344,360,296,856,1080');
karaoke.add('02:17.959', '02:24.503', '温温婉婉让人醉让人醉', '624,344,368,512,376,488,968,936,848,1080');
karaoke.add('02:25.199', '02:32.143', '都说江南多情意呀', '448,1472,808,920,880,936,400,1080');
karaoke.add('02:32.582', '02:39.287', '我只在乎雨中雨中的那个你', '545,296,440,480,384,1240,560,296,288,384,712,1080');
karaoke.add('02:40.239', '02:45.487', '字幕制作/翁奕童苑', '304,432,560,512,480,528,440,424,488,1080');
karaoke.add('03:09.407', '03:15.271', '雨水呀打湿我们的衣裳', '208,552,544,848,927,385,336,472,512,1080');
karaoke.add('03:16.055', '03:22.655', '温温婉婉让人醉让人醉', '480,480,416,480,384,464,1016,984,816,1080');
karaoke.add('03:23.407', '03:30.287', '都说江南多情意呀', '472,1432,824,1016,792,872,392,1080');
karaoke.add('03:30.743', '03:37.471', '我只在乎雨中雨中的那个你', '624,360,368,280,472,1152,688,368,312,368,656,1080');
karaoke.add('03:38.056', '03:47.352', '我只在乎雨中雨中的那个你', '584,369,384,456,423,1552,920,288,608,1280,1352,1080');
karaoke.add('03:52.544', '03:54.616', '谢谢欣赏', '368,520,512,672');
`;
const audio = document.getElementById('audio');
      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`;
      }
      
      // 监听更新歌词
      audio.addEventListener('timeupdate', () => {
            const currentTimeMs = audio.currentTime * 1000;
            const index = getCurrentLyricIndex(lyrics, currentTimeMs);
            
            if (index !== currentIndex) {
                updateLyricDisplay(index);
            }
            
            updateLyricMask(currentTimeMs);
      });
      updateLyricDisplay(0);
</script>

红影 发表于 2025-8-21 14:13

真漂亮,点击暂停,转动的光盘还会收回去呢。
歌词同步漂亮。页面上还有时间进度呢,欣赏亚伦老师好帖{:4_199:}

红影 发表于 2025-8-21 14:16

记得很早以前在mvbox里弄下来的歌词都是ksc歌词,还想着需要转成lrc才能用,现在又需要ksc了,都不知道这样格式的歌词怎么做了{:4_173:}

梦油 发表于 2025-8-21 17:09

欣赏佳作,问候亚伦。

亚伦影音工作室 发表于 2025-8-21 18:06

本帖最后由 亚伦影音工作室 于 2025-8-21 18:08 编辑 <br /><br />        <style>
#bj { position: relative;
            width: 1286px;
            height: 720px;
            margin-left: -300px;
            margin-top: 120px;
            overflow: hidden;z-index:12345;
         background:#006600
      
      }
.intro {margin: 0px0px;z-index:2;
      width: 100%;
      height:100%;
      position: absolute;
      mask-image:linear-gradient(
   90deg,
    rgba(0,0,0,0),
    rgba(0,0,0,1) 50%,
    rgba(0,0,0,1) 50%,
    rgba(0,0,0,0)
),linear-gradient(
   0deg,
    rgba(0,0,0,0),
    rgba(0,0,0,1) 50%,
    rgba(0,0,0,1) 50%,
    rgba(0,0,0,0)
);
}
</style>
<div id="bj"><img class='intro'src='https://pic1.imgdb.cn/item/639c84a6b1fccdcd36db2be7.jpg'></div>

杨帆 发表于 2025-8-21 18:41

漂亮!谢谢亚伦老师精彩分享{:4_191:}
页: [1]
查看完整版本: ksc歌词播放器界面设计