魂引
<style>#mydiv { --width: 1024px; margin: 100px 0 30px calc(50% - (var(--width) / 2 + 81px)); width: var(--width); height: 640px; background: #eee url('https://638183.freep.cn/638183/t24/webp/hyyb.webp') no-repeat center/cover; box-shadow: 2px 2px 8px rgba(0,0,0,.7); overflow: hidden; z-index: 1; position: relative;}
.ball { position: absolute; width: 40px; height: 40px; background: linear-gradient(plum, teal); border-radius: 50%; opacity: .95; transition: width .5s, height .5s, opacity .5s; cursor: pointer; offset-path: path('M40 20 Q512 1080,984 20'); offset-distance: 0; animation: 6s linear forwards;}
.ball:hover { width: 45px; height: 45px; opacity: .75; }
.ball:nth-of-type(1) { left: -20px; top: 0; animation-name: move1-1;}
.ball:nth-of-type(2) { left: 20px; top: 0; background: linear-gradient(tan, cyan); offset-distance: 50%;}
#vid { position: absolute; width: 80%; height: 50%; transform: scale(-1,1) rotate(30deg); object-fit: cover; mix-blend-mode: multiply; pointer-events: none; }
@keyframes move1-1 {
from { offset-distance: 0%; transform: rotate(720deg); filter: hue-rotate(0); }
to { offset-distance: 50%; transform: rotate(0); filter: hue-rotate(360deg); }
}
@keyframes move1-2 {
from { offset-distance: 50%; transform: rotate(0); filter: hue-rotate(360deg); }
to { offset-distance: 0%; transform: rotate(720deg); filter: hue-rotate(0); }
}
@keyframes move2-1 {
from { offset-distance: 50%; transform: rotate(720deg); filter: hue-rotate(0deg); }
to { offset-distance: 100%; transform: rotate(0); filter: hue-rotate(360deg); }
}
@keyframes move2-2 {
from { offset-distance: 100%; transform: rotate(0); filter: hue-rotate(360deg); }
to { offset-distance: 50%; transform: rotate(720deg); filter: hue-rotate(0); }
}
</style>
<div id="mydiv">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=29999267" autoplay loop></audio>
<video id="vid" src="https://img.tukuppt.com/video_show/2269348/00/17/44/5ec494a494d82.mp4" autoplay loop muted></video>
<div id="b1" class="ball"></div>
<div id="b2" class="ball"></div>
</div>
<script>
const balls = ;
b1.onanimationend = ()=> {
let ani = window.getComputedStyle(b1).getPropertyValue('animation-name');
if(ani === 'move1-1') {
b1.style.animationName = '';
b2.style.animationName = 'move2-1';
}else{
b1.style.animationName = 'move1-1';
}
};
b2.onanimationend = () => {
let ani = window.getComputedStyle(b2).getPropertyValue('animation-name');
if(ani === 'move2-1') {
b2.style.animationName = 'move2-2';
}else{
b2.style.animationName = '';
b1.style.animationName = 'move1-2';
}
};
var mState = () => {
balls.forEach(b => {
b.style.animationPlayState = aud.paused ? 'paused' : 'running';
b.title = ['暂停','播放'][+aud.paused];
});
aud.paused ? vid.pause() : vid.play();
};
aud.oncanplay = aud.onplaying = aud.onpause = () => mState();
balls.forEach(b => b.onclick = () => aud.paused ? aud.play() : aud.pause());
var sF = document.createElement('script');
sF.src = 'https://638183.freep.cn/638183/web/api/fullscreen.js';
sF.charset = 'utf-8';
document.querySelector('body').appendChild(sF);
sF.onload = () => FS({
papa: '#mydiv',
css: 'bottom: 10px; left: 50%; transform: translate(-50%); --color: snow; --fsBg: transparent;',
});
window.onresize = () => balls.forEach(b => {
b.style.offsetPath = `path('M40 20 Q${mydiv.offsetWidth / 2} ${mydiv.offsetHeight * 2 - 200}, ${mydiv.offsetWidth - 40} 20')`;
b.style.animationDuration = ((mydiv.offsetWidth + mydiv.offsetHeight) / (1024 + 640)) * 6 + 's';
});
</script>
<style>
.mum { position: relative; margin: 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: thick groove 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>
<h2>代码:</h2>
<div class='mum'>
<cl-cd data-idx="1"><<span class="tDarkRed">style</span>></cl-cd>
<cl-cd data-idx="2">#mydiv { <span class="tBlue">--width:</span> 1024px; <span class="tBlue">margin:</span> 30px 0 30px calc(50% - (<span class="tBlue">var</span>(--width) / 2 + 81px)); <span class="tBlue">width:</span> <span class="tBlue">var</span>(--width); <span class="tBlue">height:</span> 640px; <span class="tBlue">background:</span> #eee url(<span class="tMagenta">'https://638183.freep.cn/638183/t24/webp/hyyb.webp'</span>) no-repeat center/cover; <span class="tBlue">box-shadow:</span> 2px 2px 8px rgba(0,0,0,.7); <span class="tBlue">overflow:</span> hidden; <span class="tBlue">z-index:</span> 1; <span class="tBlue">position:</span> relative;}</cl-cd>
<cl-cd data-idx="3">.ball { <span class="tBlue">position:</span> absolute; <span class="tBlue">width:</span> 40px; <span class="tBlue">height:</span> 40px; <span class="tBlue">background:</span> linear-gradient(plum, teal); <span class="tBlue">border-radius:</span> 50%; <span class="tBlue">opacity:</span> .95; <span class="tBlue">transition:</span> width .5s, height .5s, opacity .5s; <span class="tBlue">cursor:</span> pointer; <span class="tBlue">offset-path:</span> path(<span class="tMagenta">'M40 20 Q512 1080,984 20'</span>); <span class="tBlue">offset-distance:</span> 0; <span class="tBlue">animation:</span> 6s linear forwards;}</cl-cd>
<cl-cd data-idx="4">.<span class="tBlue">ball:</span>hover { <span class="tBlue">width:</span> 45px; <span class="tBlue">height:</span> 45px; <span class="tBlue">opacity:</span> .75; }</cl-cd>
<cl-cd data-idx="5">.<span class="tBlue">ball:</span>nth-of-type(1) { <span class="tBlue">left:</span> -20px; <span class="tBlue">top:</span> 0; <span class="tBlue">animation-name:</span> move1-1;}</cl-cd>
<cl-cd data-idx="6">.<span class="tBlue">ball:</span>nth-of-type(2) { <span class="tBlue">left:</span> 20px; <span class="tBlue">top:</span> 0; <span class="tBlue">background:</span> linear-gradient(tan, cyan); <span class="tBlue">offset-distance:</span> 50%;}</cl-cd>
<cl-cd data-idx="7">#vid { <span class="tBlue">position:</span> absolute; <span class="tBlue">width:</span> 80%; <span class="tBlue">height:</span> 50%; <span class="tBlue">transform:</span> scale(-1,1) rotate(30deg); <span class="tBlue">object-fit:</span> cover; <span class="tBlue">mix-blend-mode:</span> multiply; <span class="tBlue">pointer-events:</span> none; }</cl-cd>
<cl-cd data-idx="8">@keyframes move1-1 {</cl-cd>
<cl-cd data-idx="9"> from { <span class="tBlue">offset-distance:</span> 0%; <span class="tBlue">transform:</span> rotate(720deg); <span class="tBlue">filter:</span> hue-rotate(0); }</cl-cd>
<cl-cd data-idx="10"> to { <span class="tBlue">offset-distance:</span> 50%; <span class="tBlue">transform:</span> rotate(0); <span class="tBlue">filter:</span> hue-rotate(360deg); }</cl-cd>
<cl-cd data-idx="11">}</cl-cd>
<cl-cd data-idx="12">@keyframes move1-2 {</cl-cd>
<cl-cd data-idx="13"> from { <span class="tBlue">offset-distance:</span> 50%; <span class="tBlue">transform:</span> rotate(0); <span class="tBlue">filter:</span> hue-rotate(360deg); }</cl-cd>
<cl-cd data-idx="14"> to { <span class="tBlue">offset-distance:</span> 0%; <span class="tBlue">transform:</span> rotate(720deg); <span class="tBlue">filter:</span> hue-rotate(0); }</cl-cd>
<cl-cd data-idx="15">}</cl-cd>
<cl-cd data-idx="16">@keyframes move2-1 {</cl-cd>
<cl-cd data-idx="17"> from { <span class="tBlue">offset-distance:</span> 50%; <span class="tBlue">transform:</span> rotate(720deg); <span class="tBlue">filter:</span> hue-rotate(0deg); }</cl-cd>
<cl-cd data-idx="18"> to { <span class="tBlue">offset-distance:</span> 100%; <span class="tBlue">transform:</span> rotate(0); <span class="tBlue">filter:</span> hue-rotate(360deg); }</cl-cd>
<cl-cd data-idx="19">}</cl-cd>
<cl-cd data-idx="20">@keyframes move2-2 {</cl-cd>
<cl-cd data-idx="21"> from { <span class="tBlue">offset-distance:</span> 100%; <span class="tBlue">transform:</span> rotate(0); <span class="tBlue">filter:</span> hue-rotate(360deg); }</cl-cd>
<cl-cd data-idx="22"> to { <span class="tBlue">offset-distance:</span> 50%; <span class="tBlue">transform:</span> rotate(720deg); <span class="tBlue">filter:</span> hue-rotate(0); }</cl-cd>
<cl-cd data-idx="23">}</cl-cd>
<cl-cd data-idx="24"><<span class="tDarkRed">/style</span>></cl-cd>
<cl-cd data-idx="25"> </cl-cd>
<cl-cd data-idx="26"><<span class="tDarkRed">div</span> <span class="tRed">id</span>=<span class="tMagenta">"mydiv"</span>></cl-cd>
<cl-cd data-idx="27"> <<span class="tDarkRed">audio</span> <span class="tRed">id</span>=<span class="tMagenta">"aud"</span> src=<span class="tMagenta">"https://music.163.com/song/media/outer/url?<span class="tRed">id</span>=29999267"</span> autoplay loop><<span class="tDarkRed">/audio</span>></cl-cd>
<cl-cd data-idx="28"> <<span class="tDarkRed">video</span> <span class="tRed">id</span>=<span class="tMagenta">"vid"</span> src=<span class="tMagenta">"https://img.tukuppt.com/video_show/2269348/00/17/44/5ec494a494d82.mp4"</span> autoplay loop muted><<span class="tDarkRed">/video</span>></cl-cd>
<cl-cd data-idx="29"> <<span class="tDarkRed">div</span> <span class="tRed">id</span>=<span class="tMagenta">"b1"</span> class=<span class="tMagenta">"ball"</span>><<span class="tDarkRed">/div</span>></cl-cd>
<cl-cd data-idx="30"> <<span class="tDarkRed">div</span> <span class="tRed">id</span>=<span class="tMagenta">"b2"</span> class=<span class="tMagenta">"ball"</span>><<span class="tDarkRed">/div</span>></cl-cd>
<cl-cd data-idx="31"><<span class="tDarkRed">/div</span>></cl-cd>
<cl-cd data-idx="32"> </cl-cd>
<cl-cd data-idx="33"><<span class="tDarkRed">script</span>></cl-cd>
<cl-cd data-idx="34"><span class="tGreen">//小球数组</span></cl-cd>
<cl-cd data-idx="35"><span class="tBlue">const</span> balls = ;</cl-cd>
<cl-cd data-idx="36"><span class="tGreen">//小球1动画结束</span></cl-cd>
<cl-cd data-idx="37">b1.onanimationend = ()=> {</cl-cd>
<cl-cd data-idx="38"> <span class="tBlue">let</span> ani = <span class="tRed">window</span>.getComputedStyle(b1).getPropertyValue(<span class="tMagenta">'animation-name'</span>);</cl-cd>
<cl-cd data-idx="39"> <span class="tBlue">if</span>(ani === <span class="tMagenta">'move1-1'</span>) {</cl-cd>
<cl-cd data-idx="40"> b1.style.animationName = <span class="tMagenta">''</span>;</cl-cd>
<cl-cd data-idx="41"> b2.style.animationName = <span class="tMagenta">'move2-1'</span>;</cl-cd>
<cl-cd data-idx="42"> }<span class="tBlue">else</span>{</cl-cd>
<cl-cd data-idx="43"> b1.style.animationName = <span class="tMagenta">'move1-1'</span>;</cl-cd>
<cl-cd data-idx="44"> }</cl-cd>
<cl-cd data-idx="45">};</cl-cd>
<cl-cd data-idx="46"><span class="tGreen">//小球2动画结束</span></cl-cd>
<cl-cd data-idx="47">b2.onanimationend = () => {</cl-cd>
<cl-cd data-idx="48"> <span class="tBlue">let</span> ani = <span class="tRed">window</span>.getComputedStyle(b2).getPropertyValue(<span class="tMagenta">'animation-name'</span>);</cl-cd>
<cl-cd data-idx="49"> <span class="tBlue">if</span>(ani === <span class="tMagenta">'move2-1'</span>) {</cl-cd>
<cl-cd data-idx="50"> b2.style.animationName = <span class="tMagenta">'move2-2'</span>;</cl-cd>
<cl-cd data-idx="51"> }<span class="tBlue">else</span>{</cl-cd>
<cl-cd data-idx="52"> b2.style.animationName = <span class="tMagenta">''</span>;</cl-cd>
<cl-cd data-idx="53"> b1.style.animationName = <span class="tMagenta">'move1-2'</span>;</cl-cd>
<cl-cd data-idx="54"> }</cl-cd>
<cl-cd data-idx="55">};</cl-cd>
<cl-cd data-idx="56"><span class="tGreen">//联动控制函数</span></cl-cd>
<cl-cd data-idx="57"><span class="tBlue">var</span> mState = () => {</cl-cd>
<cl-cd data-idx="58"> balls.forEach(b => {</cl-cd>
<cl-cd data-idx="59"> b.style.animationPlayState = aud.paused ? <span class="tMagenta">'paused'</span> : <span class="tMagenta">'running'</span>;</cl-cd>
<cl-cd data-idx="60"> b.title = [<span class="tMagenta">'暂停'</span>,<span class="tMagenta">'播放'</span>][+aud.paused];</cl-cd>
<cl-cd data-idx="61"> });</cl-cd>
<cl-cd data-idx="62"> aud.paused ? vid.pause() : vid.play();</cl-cd>
<cl-cd data-idx="63">};</cl-cd>
<cl-cd data-idx="64"><span class="tGreen">//音频控件监听事件</span></cl-cd>
<cl-cd data-idx="65">aud.oncanplay = aud.onplaying = aud.onpause = () => mState();</cl-cd>
<cl-cd data-idx="66"><span class="tGreen">//小球点击事件</span></cl-cd>
<cl-cd data-idx="67">balls.forEach(b => b.onclick = () => aud.paused ? aud.play() : aud.pause());</cl-cd>
<cl-cd data-idx="68"><span class="tGreen">//加载全屏插件</span></cl-cd>
<cl-cd data-idx="69"><span class="tBlue">var</span> sF = <span class="tRed">document</span>.createElement(<span class="tMagenta">'script'</span>);</cl-cd>
<cl-cd data-idx="70">sF.src = <span class="tMagenta">'https://638183.freep.cn/638183/web/api/fullscreen.js'</span>;</cl-cd>
<cl-cd data-idx="71">sF.charset = <span class="tMagenta">'utf-8'</span>;</cl-cd>
<cl-cd data-idx="72"><span class="tRed">document</span>.querySelector(<span class="tMagenta">'body'</span>).appendChild(sF);</cl-cd>
<cl-cd data-idx="73">sF.onload = () => FS({</cl-cd>
<cl-cd data-idx="74"> <span class="tBlue">papa:</span> <span class="tMagenta">'#mydiv'</span>,</cl-cd>
<cl-cd data-idx="75"> <span class="tBlue">css:</span> <span class="tMagenta">'<span class="tBlue">bottom:</span> 10px; <span class="tBlue">left:</span> 50%; <span class="tBlue">transform:</span> translate(-50%); <span class="tBlue">--color:</span> snow; <span class="tBlue">--fsBg:</span> transparent;'</span>,</cl-cd>
<cl-cd data-idx="76">});</cl-cd>
<cl-cd data-idx="77"><span class="tGreen">//动画响应窗体变化</span></cl-cd>
<cl-cd data-idx="78"><span class="tRed">window</span>.onresize = () => balls.forEach(b => {</cl-cd>
<cl-cd data-idx="79"> b.style.offsetPath = `path(<span class="tMagenta">'M40 20 Q${mydiv.offsetWidth / 2} ${mydiv.offsetHeight * 2 - 200}, ${mydiv.offsetWidth - 40} 20'</span>)`;</cl-cd>
<cl-cd data-idx="80"> b.style.animationDuration = ((mydiv.offsetWidth + mydiv.offsetHeight) / (1024 + 640)) * 6 + <span class="tMagenta">'s'</span>;</cl-cd>
<cl-cd data-idx="81">});</cl-cd>
<cl-cd data-idx="82"><<span class="tDarkRed">/script</span>></cl-cd>
</div>
本帖最后由 马黑黑 于 2024-6-28 19:28 编辑
本帖:
(一)利用元素 animationend 事件实现对小播CSS关键帧动画进行管理,得出一静一动的动画效果。原理请查阅:
CSS关键帧动画之圆周运动演示 - 马黑黑教程专版 - 花潮论坛 - Powered by Discuz! (huachaowang.com)
不同的是,这里的动画使用相抵路径(offset-path)实现,通过 @keyframes 创建四个不同方向的运行路线(都在路径上,两两衔接)。元素在路径上的运行距离以 offset-distance 表示,支持百分比,路径变长变短均能适应。
动画除了路径动画,还有旋转与色相变化,均在每一个 @keyframes 属性中一并设定。
(二)关于小球:JS代码中,需要一个数组管理小球。该数组会在联动控制函数和小球的点击事件中用上。
(三)关于相抵路径、动画运行周期时长适应全屏或窗体其他尺寸动态变化
由于相抵路径的 path 的数据是像素单位,视口尺寸变化时只能动态调整。window.onresize 事件中,以比例方法对二次贝塞尔曲线的终点和控制点进行测算,终点X坐标 = mydiv.offsetWidth - 40,40是小球的尺寸要减去;控制点X坐标 = mydiv.offsetWidth / 2;Y坐标 = mydiv.offsetHeight * 2 - 200,参照了原始相抵路径的设计;animation-duration 即运行周期时长的计算复杂一些:
((mydiv.offsetWidth + mydiv.offsetHeight) / (1024 + 640)) * 6
这是采用比例方法设计的算式:((宽+高)÷(原始宽 +原始高))× 6,6 是原生动画在 1024*640尺寸下的运行周期时长。这是大致的测算,科学的方法是重新计算曲线长度,这太复杂,做个简单的帖子用不着这么认真。
进来听到这音乐也是有点魔幻的{:4_170:} “以比例方法对二次贝塞尔曲线的终点和控制点进行测算”
这个考虑得十分细致{:4_199:} 发现我电脑预览自己改了路径 出来是变换了,但点击全屏还是原来的路径 十分奇妙的背景和音乐,配上这样的粒子特效,十分赞。视频的配置也特别漂亮{:4_199:} 还是没有搞懂,实践了再说 一动一静的效果十分玄妙,论坛出问题时,正好有时间,在黑黑的地方学习了一下,也跟着学做了一个,等空了发出来{:4_173:} 听到这凄凉又魔幻的声音,是哪个冤魂出来溜达了{:4_189:} 这个配乐可真有特点,它与整个作品很和谐地融为一体。 终点X坐标 = mydiv.offsetWidth - 40,40是小球的尺寸要减去;控制点X坐标 = mydiv.offsetWidth / 2;Y坐标 = mydiv.offsetHeight * 2 - 200,
这个全屏的计算的确是很有讲究的。。改了路径方向之后的计算更加复杂。。{:4_170:} 6 是原生动画在 1024*640尺寸下的运行周期时长。
1024和640改了之后,6也是要更改的。。{:4_173:}
现在要回头再看看我做的效果。
贴子效果漂亮
斜飞的几只蝙蝠搭配主体画面,令人观之难忘。。
音乐魔幻。。
贴子标题勾人。。
说明超级详细。。全是优点{:4_170:} 南无月 发表于 2024-6-29 17:28
贴子效果漂亮
斜飞的几只蝙蝠搭配主体画面,令人观之难忘。。
音乐魔幻。。
{:4_176:} 南无月 发表于 2024-6-29 17:25
6 是原生动画在 1024*640尺寸下的运行周期时长。
1024和640改了之后,6也是要更改的。。
现在要 ...
{:4_190:} 南无月 发表于 2024-6-29 17:24
终点X坐标 = mydiv.offsetWidth - 40,40是小球的尺寸要减去;控制点X坐标 = mydiv.offsetWidth / 2;Y坐标 ...
是的,你需要明确哪一些数据需要修改,就去弄它 梦油 发表于 2024-6-29 16:07
这个配乐可真有特点,它与整个作品很和谐地融为一体。
{:4_190:} 小辣椒 发表于 2024-6-29 15:45
进来听到这音乐也是有点魔幻的
多少有一点点吧 红影 发表于 2024-6-29 15:53
“以比例方法对二次贝塞尔曲线的终点和控制点进行测算”
这个考虑得十分细致
这是大概的测算,按理应比较路径的长短来配置窗口变化时的路径