马黑黑 发表于 2024-6-13 12:51

使用getAnimations()方法控制CSS动画

<style>
.pa p { font: normal 18px/24px sans-serif; margin: 12px 0; }
.pa mark { padding: 2px 6px; background: lightblue; }
.tz { margin: 20px auto; width: 760px; height: 400px; border: 1px solid gray; position: relative; }
.pic { position: absolute; left: 20px; top: 20px; animation: rot 4s linear infinite; }
.pic:nth-of-type(1) { left: 100px; top: 10px; }
.pic:nth-of-type(2) { left: 460px; top: 10px; }
.pic:nth-of-type(3) { left: 320px; top: 200px; }
.tMid { text-align: center; }
@keyframes rot {
        to { transform: rotate(360deg); }
}
.mum { position: relative; margin: 12px 0; padding: 10px; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; color: black; background: rgba(240, 240, 240,.95); box-shadow: 2px 2px 4px gray; border: thin solid lightblue; border-radius: 6px; }
.mum ::selection { background-color: rgba(0,100,100,.35); }
.mum div { margin: 0; padding: 0; }
.mum cl-cd { display: block; position: relative; margin: 0 0 0 50px; padding: 0 0 0 10px; white-space: pre-wrap; overflow-wrap: break-word; border-left: 1px solid silver; }
.mum cl-cd::before { position: absolute; content: attr(data-idx); width: 50px; color: gray; text-align: right; transform: translate(-70px); }
.tRed { color: red; }
.tBlue { color: blue; }
.tGreen { color: green; }
.tDarkRed { color: darkred; }
.tMagenta { color: magenta; }
</style>

<div class="pa">
        <p>如下效果是纯CSS关键帧动画,请特别注意,未经干预前动画由CSS直接驱动,三朵花的旋转无限循环:</p>
        <div class="tz" id="tz0">
                <img class="pic" alt="" src="https://638183.freep.cn/638183/t23/btn/12f.png" />
                <img class="pic" alt="" src="https://638183.freep.cn/638183/t23/btn/12f.png" />
                <img class="pic" alt="" src="https://638183.freep.cn/638183/t23/btn/12f.png" />
        </div>
        <p class="tMid">
                <button id="onebyone" type="button" value="1">接力旋转</button>
                <button id="restore" type="button" value="2" disabled>恢复原状</button>
        </p>
        <p>点击接力旋转按钮,其中一朵花朵继续旋转,另外两朵花暂停,旋转着的花朵停下,下一朵花紧接着旋转,如此循环往复。点击恢复原状按钮,结束接力赛旋转,三朵花的旋转状态恢复到初始时的样子。</p>
        <p>接力运行动画的实现得益于 getAnimations() 方法,该方法由 Web Animations API(简称WAAPI) 提供,我们之前介绍的原生 JS animate() 方法也是由 WAAPI 提供。</p>
        <p>getAnimations() 方法可以基于 document 即 DOM,<mark>document.getAnimations();</mark>,这将拿到整个页面的所有动画,包含三种类型的动画:① CSS动画(CSS Animations),② CSS过度动画(CSS Transitions),③ Web动画(Web Animations,即之前介绍的原生JS Animate动画),数据以数组形式存储;也可以基于 element 即某个或某些特定的HTML元素,拿到的也是数组数据。利用这些数据,我们可以相应地管理各类WAAPI所能管理的动画,本文重点讨论的动画类型是CSS动画——如前已述,接力旋转按钮点击后的效果就是通过基于 document 的 getAnimations() 方法实现的。</p>
        <p>考虑一下下面的CSS样式表:</p>
        <div class='mum'>
