无限 SVG 动画在悬停时平滑地检索初始状态
Posted
技术标签:
【中文标题】无限 SVG 动画在悬停时平滑地检索初始状态【英文标题】:Infinite SVG animation smoothly retrieve initial state on hover 【发布时间】:2021-03-09 07:33:36 【问题描述】:我希望我的无限 svg 动画在悬停时暂停在其初始或最终状态。我尝试使用 JS 删除类或在 CSS 中将animation-iteration-count: unset;
设置为悬停或设置animation: none;
,但在每种情况下,转换都是突然的。我希望它顺利。这是代码:
html
<div class="button">
<svg version="1.1" id="sound--icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100" >
<rect class="rect-1 rect-anim" x="10" fill="currentColor"></rect>
<rect class="rect-2 rect-anim" x="30" fill="currentColor"></rect>
<rect class="rect-3 rect-anim" x="50" fill="currentColor"></rect>
<rect class="rect-4 rect-anim" x="70" fill="currentColor"></rect>
</svg>
</div>
CSS
#sound--icon .rect-anim
animation-name: sound-icon-anim;
animation-duration: .8s;
animation-timing-function: cubic-bezier(.97,0,0,1);
animation-iteration-count: infinite;
/* #sound--icon:hover .rect-anim
animation-iteration-count: unset;
*/
#sound--icon .rect-1
animation-delay: .1s;
#sound--icon .rect-2
animation-delay: .3s;
#sound--icon .rect-3
animation-delay: .5s;
#sound--icon .rect-4
animation-delay: .7s;
@keyframes sound-icon-anim
0%
height: 30%;
10%
height: 70%;
20%
height: 50%;
50%
height: 80%;
60%
height: 100%;
70%
height: 70%;
100%
height: 30%;
我最终的目标是:
动画正在运行 当 div 悬停/动画暂停时,高度平滑地达到 50% 当点击 div/动画暂停时,高度平滑地达到 30% 当 div 再次悬停/动画仍然暂停时,高度平滑地达到 50% 再次单击 div 时动画会重新运行(因此从 50% 开始,而不是从 0% 动画状态状态的 30% 开始)在纯 CSS 中这对您来说是否可行?
【问题讨论】:
【参考方案1】:我认为您无法在纯 CSS 中做到这一点。使用Web Animations API,这是可能的,但在当前的实施状态下,只有部分方法是现实的。
您可以实现的是在迭代周期结束时停止动画。在一个不是循环结束的点停止将需要实现AnimationTimeline。但目前 Chrome 缺少它。
document.querySelectorAll('.button').forEach(button =>
//.getAnimations() returns an array
const animations = [...button.querySelectorAll('.rect-anim')]
.map(r => r.getAnimations())
.flat();
button.addEventListener('mouseenter', () =>
for (let animation of animations)
const iterations = animation.effect.getComputedTiming()
.currentIteration + 1;
animation.effect.updateTiming(iterations)
);
button.addEventListener('mouseleave', () =>
for (let animation of animations)
animation.effect.updateTiming(iterations: Infinity);
);
);
.button
margin: 20px;
width: 100px;
#sound--icon .rect-anim
animation-name: sound-icon-anim;
animation-duration: .8s;
animation-timing-function: cubic-bezier(.97,0,0,1);
animation-iteration-count: infinite;
#sound--icon .rect-1
animation-delay: .1s;
#sound--icon .rect-2
animation-delay: .3s;
#sound--icon .rect-3
animation-delay: .5s;
#sound--icon .rect-4
animation-delay: .7s;
@keyframes sound-icon-anim
0%
height: 30%;
10%
height: 70%;
20%
height: 50%;
50%
height: 80%;
60%
height: 100%;
70%
height: 70%;
100%
height: 30%;
<div id="this-button" class="button">
<svg version="1.1" id="sound--icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100" >
<rect class="rect-1 rect-anim" x="10" y="40" fill="currentColor"></rect>
<rect class="rect-2 rect-anim" x="30" y="40" fill="currentColor"></rect>
<rect class="rect-3 rect-anim" x="50" y="40" fill="currentColor"></rect>
<rect class="rect-4 rect-anim" x="70" y="40" fill="currentColor"></rect>
</svg>
</div>
【讨论】:
【参考方案2】:谢谢老兄,我学到了一些东西。最后,我对 JS 中的结果做了一些非常令人满意的事情。当悬停getComputedStyle
时,我计算了矩形的高度,并将其更改为我想要添加过渡的高度。我认为代码可以优化,尤其是setTimeout
部分,它删除了定义动画的 CSS 类。如果它不存在,它会与第四个矩形混淆。
const svg = document.querySelector('#sound__icon')
const btn = document.querySelector('.mute')
const rects = svg.children
let mute = false
function muting()
if(!mute)
mute = true
for(let rect of rects)
rect.style.height = '30%'
else
mute = false
audioAnimActivate()
function audioAnimActivate()
for(let rect of rects)
rect.classList.add('rect-anim')
svg.classList.add('sound__icon--on')
function audioAnimFreeze()
for(let rect of rects)
let rectHeight = window.getComputedStyle(rect).height
rect.style.height = rectHeight
rect.classList.remove('rect-anim')
rect.style.transition = 'height .5s ease'
rect.style.height = '50%'
//? How to code this better ???
setTimeout(() => svg.classList.remove('sound__icon--on'), 16)
btn.addEventListener('click', muting)
btn.addEventListener('mouseenter', () =>
if(mute)
rects[0].style.height = '60%'
rects[1].style.height = '20%'
rects[2].style.height = '40%'
rects[3].style.height = '80%'
else
audioAnimFreeze()
)
btn.addEventListener('mouseleave', () =>
if(!mute)
audioAnimActivate()
else
for(let rect of rects)
rect.style.height = '30%'
)
.mute
width: 25vw;
height: 25vw;
border: none;
display: flex;
justify-content: center;
align-items: center;
background: none;
.rect-1, .rect-2, .rect-3, .rect-4
width: 10%;
height: 30%;
.sound__icon--on .rect-anim
animation-name: sound-icon-anim;
animation-duration: 1s;
animation-timing-function: cubic-bezier(.97,0,0,1);
animation-iteration-count: infinite;
.sound__icon--on .rect-1
animation-delay: .1s;
.sound__icon--on .rect-2
animation-delay: .3s;
.sound__icon--on .rect-3
animation-delay: .5s;
.sound__icon--on .rect-4
animation-delay: .7s;
@keyframes sound-icon-anim
0%
height: 30%;
10%
height: 70%;
20%
height: 50%;
50%
height: 80%;
60%
height: 100%;
70%
height: 70%;
100%
height: 30%;
<div class="mute">
<svg version="1.1" id="sound__icon" class="sound__icon--on"xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100" >
<rect class="rect-1 rect-anim" x="15" fill="currentColor"></rect>
<rect class="rect-2 rect-anim" x="35" fill="currentColor"></rect>
<rect class="rect-3 rect-anim" x="55" fill="currentColor"></rect>
<rect class="rect-4 rect-anim" x="75" fill="currentColor"></rect>
</svg>
</div>
【讨论】:
以上是关于无限 SVG 动画在悬停时平滑地检索初始状态的主要内容,如果未能解决你的问题,请参考以下文章