歌词滚动效果

css 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 设置标签页图标 -->
    <link rel="shortcut icon" href="./favicon.ico" type="image/x-icon">
    <title>Document</title>
    <style>
        html {
            background: #000;
            color: aliceblue;
            width: 100%;
            height: 100%;
        }
        body {
            display: flex;
            flex-direction: column;
            width: 100%;
            height: 100%;
            overflow: hidden;
        }
        ul, li {
            margin: 0;
            padding: 0;
        }
        .head {
            flex: none;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 30px 0;
        }
        img {
            width: 100px;
            height: 100px;
            border-radius: 50%;
            margin-right: 20px;
        }
        .right {
            display: flex;
            flex-direction: column;
            justify-content: center;
        }
        .des {
            margin: 6px;
            padding-left: 7px;
        }
        audio {
            width: 600px;
            height: 60px;
        }
        .content {
            flex: 1;
            overflow: hidden;
            margin: 0;
        }
        #list {
            list-style: none;
            transition: .5s ease;
        }
        .lyric {
            text-align: center;
            height: 40px;
            line-height: 40px;
            transition:  0.2s;
        }
        .active {
            /* font-size: 20px; // 过渡会引起回流,推荐使用tranform */
            transform: scale(1.2);
            color: aquamarine;
        }
    </style>
</head>
<body>
    <div class="head">
        <img src="http://p3fx.kgimg.com/uploadpic/softhead/120/20241213/20241213201306973919.jpg" />
        <div class="right">
            <p class="des">江语晨-最后一页</p>
             <!-- 加controls这个属性才能看到audio标签 -->
            <audio controls autoplay src = "https://sharefs.tx.kugou.com/202505141511/3fdf7e70100a2d282f80b08266c04d5d/v3/653e2f76f69079273c79f65fecf58aea/yp/full/ap1000_us0_pi409_s2981929298.mp3">
        </div>
    </div>
    <div class="content">
        <ul id="list"></ul>
    </div>
    <script>    
        const lyr = "[00:00.00]滴滴音乐网 www.dda5.com\n[00:00.20]\n[00:00.56]雨停滞天空之间\n[00:04.07]像泪在眼眶盘旋\n[00:07.90]这也许是最后一次见面\n[00:15.43]沿途经过的从前\n[00:19.14]还来不及再重演\n[00:23.86]拥抱早已悄悄冷却\n[00:30.84]海潮声 淹没了离别时的黄昏\n[00:38.36]只留下不舍的体温\n[00:45.70]星空下 拥抱着快凋零的温存\n[00:52.96]爱只能在回忆里完整\n[01:03.92]想把你抱进身体里面\n[01:09.45]不敢让你看见\n[01:13.09]嘴角那颗没落下的泪\n[01:19.73]如果这是最后的一页\n[01:24.34]在你离开之前\n[01:28.08]能否让我把故事重写\n[02:38.01]雨停滞天空之间\n[02:41.62]像泪在眼眶盘旋\n[02:45.45]这也许是最后一次见面\n[02:53.00]沿途经过的从前\n[02:56.70]还来不及再重演\n[03:01.47]拥抱早已悄悄冷却\n[03:08.49]海潮声 淹没了离别时的黄昏\n[03:15.89]只留下不舍的体温\n[03:23.26]星空下 拥抱着快凋零的温存\n[03:30.52]爱只能在回忆里完整\n[03:37.83]想把你抱进身体里面\n[03:43.08]不敢让你看见\n[03:46.85]嘴角那颗没落下的泪\n[03:52.90]如果这是最后的一页\n[03:58.10]在你离开之前\n[04:01.81]能否让我把故事重写\n[04:08.04]想把你抱进身体里面\n[04:13.23]不敢让你看见\n[04:16.83]嘴角那颗没落下的泪\n[04:22.88]如果这是最后的一页\n[04:28.12]在你离开之前\n[04:31.83]能否让我把故事重写\n";
        const avatar = "http://p3fx.kgimg.com/uploadpic/softhead/120/20241213/20241213201306973919.jpg";
        const src = "https://sharefs.tx.kugou.com/202505141511/3fdf7e70100a2d282f80b08266c04d5d/v3/653e2f76f69079273c79f65fecf58aea/yp/full/ap1000_us0_pi409_s2981929298.mp3";
        const audio = document.querySelector('audio');
        audio.src = src;
        const lyrics = lyr.split('\n').filter(v => v).map((item) => {
            // const matchs = item.match(/\[(\S+)\](\S*)/);
            const [_, time, sentence] = item.split(/[\[\]]/); // 根据[或]符号分割
            const [minute, second] = time.split(':');  // 00:30.84
            const fTime = +minute * 60 + +second;
            return { time: fTime, sentence };
        });
        console.log('lyrics', lyrics);
        const content = document.querySelector('.content');
        const list = document.querySelector('#list');
        // 创建一个文档片段,把所有dom的操作先应用在这个片段上,之后将片段加入文档,就可以做到将多次dom操作合并成一次,提高效率。
        const frag = document.createDocumentFragment();
        lyrics.forEach(({ sentence }) => {
            const li = document.createElement('div');
            //innerText和textContent的区别;attributes和property的区别
            li.innerText = sentence;
            // add remove toggle
            li.classList.add('lyric');
            // 操作dom树次数过多,需要优化
            frag.appendChild(li);
        });
        list.appendChild(frag);
        // console.dir(list);
        audio.play();

        // 监听歌曲进度,设置歌词偏移量
        const contentHeight = content.clientHeight;
        const listHeight = list.clientHeight;
        const lyricHeight = list.children[0].clientHeight;
        const minOffset = 0;
        const maxOffset = listHeight - contentHeight;
        const scrollSentence = () => {
            const currentTime = audio.currentTime;
            const index = lyrics.findIndex(({ time }) => time > currentTime);
            const currentIndex = index < 0 ? lyrics.length - 1 : index - 1;
            let offset = currentIndex * lyricHeight + lyricHeight / 2 - contentHeight / 2;
            if (offset < 0) {
                offset = 0;
            }
            if (offset > maxOffset) {
                offset = maxOffset;
            }
            list.style.transform = `translateY(-${offset}px)`;
            const activeLi =  list.querySelector('.active');
            if (activeLi) {
                activeLi.classList.remove('active');
            }
            const li = list.children[currentIndex];
            if (li) {
                li.classList.add('active');
            }
        }
        audio.ontimeupdate = (e) => {
            scrollSentence();
        };
    </script>
</body>
</html>

效果:

相关推荐
Ten peaches12 分钟前
Selenium-Java版(操作元素)
java·selenium·测试工具·html
恋猫de小郭1 小时前
如何查看项目是否支持最新 Android 16K Page Size 一文汇总
android·开发语言·javascript·kotlin
赵大仁2 小时前
React Native 与 Expo
javascript·react native·react.js
程序员与背包客_CoderZ3 小时前
Node.js异步编程——Callback回调函数实现
前端·javascript·node.js·web
清灵xmf3 小时前
从 Set、Map 到 WeakSet、WeakMap 的进阶之旅
前端·javascript·set·map·weakset·weakmap
运维@小兵4 小时前
vue使用路由技术实现登录成功后跳转到首页
前端·javascript·vue.js
肠胃炎4 小时前
React构建组件
前端·javascript·react.js
邝邝邝邝丹4 小时前
React学习———React.memo、useMemo和useCallback
javascript·学习·react.js
酷爱码4 小时前
HTML5表格语法格式详解
前端·html·html5
美酒没故事°5 小时前
纯css实现蜂窝效果
前端·javascript·css