【问题标题】:Reverse background-position animation without resetting无需重置即可反转背景位置动画
【发布时间】:2018-05-03 05:05:25
【问题描述】:

我用 CSS 创建了一个动画,它随着时间的推移改变元素的background-position,以创建一种带有背景的滚动效果。

@keyframes stars-animate {
    0% {
        background-position: 0 -500px;
    }
    100% {
        background-position: 2000px -500px;
    }
}

这非常有效。但是,我也想开始倒带动画并创建一个反向滚动事件。这是由一些不相关的操作触发的。

function triggerReverse(element) {
    element.style.animationDirection = 'reverse';
}

但是,当我将 animation-direction 设置为 reverse 时,它确实可以工作,但在翻转整个背景之前就不行了。

是我做错了,还是这样做的方法不对,如果是,正确的方法是什么?

编辑:我需要能够在动画播放时反转动画

【问题讨论】:

    标签: javascript html css animation background-position


    【解决方案1】:

    以@themefield 的上述回答为基础 - 谢谢@themefield! - 这种方式是“最好的”,而不是完美的。 (有时字母倒转时的位置并不完全正确。)

    行之有效的方法是 a)在最后将动画重置为前进/后退 b) 在切换时将动画替换为相反的动画,设置 - 开始时间以尝试将其定位在原来的位置。

    通常效果很好,有时效果很差。

    span = document.querySelector('span')
    button = document.querySelector('button')
    timerElement = document.querySelector('#timerId')
    duration = 3; // animation-during
    
    let startTime = Date.now();
    toSec = (msec) => msec / 1000
    elapsedTimeMsec = () => Date.now() - startTime
    elapsedTimeSec = () => toSec(elapsedTimeMsec())
    updateTimer = () =>     timerElement.innerHTML = `${elapsedTimeSec().toPrecision(2)}s`
    
    let intervalHandle;
    
    startTimer = () => {
        intervalHandle = window.setInterval(() => {
            updateTimer()
        }, 500)
    }
    
    endTimer = () => {
        window.clearInterval(intervalHandle)
        intervalHandle = null
    }
    
    span.addEventListener('animationstart', () => {
        startTime = Date.now();
        startTimer()
    });
    
    span.addEventListener('animationiteration', () => span.style.animationPlayState = 'paused');
    
    toggleAnimation = (shouldDelay) => {
        span.classList.remove('my_anim');
        void span.offsetWidth;
        span.classList.add('my_anim');
        if(span.style.animationDirection !== 'reverse')
            span.style.animationDirection = 'reverse';
        else
            span.style.animationDirection = 'normal';
        if(shouldDelay !== null && shouldDelay) {
            span.style.animationDelay = `-${elapsedTimeSec()}s`;
        } else {
            span.style.animationDelay = `0s`;
        }
        span.style.animationPlayState = 'running';
    }
    span.addEventListener('animationend', () => {
        endTimer()
        updateTimer()
    
        toggleAnimation();
    });
    
    button.addEventListener('click', () => {
        endTimer()
        updateTimer()
        toggleAnimation(true) // todo pass in delay!
    });
    span.my_anim {
                font-size: 54px;
                animation: 3s 1 normal both my_move;
            }
    
            @keyframes my_move {
                from {
                    margin-left: 0;
                }
                to {
                    margin-left: 50%;
                }
            }
    
            button {
                /*visibility: hidden;*/
            }
            #timerId {
                font-size: 24px;
                color: darkturquoise;
                position: fixed;
                bottom: 0;
                left: 50%;
            }
    <div>
        <span class="my_anim">@</span>
    </div>
    <button>reverse</button>
    <span id="timerId"></span>

    【讨论】:

      【解决方案2】:

      更新

      下面更新的示例代码提供了使用户能够中断/暂停动画(在第一次迭代期间)并立即开始反转动画的效果。

      这里是用时间来控制。记录从动画开始所经过的时间,并计算如何开始反向动画。在 css 中定义了 2 次迭代来形成一个完整的循环。如果没有用户干预,动画会在第一次迭代后暂停/停止。但如果有,请暂停迭代并立即以计算出的animation-delay 时间重新开始它。这看起来像是一个立竿见影的逆转,但实际上它是一个新的开始。

      还有一个关于如何重新启动动画的技巧。请参考代码注释。

      我四处搜索,但发现到目前为止没有人提到类似的场景,也没有类似的解决方案。与其用时间来控制,我想看看其他更好的方法。

      我的测试也证明了不同的运行环境呈现的流畅度略有不同。幸运的是,这里的 SO 是最好的。

      尝试该解决方案,看看它是否可以在您自己的场景中正常工作。

      const span = document.querySelector('span'),
        button = document.querySelector('button'),
        duration = 10; // animation-during
      
      let startTime;
      
      span.addEventListener('animationstart', () => {
        startTime = Date.now();
        button.style.visibility = 'visible';
      });
      
      span.addEventListener('animationiteration', () => span.style.animationPlayState = 'paused');
      
      span.addEventListener('animationend', () => {
        button.style.visibility = 'hidden';
      });
      
      button.addEventListener('click', () => {
        span.classList.remove('my_anim');
        void span.offsetWidth; // safely apply changes
        span.classList.add('my_anim');
        const elapsed = Date.now() - startTime;
        const delay = (elapsed < duration * 1000) ? (elapsed / 1000 - duration * 2) : -duration;
        span.style.animationDelay = `${delay}s`;
        span.style.animationPlayState = 'running';
      });
      span.my_anim {
        animation: 10s 2 alternate my_move;
      }
      
      @keyframes my_move {
        from {
          margin-left: 0;
        }
        to {
          margin-left: 50%;
        }
      }
      
      button {
        visibility: hidden;
      }
      <div>
        <span class="my_anim">@</span>
      </div>
      <button>reverse</button>

      此示例不使用background-position 进行动画,而是使用普通字符。

      const span = document.querySelector("span"),
        button = document.querySelector("button");
      
      span.addEventListener(
        "animationiteration",
        function() {
          this.classList.add("paused");
          button.style.visibility = "visible";
        }
      );
      
      button.addEventListener(
        "click",
        function() {
          this.style.visibility = "hidden";
          span.classList.remove("paused");
        }
      ); 
      span {
        animation: 3s 2 alternate my_move;
      }
      
      span.paused {
        animation-play-state: paused;
      }
      
      @keyframes my_move {
        from {
          margin-left: 0;
        }
        to {
          margin-left: 50%;
        }
      }
      
      button {
        visibility: hidden;
      }
      <div>
        <span>@</span>
      </div>
      <button>reverse</button>

      NB:必要时使用 -webkit- 前缀作为 css 动画。

      【讨论】:

      • 这是最优化的,但它并没有解决我的问题
      • 这不起作用。我需要能够在播放时反转动画
      • @AceInteract:我的理解是您希望用户操作停止动画,然后立即开始在当前位置反转。我说的对吗?
      • 是的。正是我需要的
      • @AceInteract 好的。我已经用一个这样的例子更新了我的答案。
      猜你喜欢
      • 1970-01-01
      • 2012-02-09
      • 1970-01-01
      • 1970-01-01
      • 2011-07-07
      • 2011-03-01
      • 1970-01-01
      • 2011-07-27
      相关资源
      最近更新 更多