马黑黑 发表于 2025-12-3 08:14

原生lrc歌词同步新尝试

<div class="codebox" data-prev="1">
&lt;style&gt;
        #pa {
                margin: 20px auto;
                width: 1024px ;
                height: 640px;
                border: 1px solid gray;
                display: grid;
                place-items: center;
                position: relative;
        }
        #wrapper {
                position: absolute;
                padding: 10px;
                font: bold 2rem/1.2 sans-serif;
                text-shadow: 1px 1px 1px gray;
                bottom: 20px;
        }
        .char {
                display: inline-block;
                padding: 0 2px;
                opacity: 0;
                transform: translate(var(--x), var(--y));
                animation: fadeIn 0.3s var(--delay) forwards;
        }
        audio { position: absolute; left: 20px; top: 20px; }
        @keyframes fadeIn {
                to {
                        transform: translate(0, 0);
                        opacity: 1;
                }
        }
       
&lt;/style&gt;

&lt;div id="pa"&gt;
        &lt;audio id="aud" src="https://music.163.com/song/media/outer/url?id=3319366184" controls autoplay&gt;&lt;/audio&gt;
        &lt;div id="wrapper"&gt;HUACHAO LRC&lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;

        const gc = `王睿卓 - 花田错\n我犯了错\n夜好深了 纸窗里怎么亮着\n那不是 彻夜等候 你为我点的烛火\n不过是 一次邂逅 红楼那一场梦\n我的山水 全部褪色 像被大雨洗过\n杯中景色鬼魅 我忘了我是谁\n心情就像夜凉如水\n手里握着蝴蝶杯 单飞 不醉不归\n花田里犯了错\n说好 破晓前忘掉\n花田里犯了错\n拥抱 变成了煎熬\n花田里犯了错\n犯错像迷恋镜花水月的无聊\n花田里犯了错\n请原谅我多情的打扰\n醉 怎么会喝醉\n美 因为你的美\n爱匆匆一瞥不过点缀\n飞 看大雪纷飞\n却 再也找不回\n被白雪覆盖那些青翠\n当时空成为拥有你\n唯一条件 我又醉\n琥珀色的月 结了霜的泪\n我会记得这段岁月\n花田里犯了错\n说好 破晓前忘掉\n花田里犯了错\n拥抱 变成了煎熬\n花田里犯了错\n犯错像迷恋镜花水月的无聊\n花田里犯了错\n请原谅我多情的打扰\n我的山水全部褪了色\n多情的打扰请原谅我\n不是彻夜为我点的火\n在那花田里我犯了错\n我的山水全部褪了色\n多情的打扰请原谅我\n不是彻夜为我点的火\n在那花田里我犯了错\n花田里犯了错\n说好 破晓前忘掉\n花田里犯了错\n拥抱 变成了煎熬\n花田里犯了错\n犯错像迷恋镜花水月的无聊\n花田里犯了错\n请原谅我多情的打扰\n我犯了错`;

        const gcAr = lrc2HC(gc);
        let curkey = 0, isSeeking = false;

        aud.ontimeupdate = () =&gt; {
                if(curkey &gt; gcAr.length - 1) return;
                if(aud.currentTime &gt;= gcAr) {
                        const gap = gcAr?. ?? 0 - gcAr;
                        showLrc(gcAr, wrapper, gap);
                }
        };

        aud.onended = () =&gt; {
                curkey = 0;
                aud.play();
        }

        aud.onseeked = () =&gt; calcKey();

        function lrc2HC(text) {
                let lrcAr = [];
                let ar = text.trim().split('\n');
                ar.sort();
                let reg = /\[(\d+)[.:](\d+)[.:](\d+)\](.*)/;
                ar.forEach(item =&gt; {
                        if(reg.test(item)) {
                                let result = item.match(reg);
                                let tmsg = parseInt(result) * 60 + parseInt(result) + parseInt(result) / 1000;
                                lrcAr.push(.trim()]);
                        }
                });
                return lrcAr ? lrcAr : ;
        };

        function calcKey() {
                for (let j = 0; j &lt; gcAr.length; j++) {
                        if (aud.currentTime &lt;= gcAr) {
                                curkey = j - 1;
                                break;
                        }
                }
                if (curkey &lt; 0) curkey = 0;
                if (curkey &gt; gcAr.length - 1) curkey = gcAr.length - 1;
                let time = gcAr?. ?? 0 - gcAr;
                isSeeking = false;
                showLrc(gcAr, wrapper, time);
        }

        function showLrc(str, targetElm, time) {
                if(isSeeking) return;
                targetElm.innerHTML = '';
                const chars = str.split('').map(c =&gt; c === ' ' ? '&nbsp;' : c);
                const frg = document.createDocumentFragment();
                chars.forEach((char, idx) =&gt; {
                        const span = document.createElement('span');
                        span.innerHTML = char;
                        span.classList.add('char');
                        const x = Math.random() * (Math.random() &gt; 0.5 ? 300 : -300);
                        const y = Math.random() * (Math.random() &gt; 0.5 ? 300 : -300);
                        span.style.cssText += `
                                color: #${Math.random().toString(16).substring(2,8)};
                                --x: ${x}px;
                                --y: ${y}px;
                                --delay: ${Math.random() * 0.5}s;
                        `;
                        frg.appendChild(span);
                });
                targetElm.appendChild(frg);
                curkey ++;
                setTimeout(() =&gt;isSeeking = false, time);
        }