<cl-cd data-idx="1">&lt;<span class="tDarkRed">style</span>&gt;</cl-cd>
<cl-cd data-idx="2">.pic { <span class="tBlue">position:</span> absolute; <span class="tBlue">left:</span> 20px; <span class="tBlue">top:</span> 20px; <span class="tBlue">animation:</span> rot 4s linear infinite; }</cl-cd>
<cl-cd data-idx="3">@keyframes rot {</cl-cd>
<cl-cd data-idx="4">    to { <span class="tBlue">transform:</span> rotate(360deg); }</cl-cd>
<cl-cd data-idx="5">}</cl-cd>
<cl-cd data-idx="6">&lt;<span class="tDarkRed">/style</span>&gt;</cl-cd>
        </div>

        <p>留意第2行代码,简写形式的 animation 属性中有一个参数 infinite,它是 animation-iteration-count 的值,指动画运行次数,infinite 是无限循环运行。无限循环运行的动画 onfinish 事件(动画完成事件)永远不会被触发,因此需要动态修改动画运行次数,比如改为运行一次。控制CSS关键帧动画可以通过修改元素级CSS样式来处理,我们会拿到要控制动画的所有元素,在JS中这么处理就可以达到目的:</p>
        <div class='mum'><cl-cd data-idx="1">element.style.<span class="tRed">animationIterationCount</span> = 1;</cl-cd></div>
        <p>当然,如果CSS样式表中直接在简写属性的 animation 值中去掉 infinite,那一切将很简单,我们只需简单地通过 getAnimations() 方法获取所有的动画然后控制这些动画就好。或者,不在CSS样式表中修改动画运行次数,那能否通过拿到的动画操作句柄,使用 WAAPI 的方式去修改动画运行次数呢?针对CSS动画目前不行,<mark>动画对象.iterations = 1;</mark> 这样的设置目前仅能作用于由 JS 的 animate() 方法生成的动画。</p>
        <p>修改动画运行次数为1次之后,接着就可以真奔主题,让CSS关键帧动画接力运行。我们要做的仅仅是封装 <mark>动画对象.onfinish</mark> 事件:让前一个动画运行结束时开启下一个动画的运行,并且令其头尾衔接。以下是完整的实现代码,其运行结果是动画一开始就进入接力状态,且每一朵花都具备接受点击功能,通过点击任意一朵花,动画都会在播放、暂停两种状态间切换:</p>
