马黑黑 发表于 2024-2-1 08:25

range进度条播放器+LRC歌词同步教程(一)

<style>
.mum { font-size: 18px; }
.mum mark { padding: 0 4px; background: lightblue; }
.mum pre { padding: 12px; background: #eee; color: blue; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; tab-size: 4; white-space: pre-wrap; word-wrap: break-word; }
.mum a { color: red; }
.mum a:hover { color: darkred; }
.tGreen { color: green; }
.tRed { color: red; }

.lrc {
        position: relative;
        width: fit-content;
        font: bold 2.4em sans-serif;
        color: lightblue;
        text-shadow: 1px 1px 1px rgba(0,0,0,.45);
}
.lrc::before {
        position: absolute;
        content: attr(data-lrc);
        width: 100%;
        height: 100%;
        color: transparent;
        background: linear-gradient(rgba(250,0,0,.7),rgba(0,0,180,.8));
        background-clip: text;
        -webkit-background-clip: text;
        clip-path: inset(0 60% 0 0);
        border-bottom: 1px solid navy;
}
.lrctest::before { clip-path: inset(0 100% 0 0); animation: lrcGo 4s linear infinite; }
@keyframes lrcGo { to { clip-path: inset(0 0 0 0); } }
</style>

<div class="mum">

<p>本教程是 range 进度条播放器的延续与扩展,主要探讨在现有播放器基础上,如何实现LRC歌词同步。这里所说的同步是模拟性质的,要真的做到专业级别的LRC歌词同步,需要十分复杂、繁琐的实现机制,我们的方法则是相当简洁的。</p>
<p>本节要讲的内容是如何通过CSS关键帧动画模拟LRC歌词同步,属于UI界面的范畴,暂时和range播放器扯不上关系(不过放心,将range播放器整合过来的时机会很快到来)。思路大概是这样:通过一个<mark>歌词容器+伪元素</mark>的方法来完成UI的设计,具体而言,容器元素显示底词,伪元素采用 clip-path 的切割方式模拟歌词同步。请看 #lrc 选择器和其伪元素的CSS代码、html详细代码:</p>
<pre>
&lt;style&gt;
#lrc {
        position: absolute;
        font: bold 2.4em sans-serif;
        color: lightblue;
        text-shadow: 1px 1px 1px rgba(0,0,0,.45);
}
#lrc::before {
        position: absolute;
        content: attr(data-lrc); <span class="tGreen">/* 通过 attr 函数,伪元素接收主元素传递的 data-lrc 文本值接口 */</span>
        width: 100%;
        height: 100%;
        color: transparent; <span class="tGreen">/* 前景色透明 :文本将呈现元素的背景图片 */</span>
        background: linear-gradient(rgba(250,0,0,.7),rgba(0,0,180,.8)); <span class="tGreen">/* 渐变背景 */</span>
        background-clip: text; <span class="tGreen">/* 背景切割 :只在文本上显示背景图片 */</span>
        -webkit-background-clip: text; <span class="tGreen">/* Chromium内核前缀属性 :它实现了text裁切值 */</span>
        clip-path: inset(0 <span class="tRed">60%</span> 0 0); <span class="tGreen">/* 内矩形切割元素 :60%观察效果用,实际使用时改为100%(开始时全割掉) */</span>
        border-bottom: 1px solid navy; <span class="tGreen">/* 底边框 */</span>
}
&lt;/style&gt;

&lt;div id="lrc" <span class="tRed">data-lrc="HuaChao LRC"</span>&gt;HuaChao LRC&lt;/div&gt;
</pre>
<p>看看效果:</p>
<div class="lrc" data-lrc="HuaChao LRC">HuaChao LRC</div>
<p>是不是很酷?这是伪元素右边裁切 60% 的效果 [ clip-path: inset(0 60% 0 0); ]。</p>
<p>要点:</p>
<p>(一)CSS里,<span class="tRed">主元素和伪元素均需要定位</span>,当主元素绝对定位,其宽高会根据里面的(非绝对定位的)内容自动伸缩,宽度在这里尤其重要,它是伪元素切割的尺寸依据,所以,主元素 position: absolute 的设置是很恰当的。当然,设置为相对定位也不是不可以,只是需要加入额外的属性设置,且在帖子中的定位也不那么方便,这里不推荐(但本文的演示使用到了relative定位,出于排版等需要)。</p>
<p>(二)<span class="tRed">伪元素绝对定位是必须的</span>,这样,100%宽高设置会令它与主元素完全贴合。伪元素的字体不用设计,它继承父元素的值,文本阴影、前景色等也有继承性。伪元素的文本颜色设为透明,因为,我们用 background-clip 属性定义了伪元素的背景颜色剪裁为文字显示,就是说,伪元素的背景只有在文字笔画之上显示出来。这里,我们使用渐变做伪元素的背景,可以使用图片。然后,非常重要的,我们使用 clip-path 来切割伪元素,切割方式是 inset(内矩形切割),按上、右、下、左的顺序布置参数,我们测试用的右边参数为 60% ,只是用来观察效果。最后我们还给伪元素的底边框设置为1像素,像是漂亮的下划线,有一定的修饰和提示作用。</p>
<p>(三)和播放器的时间信息显示原理一样,我们依然<span class="tRed">使用伪元素的 content: attr(data-xx) 来接收主元素传来的相应值</span>,以便让主元素的文本和伪元素的文本保持一致(将来JS通过 <mark>元素.dataset.名称</mark> 方法读写 data-xx 值)。这里再次提示,<mark>attr()</mark> 是一个CSS内置函数,它接收的参数结构固定为 <span class="tRed">data-名称</span>,data-名称的内容则由html对应元素提供,JS可以对之动态读写。</p>
<p>最后,我们设置一个关键帧动画,并让伪元素运行这个动画。看代码:</p>
<pre>
&lt;style&gt;
#lrc {
        position: absolute;
        font: bold 2.4em sans-serif;
        color: lightblue;
        text-shadow: 1px 1px 1px rgba(0,0,0,.45);
}
#lrc::before {
        position: absolute;
        content: attr(data-lrc);
        width: 100%;
        height: 100%;
        color: transparent;
        background: linear-gradient(rgba(250,0,0,.7),rgba(0,0,180,.8));
        background-clip: text;
        -webkit-background-clip: text;
        <span class="tRed">clip-path: inset(0 100% 0 0);</span> <span class="tGreen">/* 开始时裁切玩右边 */</span>
        animation: lrcGo 4s linear <span class="tRed">forwards</span>; <span class="tGreen">/* 动画停留在最后一个动作 */</span>
        border-bottom: 1px solid navy;
}
<span class="tGreen">/* 关键帧动画 :到裁切 0 (元素初始裁切 100%) */</span>
@keyframes lrcGo { to { <span class="tRed">clip-path: inset(0 0 0 0);</span> } }
&lt;/style&gt;

