amedicalenthusiast
amedicalenthusiast

Reputation: 282

How to make CSS animation slows down to stop on hover, and continue moving infinitely on mouse leave?

I'm trying to create a marquee using the following css styling:

@keyframes scroll {
 0% { transform: translateX(0) }
 100% { transform: translate(-100%) }
}
animation: scroll var(--duration) linear infinite;

I want to make it slows down and stops on hover, then continue looping onMouseLeave, but I can't seem to achieve this effect due to css animation recomputing / resetting position each time I apply a new animation.

Is there a way to achieve this effect? Thank you!

Upvotes: 2

Views: 6110

Answers (5)

tubstrr
tubstrr

Reputation: 1255

You can toggle the animation-play-state: pause of the marquee, on :hover of the parent component.

The animation-play-state CSS property sets whether an animation is running or paused.

With the marquee paused, you can then animate the parent's transform: translateX(); on :hover which you can control to make it feel like the Marquee is slowing down to a stop.

When :hover is removed, the DOM will resume the Marquee animation, with the combination of the parent wrapper's translate returning to 0.

Here is a working example of what I'm talking about:

.container {
     --duration: 20s;
     height: 100px;
     background: red;
     transform: translateX(0);
     transition: calc(var(--duration) * .1) ease-out;
}
 .container h1 {
     display: inline-block;
     width: max-content;
     animation: scroll var(--duration) linear infinite;
}
 .container:hover {
     transform: translateX(-20%);
}
 .container:hover h1 {
     animation-play-state: paused;
}
 @keyframes scroll {
     0% {
         transform: translateX(0);
    }
     100% {
         transform: translate(-100%);
    }
}
 
<div class="container">
  <h1>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vestibulum nulla ut nibh vulputate egestas.  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vestibulum nulla ut nibh vulputate egestas.  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vestibulum nulla ut nibh vulputate egestas. </h1>
</div>

Upvotes: 9

SolessChong
SolessChong

Reputation: 3407

CSS

.marquee {
  overflow: hidden;
  position: relative;
  white-space: nowrap;
}

.marquee-content {
  display: inline-block;
  animation: scroll var(--duration, 10s) linear infinite;
}

@keyframes scroll {
  0% {
    transform: translateX(100%);
  }
  100% {
    transform: translateX(-100%);
  }
}

JS

const marqueeContent = document.getElementById('marqueeContent');

marqueeContent.addEventListener('mouseenter', () => {
  const computedStyle = getComputedStyle(marqueeContent);
  const currentTransform = computedStyle.transform;
  marqueeContent.style.animationPlayState = 'paused';
  marqueeContent.style.transform = currentTransform;
});

marqueeContent.addEventListener('mouseleave', () => {
  marqueeContent.style.animationPlayState = 'running';
});

a marquee that scrolls from right to left.

Upvotes: 0

jottin
jottin

Reputation: 429

Maybe this will change in the future, but there is no "easing" for "animation-play-state". Switching animations on hover is about all you can do without using js...

https://developer.mozilla.org/en-US/docs/Web/CSS/animation-play-state

.marquee-container {
  overflow: hidden;
  width: 100%;
}

.marquee {
  animation: scroll 5s linear infinite;
  cursor: pointer;
  background-color: #f1f1f1;
  margin-left: 0;
  min-height: 100px;
}

@keyframes scroll {
  0% {
    margin-left: 0;
  }
  100% {
    margin-left: -100%;
  }
}

@keyframes scrollstop {
  0% {
    margin-left: 0;
  }
  100% {
    margin-left: -50%;
  }
}

.marquee:hover {
  animation: scrollstop 2s forwards ease-out !important;
  animation-iteration-count: 0;
}
<div class="marquee-container">
  <div class="marquee">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec id dictum ex. Etiam eu velit ullamcorper, tristique nibh vitae, tincidunt massa. Praesent sit amet massa metus. Fusce at neque turpis. Nam ac augue et felis gravida maximus.</div>
</div>

https://jsfiddle.net/sdzqv5ng/

Upvotes: 0

IO_Nox
IO_Nox

Reputation: 23