<div class='mum'>
<cl-cd data-idx="1">&lt;<span class="tDarkRed">style</span>&gt;</cl-cd>
<cl-cd data-idx="2">.tz { <span class="tBlue">margin:</span> 20px auto; <span class="tBlue">width:</span> 760px; <span class="tBlue">height:</span> 400px; <span class="tBlue">border:</span> 1px solid gray; <span class="tBlue">position:</span> relative; }</cl-cd>
<cl-cd data-idx="3">.pic { <span class="tBlue">position:</span> absolute; <span class="tBlue">left:</span> 20px; <span class="tBlue">top:</span> 20px; <span class="tBlue">animation:</span> rot 4s linear infinite; }</cl-cd>
<cl-cd data-idx="4">.<span class="tBlue">pic:</span>nth-of-type(1) { <span class="tBlue">left:</span> 120px; <span class="tBlue">top:</span> 30px; }</cl-cd>
<cl-cd data-idx="5">.<span class="tBlue">pic:</span>nth-of-type(2) { <span class="tBlue">left:</span> 460px; <span class="tBlue">top:</span> 10px; }</cl-cd>
<cl-cd data-idx="6">.<span class="tBlue">pic:</span>nth-of-type(3) { <span class="tBlue">left:</span> 320px; <span class="tBlue">top:</span> 200px; }</cl-cd>
<cl-cd data-idx="7">@keyframes rot {</cl-cd>
<cl-cd data-idx="8">    to { <span class="tBlue">transform:</span> rotate(360deg); }</cl-cd>
<cl-cd data-idx="9">}</cl-cd>
<cl-cd data-idx="10">&lt;<span class="tDarkRed">/style</span>&gt;</cl-cd>
<cl-cd data-idx="11"> </cl-cd>
<cl-cd data-idx="12">&lt;<span class="tDarkRed">div</span> <span class="tRed">id</span>=<span class="tMagenta">"tz"</span>&gt;</cl-cd>
<cl-cd data-idx="13">    &lt;<span class="tDarkRed">img</span> class=<span class="tMagenta">"pic"</span> alt=<span class="tMagenta">""</span> src=<span class="tMagenta">"https://638183.freep.cn/638183/t23/btn/12f.png"</span> /&gt;</cl-cd>
<cl-cd data-idx="14">    &lt;<span class="tDarkRed">img</span> class=<span class="tMagenta">"pic"</span> alt=<span class="tMagenta">""</span> src=<span class="tMagenta">"https://638183.freep.cn/638183/t23/btn/12f.png"</span> /&gt;</cl-cd>
<cl-cd data-idx="15">    &lt;<span class="tDarkRed">img</span> class=<span class="tMagenta">"pic"</span> alt=<span class="tMagenta">""</span> src=<span class="tMagenta">"https://638183.freep.cn/638183/t23/btn/12f.png"</span> /&gt;</cl-cd>
<cl-cd data-idx="16">&lt;<span class="tDarkRed">/div</span>&gt;</cl-cd>
<cl-cd data-idx="17">&nbsp;</cl-cd>
<cl-cd data-idx="18">&lt;<span class="tDarkRed">script</span>&gt;</cl-cd>
<cl-cd data-idx="19"><span class="tBlue">const</span> pics = <span class="tRed">document</span>.querySelectorAll(<span class="tMagenta">'.pic'</span>); <span class="tGreen">//所有运行动画的元素</span></cl-cd>
<cl-cd data-idx="20"><span class="tBlue">const</span> anis = <span class="tRed">document</span>.getAnimations(); <span class="tGreen">//所有动画对象</span></cl-cd>
<cl-cd data-idx="21"><span class="tBlue">var</span> isplaying = true, current = 0; <span class="tGreen">//播放状态、当前播放的动画索引</span></cl-cd>
<cl-cd data-idx="22">&nbsp;</cl-cd>
<cl-cd data-idx="23"><span class="tGreen">//遍历动画对象</span></cl-cd>
<cl-cd data-idx="24">anis.forEach((ani,key) =&gt; {</cl-cd>
<cl-cd data-idx="25">&nbsp; &nbsp; pics.style.animationIterationCount = 1; <span class="tGreen">//改变元素的动画运行次数</span></cl-cd>
<cl-cd data-idx="26">&nbsp; &nbsp; <span class="tBlue">if</span>(key &gt; 0) ani.pause(); <span class="tGreen">//除了第一个动画,都先暂停</span></cl-cd>
<cl-cd data-idx="27">    <span class="tGreen">//如果不是最后一个动画对象,它运行结束时</span></cl-cd>
<cl-cd data-idx="28">&nbsp; &nbsp; <span class="tBlue">if</span>(key &lt; anis.length - 1) ani.onfinish = () =&gt; {</cl-cd>
<cl-cd data-idx="29">&nbsp; &nbsp; &nbsp; &nbsp; anis.play(); <span class="tGreen">//启动下一个动画</span></cl-cd>
<cl-cd data-idx="30">&nbsp; &nbsp; &nbsp; &nbsp; current = key + 1; <span class="tGreen">//当前动画索引加 1</span></cl-cd>
<cl-cd data-idx="31">&nbsp; &nbsp; }</cl-cd>
<cl-cd data-idx="32">    <span class="tGreen">//否则如果是最后一个动画,它运行结束时</span></cl-cd>
<cl-cd data-idx="33">&nbsp; &nbsp; <span class="tBlue">else</span> ani.onfinish = () =&gt; {</cl-cd>
<cl-cd data-idx="34">&nbsp; &nbsp; &nbsp; &nbsp; anis.play(); <span class="tGreen">//启动第一个动画</span></cl-cd>
<cl-cd data-idx="35">      current = 0; <span class="tGreen">//当前动画索引是 0</span></cl-cd>
<cl-cd data-idx="36">&nbsp; &nbsp; }</cl-cd>
<cl-cd data-idx="37">});</cl-cd>
<cl-cd data-idx="38">&nbsp;</cl-cd>
<cl-cd data-idx="39"><span class="tGreen">//花朵点击事件</span></cl-cd>
<cl-cd data-idx="40">pics.forEach((pic, key) =&gt; pic.onclick = () =&gt; {</cl-cd>
<cl-cd data-idx="41">    <span class="tGreen">//如果动画正在运行,则令当前运行的动画对象暂停,反之,如果动画暂停中,则令当前动画对象播放</span></cl-cd>
<cl-cd data-idx="42">&nbsp; &nbsp; isplaying ? anis.pause() : anis.play();</cl-cd>
<cl-cd data-idx="43">&nbsp; &nbsp; isplaying = !isplaying; <span class="tGreen">//布尔变量值取反</span></cl-cd>
<cl-cd data-idx="44">});</cl-cd>
<cl-cd data-idx="45">&lt;<span class="tDarkRed">/script</span>&gt;</cl-cd>
        </div>
        <p>以上代码可以复制到 <a href="http://mhh.52qingyin.cn/api/pcode/" target="_blank">pencil code</a> 或存为本地HTML文档运行以查看运行效果及进行相关研究。</p>
</div>

<script>
var pics = tz0.querySelectorAll('.pic');
var anis = document.getAnimations();

var rot1by1 = (flag) => {
        if(flag) {
                pics.forEach((pic,key) => {
                        pic.style.animationIterationCount = 1;
                        key > 0 ? anis.pause() : anis.play();
                        var idx = key < pics.length - 1 ? key + 1 : 0;
                        anis.onfinish = () => anis.play();
                });
        }else{
                pics.forEach((pic,key) => {
                        pic.style.animationIterationCount = 'infinite';
                        anis.play();
                });
        }
};

onebyone.onclick = () => {
        rot1by1(true);
        restore.disabled = false;
        onebyone.disabled = true;
};

restore.onclick = () => {
        rot1by1(false);
        restore.disabled = true;
        onebyone.disabled = false;
}
</script>