&lt;div id="lrc" data-lrc="HuaChao LRC"&gt;HuaChao LRC&lt;/div&gt;
</pre>
<p>效果:</p>
<div class="lrc lrctest" data-lrc="HuaChao LRC">HuaChao LRC</div>
<p>这是不是酷的不要不要的?演示的效果是不停歇地运行,给出的上述代码只运行一次、且结束后停留在最终状态,这是lrc歌词同步的需要。上述代码,可以保存为本地html文档后运行,或将代码复制到 <a href="http://mhh.52qingyin.cn/api/pcode/" target="_blank">pencil code</a> 测试。</p>

</div>

马黑黑 发表于 2024-2-1 08:29

LRC歌词同步原理可以这么概括:

伪元素遮盖父元素。伪元素用 clip-path 方式裁切,开始时切完(元素定义的 clip-path),然后通过运行关键帧动画把裁切的比例从 100% 到 0 过渡。

山里人 发表于 2024-2-1 09:21

酷酷酷酷

亚伦影音工作室 发表于 2024-2-1 14:43

又一力作,期待1

樵歌 发表于 2024-2-1 14:45

学生些呢{:4_203:}

红影 发表于 2024-2-1 18:34

又是好东西。都是跟在后面用歌词同步,从没了解它的细部,原来60%还有专业的效果啊{:4_199:}

红影 发表于 2024-2-1 18:37

border-bottom: 1px solid navy; 这个好看,还有根细线跟着{:4_199:}

红影 发表于 2024-2-1 18:38

“这是不是酷的不要不要的?”——真的太酷了{:4_199:}

红影 发表于 2024-2-1 18:40

“attr() 是一个CSS内置函数,它接收的参数结构固定为 data-名称,data-名称的内容则由html对应元素提供,JS可以对之动态读写。”
嗯嗯,努力记下{:4_178:}

醉美水芙蓉 发表于 2024-2-1 19:14

马黑黑 发表于 2024-2-1 19:15

红影 发表于 2024-2-1 18:40
“attr() 是一个CSS内置函数,它接收的参数结构固定为 data-名称,data-名称的内容则由html对应元素提供,J ...

其实简单哈。这是CSS、HTML、JS的精巧设计。

马黑黑 发表于 2024-2-1 19:16

山里人 发表于 2024-2-1 09:21
酷酷酷酷

再酷没你酷{:4_173:}

马黑黑 发表于 2024-2-1 19:16

红影 发表于 2024-2-1 18:38
“这是不是酷的不要不要的?”——真的太酷了

谢谢认同

马黑黑 发表于 2024-2-1 19:17

醉美水芙蓉 发表于 2024-2-1 19:14
非常期待老师的精彩!

这个不精彩么

马黑黑 发表于 2024-2-1 19:17

亚伦影音工作室 发表于 2024-2-1 14:43
又一力作,期待1

谢谢

马黑黑 发表于 2024-2-1 19:17

樵歌 发表于 2024-2-1 14:45
学生些呢

都旷课了

马黑黑 发表于 2024-2-1 19:18

红影 发表于 2024-2-1 18:37
border-bottom: 1px solid navy; 这个好看,还有根细线跟着

CSS复合属性,其实都有规律,像这个 border-bottom,很语义化。

马黑黑 发表于 2024-2-1 19:18

红影 发表于 2024-2-1 18:34
又是好东西。都是跟在后面用歌词同步,从没了解它的细部,原来60%还有专业的效果啊

60%是演示用

红影 发表于 2024-2-1 22:08

马黑黑 发表于 2024-2-1 19:15
其实简单哈。这是CSS、HTML、JS的精巧设计。

非常实用的一个函数呢{:4_187:}

红影 发表于 2024-2-1 22:10

马黑黑 发表于 2024-2-1 19:16
谢谢认同

客气得让人不好意思{:4_173:}
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: range进度条播放器+LRC歌词同步教程(一)