如何正确地在中间停止动画

How to stop the animation in the middle of it properly

这是一个使用 CSS 个关键帧的简单脉动动画。

问题是如果我想丢弃或删除动画中间的动画,动画会突然停止(突然跳到其初始状态,如您在代码片段中所见)。

我想让动画顺利回到初始状态,即使你在动画中间停止它。

JQuery 和 TweenMax 被接受。

有什么办法吗?

let test = document.getElementById("test");


setTimeout(function(){

test.classList.add("addAniamte");

}, 2000)


setTimeout(function(){

test.classList.remove("addAniamte");
test.classList.add("removeAniamte");
console.log('stoping aniamtion!');

}, 4500)
@-webkit-keyframes pulse {
 0% {
 -webkit-transform: scale(1, 1);
}
 50% {
 -webkit-transform: scale(3, 3);
}
 100% {
 -webkit-transform: scale(1, 1);
};
}


#test {
 background: red;
 width: 20px;
 height: 20px;
  margin: 60px;
}

.addAniamte{
  -webkit-animation: pulse 1s linear infinite;
 animation: pulse 1s linear infinite; 
}

.removeAniamte{
  -webkit-animation: none;
 animation:none;
}
<div id="test"></div>

我试图通过使用 getComputedStyle 使 javascript 完全动态化。为了顺利停止动画,我们需要有一个观察者变量,它会计算每个动画周期的持续时间。 (tempAnimationWatcher).

  1. 向元素添加动画开始事件。
  2. 计算单次动画时长(testAnimationTime)并启动tempAnimationWatcherInterval观看动画循环时间tempAnimationWatcher
  3. 如果 stopAnimation,在剩余 css 时间后停止动画。 (testAnimationTime - tempAnimationWatcher)

注意testAnimationTime计算是基于css动画时间以秒为单位的考虑。参见第 testAnimationTime = parseInt(animationDurationInCss.substring(0, animationDurationInCss.length - 1)) * 1000; 行,这是删除最后一个字符并以毫秒为单位进行转换。

const test = document.getElementById("test");
let tempAnimationWatcher = 0;
let testAnimationTime = 0;
let tempAnimationWatcherInterval;

test.addEventListener('animationstart', function (e) {
    console.log('animation starts')

    const animationDurationInCss = getComputedStyle(test).animationDuration;
    testAnimationTime = parseInt(animationDurationInCss.substring(0, animationDurationInCss.length - 1)) * 1000;
    tempAnimationWatcher = 0;
    tempAnimationWatcherInterval = setInterval(() => {
        tempAnimationWatcher += 10;
        if (tempAnimationWatcher >= testAnimationTime) tempAnimationWatcher = 0;
    }, 10);
});

function startAnimation() {
    test.classList.add("addAniamte");
}

function stopAnimation() {
    clearInterval(tempAnimationWatcherInterval);
    setTimeout(() => {
        test.classList.remove("addAniamte");
        console.log("stoping aniamtion!");
    }, (testAnimationTime - tempAnimationWatcher));
}

startAnimation();
@keyframes pulse {
    0% {
        -webkit-transform: scale(1, 1);
    }
    50% {
        -webkit-transform: scale(3, 3);
    }
    100% {
        -webkit-transform: scale(1, 1);
    }
    ;
}

#test {
    background: red;
    width: 20px;
    height: 20px;
    margin: 60px;
}

.addAniamte {
    animation: pulse 2s linear infinite;
}
<div id="test"></div>
<hr>
<button onclick="startAnimation()">Start</button>
<button onclick="stopAnimation()">Stop</button>

使用 GSAP 很容易做到这一点!以下是如何在 GSAP 3 中执行此类操作。

var tween = gsap.from("#test", {duration: 1, scale: 0.33, repeat: -1, yoyo: true, paused: true});

function startAnimation() {
    tween.play();
}

function stopAnimation() {
    tween.pause();
    gsap.to(tween, {duration: 0.5, progress: 0});
}
   
#test {
  background: red;
  width: 60px;
  height: 60px;
  margin: 60px;
}
<div id="test"></div>

<button onclick="startAnimation()">Start</button>
<button onclick="stopAnimation()">Stop</button>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.0.4/dist/gsap.min.js"></script>

如果没有 GSAP(或者至少是一种仅使用 JS 的方法),它就会像其他答案所示那样非常混乱且容易出错。事实上,我对这个问题有 my own question which lead to a CSS-Tricks article