&lt;/script&gt;
</div>

<script type="module">
import linenumber from 'https://638183.freep.cn/638183/web/js/linenumber.js';
linenumber();
</script>

马黑黑 发表于 2025-12-3 08:26

这里的 lrc2HC() 函数(行62~75)将原生lrc歌词转为早期的花潮lrc数组格式,得到的结果类似酱紫:

      [ , ]

然后就处理这些数组,实现歌词同步。不过函数未处理原生复合歌词,若有,需要手动拆开为多行。

这里的歌词同步方式是新尝试:将歌词文本逐个封装在 span 标签里,给予每一个不同的文本色,出场时运行关键帧动画 fadeIn,从四面八方入框(坑)。此为 showLrc() 函数,行 91~113 便是。

代码无任何第三方依赖,完全自给自足,在此基础上可以考虑完善和扩展。

马黑黑 发表于 2025-12-3 08:28

如果需要竖排,仅需将第 19 行CSS代码的 display 属性值 改为 block

马黑黑 发表于 2025-12-3 08:29

代码于昨晚匆忙写就,其中 lrc2HC() 等函数的代码是以前写过的代码

亚伦影音工作室 发表于 2025-12-3 10:50

这里好厉害&#128077;&#127995;


也曾年轻 发表于 2025-12-3 10:53

有特色的showLrc

评分后代码就全挤在一起了

清茶煮雪 发表于 2025-12-3 11:24

感谢老师精彩分享{:4_187:}

马黑黑 发表于 2025-12-3 11:56

也曾年轻 发表于 2025-12-3 10:53
有特色的showLrc

评分后代码就全挤在一起了

刷新一下。这个目前找不到办法处理:论坛的局部刷新机制对 JS 的新声明关键字 const、let 以及对ES6不支持继承

马黑黑 发表于 2025-12-3 11:56

清茶煮雪 发表于 2025-12-3 11:24
感谢老师精彩分享

{:4_190:}

马黑黑 发表于 2025-12-3 11:56

亚伦影音工作室 发表于 2025-12-3 10:50
这里好厉害&#128077;&#127995;

{:4_190:}

杨帆 发表于 2025-12-3 14:51

原生lrc歌词同步,逐字变色、逐字运动

代码无任何第三方依赖,既有复用性,还有扩展性。

辛苦了!马老师威武~{:4_176:}

红影 发表于 2025-12-3 18:20

这个太厉害了,逐字变色和有不同的动态方式入坑,让歌词太好看了{:4_199:}

红影 发表于 2025-12-3 18:22

这个让歌词很奇幻,其实前面的逐字高亮也是特色,也喜欢前面的{:4_187:}
这个不会逐字高亮了呢。

红影 发表于 2025-12-3 18:23

黑黑总能带来惊喜,厉害的{:4_199:}

马黑黑 发表于 2025-12-3 19:52

红影 发表于 2025-12-3 18:23
黑黑总能带来惊喜,厉害的

哪里哪里

马黑黑 发表于 2025-12-3 19:53

红影 发表于 2025-12-3 18:22
这个让歌词很奇幻,其实前面的逐字高亮也是特色,也喜欢前面的
这个不会逐字高亮了呢。

这个采用了另一种同步方式,不是模拟逐字高亮

马黑黑 发表于 2025-12-3 19:53

红影 发表于 2025-12-3 18:20
这个太厉害了,逐字变色和有不同的动态方式入坑,让歌词太好看了

有点创意吧

马黑黑 发表于 2025-12-3 19:54

杨帆 发表于 2025-12-3 14:51
原生lrc歌词同步,逐字变色、逐字运动

代码无任何第三方依赖,既有复用性,还有扩展性。


{:4_191:}

小辣椒 发表于 2025-12-3 22:40

这个是创新了{:4_199:}{:4_199:}

马黑黑 发表于 2025-12-3 23:34

小辣椒 发表于 2025-12-3 22:40
这个是创新了

{:4_190:}
页: [1]
查看完整版本: 原生lrc歌词同步新尝试