I came up with some variants:

  • A, B, C - reverse animate a container to slow down inner container, just pause infinite animations
  • D, E - pause animation and transform (with transition) container on mouse hover
  • F - just drop animation on hover
  • G (works not perfect) - make a fast last animation cycle and reset animation by JS so it can work again

 function restartAnimation(element){
            element.style.animation = "none"
            // DOM reflow x2
            element.offsetWidth;
            element.offsetWidth;
            element.style.animation = null
        }
 .flexCenter {
        display: flex;
        align-items: center;
        justify-content: center;
      }
      .page {
        width: 100%;
        height: 250px;
        background-color: aliceblue;
      }
      .container {
        margin: auto;
        border-radius: 15%;
        width: 80px;
        height: 80px;
        background-color: blue;
      }
      .spinner {
        border-radius: 15%;
        background-color: blueviolet;
        animation: spin 4s linear infinite;
      }
      .dif .spinner {
        width: 50px;
        height: 50px;
      }
      .same .spinner {
        width: 100%;
        height: 100%;
      }
      .noBgConteiner .container{
        background-color: rgba(0, 0, 0, 0);
      }
      .container.A {
        animation: spin 5s linear infinite;
        animation-play-state: paused;
        animation-direction: reverse;
        transition-timing-function: ease-in-out;
      }
      .container.B,
      .container.C {
        animation: spin 4s linear infinite;
        animation-play-state: paused;
        animation-direction: reverse;
        transition-timing-function: ease-in-out;
      }
      .container.D,
      .container.E {
        transition: transform 2s;
      }
      .container.A:hover,
      .container.B:hover,
      .container.C:hover {
        animation-play-state: running;
      }
      .spinner.C:hover {
        animation-play-state: paused;
      }
      .container.D:hover{
        transform: rotate(-45deg);
      }
      .container.E:hover{
        transform: rotate(20deg);
      }
      .spinner.D:hover, .spinner.E:hover {
        animation-play-state: paused;
      }
      .spinner.F:hover {
        animation: empty;
      }
      .spinner.G{
        animation-iteration-count: infinite;
        animation-play-state: running;
      }
      .spinner.G:hover {
        animation-iteration-count: 1;
        animation-duration: 1.5s;
      }

      @keyframes spin {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      @keyframes empty {
        /*100% {
          transform: rotate(0deg);
        }*/
      }
<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <h1>Different on :hover variations</h1>
    <p>only G need JS and works unstable (need DOM reflow to reattach animation). G without JS is an one-time solution - after first animation will be like F.</p>
    <h1>Different size for child and container</h1>
    <div class="page flexCenter dif">
      <div class="container flexCenter A">
        <div class="spinner flexCenter A"><b>A</b></div>
      </div>
      <div class="container flexCenter B">
        <div class="spinner flexCenter B"><b>B</b></div>
      </div>
      <div class="container flexCenter C">
        <div class="spinner flexCenter C"><b>C</b></div>
      </div>
      <div class="container flexCenter D">
        <div class="spinner flexCenter D"><b>D</b></div>
      </div>
      <div class="container flexCenter E">
        <div class="spinner flexCenter E"><b>E</b></div>
      </div>
      <div class="container flexCenter F">
        <div class="spinner flexCenter F"><b>F</b></div>
      </div>
      <div class="container flexCenter G">
        <div class="spinner flexCenter G" onmouseout="restartAnimation(this)"><b>G</b></div>
      </div>
    </div>
    <h1>Same size for child and container</h1>
    <div class="page flexCenter same">
      <div class="container flexCenter A">
        <div class="spinner flexCenter A"><b>A</b></div>
      </div>
      <div class="container flexCenter B">
        <div class="spinner flexCenter B"><b>B</b></div>
      </div>
      <div class="container flexCenter C">
        <div class="spinner flexCenter C"><b>C</b></div>
      </div>
      <div class="container flexCenter D">
        <div class="spinner flexCenter D"><b>D</b></div>
      </div>
      <div class="container flexCenter E">
        <div class="spinner flexCenter E"><b>E</b></div>
      </div>
      <div class="container flexCenter F">
        <div class="spinner flexCenter F"><b>F</b></div>
      </div>
      <div class="container flexCenter G">
        <div class="spinner flexCenter G" onmouseout="restartAnimation(this)"><b>G</b></div>
      </div>
    </div>
    </div>
    <h1>Same size and transparent container</h1>
    <div class="page flexCenter same noBgConteiner">
      <div class="container flexCenter A">
        <div class="spinner flexCenter A"><b>A</b></div>
      </div>
      <div class="container flexCenter B">
        <div class="spinner flexCenter B"><b>B</b></div>
      </div>
      <div class="container flexCenter C">
        <div class="spinner flexCenter C"><b>C</b></div>
      </div>
      <div class="container flexCenter D">
        <div class="spinner flexCenter D"><b>D</b></div>
      </div>
      <div class="container flexCenter E">
        <div class="spinner flexCenter E"><b>E</b></div>
      </div>
      <div class="container flexCenter F">
        <div class="spinner flexCenter F"><b>F</b></div>
      </div>
      <div class="container flexCenter G">
        <div class="spinner flexCenter G" onmouseout="restartAnimation(this)"><b>G</b></div>
      </div>
    </div>
  </body>
</html>

Upvotes: 0

Patrick Medley
Patrick Medley

Reputation: 22

Yea, you can do the effect using CSS animations and JavaScript. I hope this helps

const marquee = document.querySelector('.marquee');
marquee.addEventListener('mouseenter', () => {
  marquee.classList.add('paused');
});

marquee.addEventListener('mouseleave', () => {
  marquee.classList.remove('paused');
});
.marquee-container {
  overflow: hidden;
  width: 100%;
}

.marquee {
  animation: scroll 20s linear infinite;
}

@keyframes scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-100%);
  }
}

.marquee.paused {
  animation-play-state: paused;
}
<div class="marquee-container">
  <div class="marquee">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec id dictum ex. Etiam eu velit ullamcorper, tristique nibh vitae, tincidunt massa. Praesent sit amet massa metus. Fusce at neque turpis. Nam ac augue et felis gravida maximus. </div>
</div>

Upvotes: -1

Related Questions