亚伦影音工作室 发表于 2025-8-5 14:11

ksc歌词同步播放器

本帖最后由 亚伦影音工作室 于 2025-8-5 14:54 编辑 <br /><br /><style>
    #bj {
      position: relative;
      width: 1186px;
      height: 680px;
      margin-left: -300px;
      margin-top: 120px;
      overflow: hidden;
      background: url('https://pic1.imgdb.cn/item/6824a19658cb8da5c8f1b56e.jpg') no-repeat center / cover;
    }

   .lrc {
      position: absolute;
      width: 100%;
      height: 60px; /* 只显示一行歌词高度 */
      top: 90%;
      left: 50%;
      transform: translate(-50%, -50%); /* 容器整体居中 */
      overflow: hidden;
      text-align: center;
    }

   .lrc #ul {
      width: 100%;
      padding: 0;
      list-style: none;
      transition: 0.1s all ease;
      margin: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
    }

   .lrc #ul li {
      font-size: 38px;
      color: #ccffcc;
      font-family: "迷你简中倩", "Microsoft YaHei", sans-serif;
      font-weight: bold;
      transition: 0.1s all ease;
      list-style-type: none;
      text-align: center;
      padding: 0 10px;
      height: 60px;
      line-height: 60px;
      cursor: pointer;
      white-space: nowrap; /* 防止歌词换行 */
    }

.lrc #ul li span {
      margin: 0 5px;
      transform-origin: center center; /* x轴和y轴均以中心为原点 */
      transition: all 0.1s cubic-bezier(0.34, 1.56, 0.64, 1); /* 缓动函数让动画更自然 */
      display: inline-block; /* 确保transform-origin生效 */
    }

   .lrc #ul li span.active {
      font-family: "迷你简中倩", "Microsoft YaHei", sans-serif;
      font-weight: bold;
      font-size: 45px;
      color: red;
      transform: scale(1.02); /* 缩放效果 */
    }
   
      .logo {
         position: absolute;
      font-size: 1.5rem;
      font-weight: bold;
      color:red;
      display: flex;
   left: 40%;
      
    }
   
    .logo::before {
      content: "新型播放器";
      margin-right: 0.5rem;
      font-size: 1.8rem;
      text-align: center;
    }
   
   
   
</style>

<div id="bj">
    <div class="lrc">
      <ul id="ul"></ul>
    </div>
