马黑黑 发表于 2024-2-2 14:36

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

<style>
.mum { font-size: 18px; }
.mum pre { padding: 12px; background: #eee; color: navy; 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; }
</style>

<div class="mum">

<p>本系列教程的上一讲,<a href="https://www.huachaowang.com/forum.php?mod=viewthread&tid=73959&extra=page%3D1" target="_blank">《range进度条播放器+LRC歌词同步教程(一)》</a>,我们完成了LRC歌词显示的界面+歌词同步关键帧动画,但它还不能投入工作,我们有很多问题要解决。本讲要处理的问题是一个单一的问题:如何反复运行CSS关键帧动画。</p>
<p>同一个CSS关键帧动画,运行完一次之后再去运行,除非 animation 设为永动(infinite),否则,相同的元素再去运行同名动画无效,纯JS的传统解决方案是先清除动画,再命令元素运行动画,而且不能清除后马上运行,中间需要一个等待时间,为此需要一个 setTimeout 定时器,用于负责在动画运行结束时清除动画属性或动画名称,不能提前(否则动画还没执行完毕就已关闭)也不能太拖后(否则影响下一个动画的运行),时间要掐的恰到好处。然后呢,其它机制检测到应该运行下一个动画了,再加入动画属性或动画名称给元素。这是可行的办法,不过它涉及到的编程开销偏大也略显复杂,所以我个人倾向于使用另外一个实现手段:设计两个内容相同的关键帧动画,运行时让元素在两个动画间来回切换使用,永不重复,这是我们之前一直使用的lrc歌词插件使用的方法。</p>
<p>看看在上一讲基础上改造过的CSS基于动画的核心代码:</p>
<pre>
#lrc {} <span class="tGreen">/* 主元素代码略 */</span>
#lrc::before {
        <span class="tGreen"> /* 其它代码略 */</span>
        clip-path: inset(0 <span class="tRed">100%</span> 0 0);
        animation: var(--ani) 4s linear forwards;
}
@keyframes lrcGo0 { to { clip-path: inset(0 <span class="tRed">0</span> 0 0); } }
@keyframes lrcGo1 { to { clip-path: inset(0 <span class="tRed">0</span> 0 0); } }
</pre>
<p>以上,我们设计了两个名称不同、内容一样的关键帧动画,它们都极其简短,因为伪元素内的 clip-path 属性设定为右边全部裁切(100%),关键帧动画只需提供终点裁切点(0)就行。完美主义者或强迫症患者——二者有时没啥区别——会担心关键帧动画没有起始点不安全,若此,加上 from 也不是什么难事,只需把伪元素里的 clip-path 语句塞进 from 语句即可。</p>
<p>接下来,我们试着用JS的 setInterval 定时器来模拟音乐循环播放永不停歇的样纸,里面我们设置一个自增变量 add,当 add 值被 5 整除,就让 lrc 伪元素运行动画,动画每一次运行4秒钟(这是animation属性规定好的,将来这个运行时长为不确定时间)。先看代码:</p>
<pre>
var add = 0, lastIdx = 0, aniAr = ['lrcGo0','lrcGo1'];

setInterval( () => <span class="tRed">{</span>
        if (add % 5 == 0) {
                document.title = add;
                lrc.style.setProperty('--ani', aniAr);
                lastIdx= lastIdx === 0 ? 1 : 0;
        }
        add = (add + 1) % 20;
<span class="tRed">}</span>, 1000);
</pre>
<p>首先声明几个变量,add 用以计时;lastIdx 是动画索引依据;aniAr 是装载动画名称的数组。接着,让 setInterval 定时器投入工作,红色花括号 <span class="tRed">{}</span> 里是 setInterval 要执行的代码,每隔一秒执行一次:先用一个条件语句检测 add 的值是否被5整除,如果被整除,则令页面标题(浏览器标签的内容)显示当前 add 值(观察之用),用元素的 style.setProperty() 方法设置lrc元素的CSS变量 --ani 的值,其值从动画名称数组中获取,然后用一个三元运算处理 lastIdx 索引值,让它在 0 和 1 之间不断更换。定时器最后令 add 变量自增,但其值总是受到 20 的干预、到了 19 就会从头再来,这样,动画一个巡回执行4次,对应的 add 值分别为 0、5、10、15,15到20之间刚好留给一轮中的最后一个动画足够的运行时间而后从头继续运行。</p>
<p>本节讲到这里,主要试验一下关键帧动画反复调用功能,现在可以给出完整代码示例了。以下代码,我们给 lrc 标签找了一个爸爸,让它有足够的施展空间。运行后会发现,第一节我们设计的lrc的界面有了特定舞台背景后愈发漂亮迷人——</p>
<pre>
&lt;style&gt;
#papa {
        margin: auto;
        width: 800px;
        height: 360px;
        background: linear-gradient(tan,gray);
        box-shadow: 3px 3px 20px #000;
        position: relative;
        display: grid;
        place-items: center;
}
#lrc {
        position: absolute;
        top: 10px;
        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 100% 0 0);
        animation: var(--ani) 2s linear forwards;
        border-bottom: 1px solid navy;
}
@keyframes lrcGo0 { to { clip-path: inset(0 0 0 0); } }
@keyframes lrcGo1 { to { clip-path: inset(0 0 0 0); } }
&lt;/style&gt;