马黑黑 发表于 2024-6-13 12:53

综合运用CSS自身属性+JS基于style或attribute属性的操作,也能有效控制CSS关键帧动画,不过操作能力没有借助WAPPI那么强大

南无月 发表于 2024-6-13 13:00

每天都有好玩颜值又高的动画效果出炉。。。
老师的教程看得赏心悦目,刚才玩了好大一会儿。。{:4_170:}

南无月 发表于 2024-6-13 13:02

接力旋转特别好玩,每个小花转4秒,下一个接上。。
最喜欢这种直观演示的小程序。。。
多种情况可以选择。。。
代码比提供出来的复杂好几倍的样纸。。{:4_199:}

南无月 发表于 2024-6-13 13:03

或者,不在CSS样式表中修改动画运行次数,那能否通过拿到的动画操作句柄,使用 WAAPI 的方式去修改动画运行次数呢?针对CSS动画目前不行,动画对象.iterations = 1; 这样的设置目前仅能作用于由 JS 的 animate() 方法生成的动画。

这句最高深了。。{:4_170:}

马黑黑 发表于 2024-6-13 13:05

南无月 发表于 2024-6-13 13:03
或者,不在CSS样式表中修改动画运行次数,那能否通过拿到的动画操作句柄,使用 WAAPI 的方式去修改动画运行 ...

这个的基础在前面的三个讲义里,js原生animate动画

马黑黑 发表于 2024-6-13 13:06

南无月 发表于 2024-6-13 13:02
接力旋转特别好玩,每个小花转4秒,下一个接上。。
最喜欢这种直观演示的小程序。。。
多种情况可以选择 ...

代码自身并不复杂,复杂的是,一没有能领悟JS的诸多内置指令,二没有能理解高于幼儿园大班毕业水平的算法

马黑黑 发表于 2024-6-13 13:07

南无月 发表于 2024-6-13 13:00
每天都有好玩颜值又高的动画效果出炉。。。
老师的教程看得赏心悦目,刚才玩了好大一会儿。。

这个实现起来容易,写成文章有点费脑

红影 发表于 2024-6-13 14:14

去试了,每一朵花儿被点击都能让正在转动的暂停,这个不影响次序的依次轮换{:4_187:}

红影 发表于 2024-6-13 14:14

这个讲解很清楚,黑黑辛苦了{:4_187:}

南无月 发表于 2024-6-13 17:39

马黑黑 发表于 2024-6-13 13:05
这个的基础在前面的三个讲义里,js原生animate动画

{:4_181:}好哒,我回去再看看之前教程帮助理解

小辣椒 发表于 2024-6-13 17:41

黑黑的新产品,支持!{:4_187:}

南无月 发表于 2024-6-13 17:41

马黑黑 发表于 2024-6-13 13:06
代码自身并不复杂,复杂的是,一没有能领悟JS的诸多内置指令,二没有能理解高于幼儿园大班毕业水平的算法
{:4_170:}老师你说得都对。。都是小白的问题

南无月 发表于 2024-6-13 17:44

马黑黑 发表于 2024-6-13 13:07
这个实现起来容易,写成文章有点费脑

老师把知识点都掰开揉碎写出来~还写这么详细的标注。。。老师的教程应是全网最好懂的教程~~{:4_190:}{:4_191:}

马黑黑 发表于 2024-6-13 17:45

南无月 发表于 2024-6-13 17:44
老师把知识点都掰开揉碎写出来~还写这么详细的标注。。。老师的教程应是全网最好懂的教程~~{:4_ ...

那也不一定:有时候我回头看看写过的东东,会看不懂我到底写的啥

马黑黑 发表于 2024-6-13 17:45

南无月 发表于 2024-6-13 17:41
老师你说得都对。。都是小白的问题

{:4_190:}

马黑黑 发表于 2024-6-13 17:45

小辣椒 发表于 2024-6-13 17:41
黑黑的新产品,支持!

没有,这是烟酒烟酒而已

马黑黑 发表于 2024-6-13 17:46

红影 发表于 2024-6-13 14:14
这个讲解很清楚,黑黑辛苦了

手掌辛苦

马黑黑 发表于 2024-6-13 17:46

红影 发表于 2024-6-13 14:14
去试了,每一朵花儿被点击都能让正在转动的暂停,这个不影响次序的依次轮换

{:4_190:}

红影 发表于 2024-6-13 19:28

马黑黑 发表于 2024-6-13 17:46
手掌辛苦

脚掌也辛苦{:4_173:}
页: [1] 2 3 4 5
查看完整版本: 使用getAnimations()方法控制CSS动画