Reputation: 282
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
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.
.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
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
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
Reputation: 23
I came up with some variants:
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
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