&lt;div id="papa"&gt;
        &lt;div id="lrc" data-lrc="HuaChao LRC"&gt;HuaChao LRC&lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;

var add = 0, lastIdx = 0, aniAr = ['lrcGo0','lrcGo1'];

setInterval( () =&gt; {
        if (add % 5 == 0) {
                document.title = add;
                lrc.style.setProperty('--ani', aniAr);
                lastIdx= lastIdx === 0 ? 1 : 0;
        }
        add = (add + 1) % 20;
}
, 1000);

&lt;/script&gt;
</pre>
<p>代码就不在这里演示了,可到 <a href="http://mhh.52qingyin.cn/api/pcode/" target="_blank">pencil code</a> 运行,或存为本地html文档后运行。</p>

</div>

红影 发表于 2024-2-2 15:13

去运行了。这个歌词还带下面的线条呢,漂亮{:4_187:}

樵歌 发表于 2024-2-2 15:15

辛苦了不少脑细胞{:4_190:}哪有俺吃瓜安逸{:4_174:}

红影 发表于 2024-2-2 15:16

这个巡回执行多少次的设定,是为了后面真正理解歌词同步的运作而举的例子吧{:4_204:}

醉美水芙蓉 发表于 2024-2-2 17:23

马黑黑 发表于 2024-2-2 17:45

醉美水芙蓉 发表于 2024-2-2 17:23
试了下!黑黑老师厉害!又出新效果了!

感谢支持

马黑黑 发表于 2024-2-2 17:45

红影 发表于 2024-2-2 15:13
去运行了。这个歌词还带下面的线条呢,漂亮

上一讲就有下划线了

马黑黑 发表于 2024-2-2 17:46

樵歌 发表于 2024-2-2 15:15
辛苦了不少脑细胞哪有俺吃瓜安逸

{:4_172:}

马黑黑 发表于 2024-2-2 17:46

红影 发表于 2024-2-2 15:16
这个巡回执行多少次的设定,是为了后面真正理解歌词同步的运作而举的例子吧

没有,这是随意的,只是让两个关键帧动画能来回切换就好

红影 发表于 2024-2-2 19:49

马黑黑 发表于 2024-2-2 17:45
上一讲就有下划线了

这个是动态的啊,更漂亮{:4_187:}

红影 发表于 2024-2-2 19:50

马黑黑 发表于 2024-2-2 17:46
没有,这是随意的,只是让两个关键帧动画能来回切换就好

这样的设置有趣,还是头一次见到呢,习惯了歌词同步里你的代码,从没深想过时怎么弄出来的{:4_199:}

樵歌 发表于 2024-2-2 20:17

马黑黑 发表于 2024-2-2 17:46


只可惜木人陪俺吃{:4_189:}

马黑黑 发表于 2024-2-2 20:18

樵歌 发表于 2024-2-2 20:17
只可惜木人陪俺吃

木人也挺好,比石头人温暖{:4_170:}

马黑黑 发表于 2024-2-2 20:25

红影 发表于 2024-2-2 19:50
这样的设置有趣,还是头一次见到呢,习惯了歌词同步里你的代码,从没深想过时怎么弄出来的

里面充满着编程思想

马黑黑 发表于 2024-2-2 20:26

红影 发表于 2024-2-2 19:49
这个是动态的啊,更漂亮
这一节课就是说动画

红影 发表于 2024-2-2 22:24

马黑黑 发表于 2024-2-2 20:25
里面充满着编程思想

太厉害了{:4_199:}

马黑黑 发表于 2024-2-2 22:25

红影 发表于 2024-2-2 22:24
太厉害了

也没啥,就是找到实现思路、有实现方法

红影 发表于 2024-2-2 22:26

马黑黑 发表于 2024-2-2 20:26
这一节课就是说动画

那个下划线也是动画里更好看{:4_187:}

马黑黑 发表于 2024-2-2 22:27

红影 发表于 2024-2-2 22:26
那个下划线也是动画里更好看

它是伪元素的边框,伪元素从短变长,所以,你说呢

红影 发表于 2024-2-2 23:16

马黑黑 发表于 2024-2-2 22:25
也没啥,就是找到实现思路、有实现方法

这个不是一般的人能弄得出的{:4_187:}
页: [1] 2 3
查看完整版本: range进度条播放器+LRC歌词同步教程(二)