alexmngn
alexmngn

Reputation: 9597

Animation paused with transition

I have an animation moving the background-position of an image with keyframes, and this works fine.

Although, when the user click a button, I would like to pause the animation of the background but with a transition, slowing down then stopping the background-position from moving.

I searched for something similar to that but I didn't find anything, so I was wondering if any of you would have an idea how I could do that?

Thanks.

Upvotes: 4

Views: 1851

Answers (3)

Harry
Harry

Reputation: 89750

NO. You cannot achieve this with CSS animations while using only one element for a few reasons and they are as follows:

  • An animation's play state change to paused is an immediate action.
  • Once an animation is applied on an element, it takes control of the property until it is removed. So, changing background-position with JS will also have no effect (even if it is actually paused).

    From W3C Spec:

    CSS Animations affect computed property values. During the execution of an animation, the computed value for a property is controlled by the animation. This overrides the value specified in the normal styling system.

    A sample can be seen in the below snippet. Along with the animation-play-state: paused, the class that is toggled on also sets a specific value for background-position (with important) but it has absolutely no effect on the element. The commented out portions of the JS is how I had tried to produce a gradual stop at a slower rate but it just does not work.

    var el = document.getElementsByTagName('div')[0];
    var btn = document.getElementById('stop');
    var i = 0,
      bgPosX,
      interval,
      state = 'running';
    
    btn.addEventListener('click', function() {
    /*  state = (state == 'running') ? 'paused' : 'running';
      console.log(state);
      if (state == 'paused') {
        var currPos = window.getComputedStyle(el).backgroundPositionX;
        bgPosX = parseInt(currPos.replace('px', ''), 10);
        interval = setInterval(function() {
          slowPause(bgPosX);
        }, 1);
      } else {
        el.style.backgroundPosition = null;
      } */
      el.classList.toggle('paused');
    });
    
    /*function slowPause(currPosX) {
      el.style.backgroundPosition = currPosX + (i * 0.2) + 'px 0%';
      console.log(el.style.backgroundPositionX);
      i++;
      if (i >= 1000) {
        clearInterval(interval);
        i = 0;
        console.log('cleared');
      }
    }*/
    .animated-bg-pos {
      height: 100px;
      width: 200px;
      background-image: linear-gradient(to right, red 25%, blue 25%, blue 50%, orange 50%, orange 75%, green 75%);
      background-size: 800px 100%;
      animation-name: animate-bg-pos;
      animation-timing-function: linear;
      animation-iteration-count: infinite;
      animation-duration: 4s;
    }
    body div.animated-bg-pos.paused {
      animation-play-state: paused;
      background-position: 200px 0% !important;
    }
    @keyframes animate-bg-pos {
      from {
        background-position: 0px 0%;
      }
      to {
        background-position: 800px 0%;
      }
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
    <div class='animated-bg-pos'></div>
    
    <button id='stop'>Stop Animation</button>

  • Removal of the animation will cause the element to snap back to its original position immediately. It does not transition back gradually. You can read a bit about it here.

  • Applying transition and animation on the same property doesn't work well across browsers. It still results in the animation taking full control and renders the transition useless. Read more here.
  • Finally, even if you somehow get the removal of animation and use JS to slowly transition it to an intermediate state this will not resemble the "pause" behavior. The reason is because when you re-apply the animation it will again start from the first frame and not where you stopped it.

Considering all this, you would have to rewrite your entire animation using jQuery or JavaScript if you have to get the required behavior. A very rough implementation with timers and constant change of the background-position is available in the below snippet.

/* initial variable declarations */
var el = document.getElementsByTagName('div')[0];
var btn = document.getElementById('stop');
var i = 0,
  currPos = 0,
  pauseInterval, runningInterval,
  running = true,
  bgSize = parseInt(window.getComputedStyle(el).backgroundSize.split('px')[0], 10);

/* toggle button listener */
btn.addEventListener('click', function() {
  running = !running; // toggle state
  if (!running) {
    i = 0;
    currPos = parseInt(el.style.backgroundPositionX.replace('px', ''), 10);
    clearInterval(runningInterval); /* stop the normal animation's timer */
    pauseInterval = setInterval(function() {
      slowPause(currPos); /* call the slow pause function */
    }, 10);
  } else {
    i = 0;
    currPos = parseInt(el.style.backgroundPositionX.replace('px', ''), 10);
    clearInterval(pauseInterval); /* for safer side, stop pause animation's timer */
    runningInterval = setInterval(function() {
      anim(currPos); /* call the normal animation's function */
    }, 10);
  }
});

/* slowly pause when toggle is clicked */
function slowPause(currPosX) { 
  el.style.backgroundPosition = (currPosX + (i * .025)) % bgSize  + 'px 0%';
  i += 10;
  if (i >= 1000) { /* end animation after sometime */
    clearInterval(pauseInterval);
    i = 0;
  }
}

/* increment bg position at every interval */
function anim(currPosX) { 
  el.style.backgroundPosition = (currPosX + (i * .25)) % bgSize + 'px 0%';
  i += 10;
}

/* start animation on load */
runningInterval = setInterval(function() { 
  anim(currPos);
}, 10);
.animated-bg-pos {
  height: 100px;
  width: 200px;
  background-image: linear-gradient(to right, red 25%, blue 25%, blue 50%, orange 50%, orange 75%, green 75%);
  background-size: 800px 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='animated-bg-pos'></div>

<button id='stop'>Toggle Animation State</button>

Note: The above snippet is only a rough implementation to give an idea. I am not a JS expert and feel that the JS used in the snippet can be optimized a lot.


If you don't have a problem with adding extra wrapper elements around the one that is being animated then you can make use of the approach mentioned in vals' answer here.

var el = document.getElementsByTagName('div')[1];
var btn = document.getElementById('stop');

btn.addEventListener('click', function() {
  el.classList.toggle('paused');
});
.cover {
  position: relative;
  width: 200px;
  height: 100px;
  border: 1px solid;
  overflow: hidden;
}
.animated-bg-pos {
  position: absolute;
  top: 0px;
  left: 0px;
  height: 100px;
  width: 240px;
  left: -40px;
  background-image: linear-gradient(to right, red 25%, blue 25%, blue 50%, orange 50%, orange 75%, green 75%);
  background-size: 800px 100%;
  animation: animate-bg-pos 4s linear infinite;
  transition: transform 1s ease-out;
}
.paused {
  animation-play-state: paused;
  transform: translateX(40px);
}
@keyframes animate-bg-pos {
  from {
    background-position: 0px 0%;
  }
  to {
    background-position: 800px 0%;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='cover'>
  <div class='animated-bg-pos'></div>
</div>
<button id='stop'>Stop Animation</button>

Note: All credits for the above approach goes to vals. I have only adapted his approach to make this answer more complete.

Upvotes: 6

Guille
Guille

Reputation: 652

You can probably achieve it by using java script to remove and add a new class after a button is clicked.

something like this. Basically just create a new css class with new that sets a specific amount of time for the animation to run.

For Example if the animation you have running, runs indefinitely, then just create a new_class with similar values but with a definite time like 2s or 4s etc. After those 4s the animation should stop.

 $(document).click(function (e) {
  $el = $(e.target);
 if ($el.hasClass('Button_class')) {
      $(".moving_image_class").toggleClass('new_class');

  } else {
      $(".moving_image_class").removeClass('New_class');
  }

});

Upvotes: 0

Shomz
Shomz

Reputation: 37701

Make a class with a CSS transition for the background position, and when the button is clicked, add that class and remove your animation class at the same time. Make sure you use the same units (pixels, percentages...) in both cases.

Upvotes: 1

Related Questions