<div class="logo">寻梦花园ksc歌词同步播放器</br></div>
</div>
<audio id="audio" src="https://s2.ananas.chaoxing.com/sv-w9/audio/64/33/b2/84c87854d54b80c257e7e197028e9f66/audio.mp3" loop autoplay></audio>
<script>
    var lrc = `karaoke.add('00:00.000', '00:08.708', '晚秋-黄凯芹', '622,622,622,622,622,622,622,622,622,622,622,622,622,622');
karaoke.add('00:08.710', '00:16.958', '词:许建强', '1742,1742,1742,1742,1280');
karaoke.add('00:17.420', '00:25.668', '曲:许建强', '1742,1742,1742,1742,1280');
karaoke.add('00:26.134', '00:29.794', '曾停留风里看着多少的晚秋', '90,180,190,320,340,300,370,330,380,340,410,410');
karaoke.add('00:31.904', '00:36.034', '如何能跟你说别潇洒的远走', '170,140,180,240,320,350,410,350,370,260,450,890');
karaoke.add('00:38.144', '00:42.694', '含愁凝望你 要分手是时候', '320,350,330,340,1370,170,250,370,340,370,340');
karaoke.add('00:43.704', '00:47.454', '那心间多少泪水未让流', '190,390,430,250,310,440,420,270,500,550');
karaoke.add('00:49.264', '00:53.204', '何时能解开心中多少苦与忧', '160,150,190,270,320,340,410,360,410,230,450,650');
karaoke.add('00:55.014', '00:59.194', '何时能解释心中空虚的借口', '180,140,180,260,340,350,410,370,370,310,440,830');
karaoke.add('01:01.294', '01:05.873', '情谜围住我 你此际又回眸', '320,410,340,310,1360,180,320,369,340,260,370');
karaoke.add('01:06.953', '01:10.983', '问情怀可永久相拥有', '160,350,710,310,340,390,350,400,1020');
karaoke.add('01:12.913', '01:16.993', '朦胧缠着那份爱万缕', '300,360,340,250,450,400,360,340,1280');
karaoke.add('01:18.803', '01:22.763', '明晨离别盼望爱仍留', '210,320,280,250,500,420,400,300,1280');
karaoke.add('01:24.483', '01:29.193', '共你常一对 相爱结伴随', '340,350,320,320,1270,280,710,280,390,450');
karaoke.add('01:30.223', '01:34.343', '却总怕醒觉随梦走', '190,230,330,340,360,750,670,1250');
karaoke.add('01:58.790', '02:02.630', '何时能解开心中多少苦与忧', '200,160,160,220,320,350,380,350,380,350,380,590');
karaoke.add('02:04.500', '02:08.740', '何时能解释心中空虚的借口', '140,140,200,300,350,300,400,330,420,280,470,910');
karaoke.add('02:10.800', '02:15.460', '情谜围住我你此际又回眸', '340,340,330,280,1430,180,320,370,320,290,460');
karaoke.add('02:16.350', '02:20.760', '问情怀可永久相拥有', '200,360,750,330,300,420,350,420,1280');
karaoke.add('02:22.390', '02:25.879', '朦胧缠着那份爱万缕', '310,360,320,200,480,390,370,380,679');
karaoke.add('02:28.252', '02:32.252', '明晨离别盼望爱仍留', '210,360,320,230,550,410,310,330,1280');
karaoke.add('02:34.072', '02:38.632', '共你常一对 相爱结伴随', '220,330,340,260,1310,270,720,280,430,400');
karaoke.add('02:39.652', '02:43.821', '却总怕醒觉随梦走', '150,240,370,290,489,670,680,1280');
karaoke.add('02:45.171', '02:49.091', '何时能解开心中多少苦与忧', '170,150,170,240,380,270,450,260,380,370,380,700');
karaoke.add('02:50.871', '02:55.141', '何时能解释心中空虚的借口', '80,170,210,270,350,350,380,370,400,210,440,1040');
karaoke.add('02:57.091', '03:01.731', '情谜围住我你此际又回眸', '350,400,330,400,1290,190,270,380,310,360,360');
karaoke.add('03:02.751', '03:06.921', '问情怀可永久相拥有', '190,360,670,260,430,370,370,410,1110');
karaoke.add('03:08.681', '03:13.401', '唯求原谅我恕失敬别离后', '350,450,270,340,1390,190,290,340,360,350,390');
karaoke.add('03:14.361', '03:21.411', '未回头因已经心伤透', '180,320,1390,410,930,500,640,1450,1230');
`;

    let lrcArr = lrc.split('\n');
    let result = [];
    var audio = document.querySelector("#audio"),
      ul = document.querySelector("#ul"),
      container = document.querySelector(".lrc");

    const lrcRegex = /karaoke\.add\('([\d:/.]+)',\s*'([\d:/.]+)',\s*'([^']*)'/;
    for (let i = 0; i < lrcArr.length; i++) {
      const line = lrcArr.trim();
      if (!line) continue;
      const match = line.match(lrcRegex);
      if (!match) continue;

      const startTimeStr = match;
      const endTimeStr = match;
      const lineText = match.trim();
      if (!lineText) continue;

      const startTime = parseTime(startTimeStr);
      const endTime = parseTime(endTimeStr);
      const lineDuration = endTime - startTime;

      const words = lineText.split('');
      const wordCount = words.length;
      if (wordCount === 0) continue;

      const wordDuration = lineDuration / wordCount;
      const lineWords = [];
      words.forEach((word, index) => {
            lineWords.push({
                time: startTime + index * wordDuration,
                text: word
            });
      });
      result.push(lineWords);
    }

    function parseTime(timeStr) {
      const = timeStr.split(':');
      return parseInt(minutes, 10) * 60 + parseFloat(seconds);
    }

    function getCurrentLineIndex() {
      const currentTime = audio.currentTime;
      for (let i = 0; i < result.length; i++) {
            const line = result;
            if (currentTime >= line.time &&
                (i === result.length - 1 || currentTime < result.time)) {
                return i;
            }
      }
      return -1;
    }

    function createLyricElements() {
      const frag = document.createDocumentFragment();
      result.forEach((lineWords, lineIndex) => {
            const li = document.createElement("li");
            lineWords.forEach(wordObj => {
                const span = document.createElement("span");
                span.textContent = wordObj.text;
                span.dataset.time = wordObj.time;
                li.appendChild(span);
            });
            li.addEventListener('click', () => {
                audio.currentTime = lineWords.time;
            });
            frag.appendChild(li);
      });
      ul.appendChild(frag);
    }

    createLyricElements();

    const liHeight = 60;
    const visibleLines = 1;
    let prevLineIndex = -1;

    function syncLyric() {
      const currentLineIndex = getCurrentLineIndex();
      if (currentLineIndex === -1) return;

      const scrollOffset = currentLineIndex * liHeight;
      ul.style.transform = `translateY(-${scrollOffset}px)`;

      if (prevLineIndex!== -1 && prevLineIndex!== currentLineIndex) {
            const prevLi = ul.children;
            if (prevLi) {
                prevLi.querySelectorAll('span.active').forEach(span => {
                  span.classList.remove('active');
                });
            }
      }

      const currentLi = ul.children;
      const currentLine = result;
      const currentTime = audio.currentTime;

      currentLine.forEach((wordObj, wordIndex) => {
            const span = currentLi.children;
            const isActive = currentTime >= wordObj.time &&
                            (wordIndex === currentLine.length - 1 ||
                           currentTime < currentLine.time);
            
            if (isActive) {
                span.classList.add('active');
            } else {
                span.classList.remove('active');
            }
      });

      prevLineIndex = currentLineIndex;
    }

    audio.addEventListener("timeupdate", syncLyric);
</script>

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

逐个字的高亮,这样的歌词同步好看。虽然很难完全对上文字{:4_199:}

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

欣赏亚伦老师好帖{:4_199:}

梦油 发表于 2025-8-5 14:37

欣赏佳作,问候亚伦。

杨帆 发表于 2025-8-5 18:59

漂亮!谢谢亚伦老师精彩分享{:4_190:}

樵歌 发表于 2025-8-5 20:23

精彩!
页: [1]
查看完整版本: ksc歌词同步播放器