简体   繁体   中英

Reverse background-position animation without resetting

I created an animation with CSS that changes the background-position of an element over time, to create a sort of scrolling effect with the background.

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

This works perfectly. However, I also want to start to rewind the animation and create a reverse scrolling event. This is triggered by some irrelevant action.

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

However, when I set the animation-direction to reverse , it does work, but not before it flips the entire background.

Am I doing it wrong, or is that the wrong way to do it, and if so, what is the right way?

Edit: I need to be able to reverse the animation while it is playing

UPDATE

The renewed sample code below provide the effect that enables a user to interrupt/pause the animation (during the first iteration) and immediately start to reverse the animation.

Here it is using time to control. Record the elapsed time from the beginning of animation, and calculate how to start the reverse animation. There are 2 iterations defined in css to make a whole loop. Without user intervention, the animation pauses/stops after the first iteration. But if there is, pause the iteration and immediately re-start it with a calculated animation-delay time. This will looks like an immediate reverse, however actually it is a new start.

There is also a trick on how to re-start the animation. Please refer to the code comment.

I searched around but found nobody has mentioned a similar scenario so far, nor a similar solution. Instead of to use time to control, I would like to see other better approaches.

My test also proves that different running environments render slightly different smoothness. Fortunately, here in SO is the best.

Try the solution to see if it can works well in your own scenario.

 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> 


This example does not use background-position for animation but a plain character.

 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 : Use -webkit- prefix for css animation when necessary.

building off of @themefield's answer above - thanks, @themefield! - this way works the 'best', not perfect. (Sometimes the letter isn't in exactly the right spot when it reverses.)

The approach that worked was a) reset animation to forward / reverse at the end b) replace the animation with its opposite on toggling, setting a - start time to try to position it where it was.

Often it works pretty good, sometimes a lot off.

 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM