Skip to content

Instantly share code, notes, and snippets.

@alsolovyev
Last active November 28, 2024 00:20
Show Gist options
  • Save alsolovyev/abbcedfbebd84cd1f59ab5dcd7665863 to your computer and use it in GitHub Desktop.
Save alsolovyev/abbcedfbebd84cd1f59ab5dcd7665863 to your computer and use it in GitHub Desktop.
A GSAP-powered marquee example that pauses smoothly on mouse hover and resumes seamlessly (https://codepen.io/janeRivas/full/JoPjzZj)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Marquee</title>
<style>
body {
margin: 0;
}
main {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: #f4f4f4;
}
.marquee {
overflow-x: hidden;
transition: opacity 500ms;
}
.container {
display: flex;
gap: 20px;
padding-bottom: 1px;
}
.is-loading {
opacity: 0;
}
.box {
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
width: 170px;
aspect-ratio: 3 / 2;
box-shadow: 0 1px rgba(0, 0, 0, 0.1);
border-radius: 4px;
background: #fff;
cursor: pointer;
}
</style>
</head>
<body>
<main>
<div class="marquee">
<div class="container">
<div class="box"></div>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const marquee = document.querySelector('.marquee')
const container = document.querySelector('.container')
const box = document.querySelector('.box')
const boxGap = parseFloat(window.getComputedStyle(container, null).getPropertyValue('gap'))
const boxWidth = box.getBoundingClientRect().width
// Adds or removes items as needed
function syncMarqueeBoxes() {
const boxes = document.querySelectorAll('.box')
const numBoxes = Math.ceil(window.innerWidth / boxWidth) + 1
if (boxes.length < numBoxes) {
for (let i = 0; i < numBoxes - boxes.length; i++) {
const boxClone = box.cloneNode()
container.appendChild(boxClone)
}
return
}
for (let i = 0; i < boxes.length - numBoxes; i++) {
container.removeChild(container.lastElementChild)
}
}
window.addEventListener('resize', syncMarqueeBoxes)
syncMarqueeBoxes()
// Animation
let tween = gsap.to(container, {
x: -(boxWidth + boxGap),
duration: 2,
repeat: -1,
ease: 'linear',
onInit: () => marquee.classList.remove('is-loading'),
})
marquee.addEventListener('mouseenter', () => {
gsap.to(tween, {
timeScale: 0,
duration: 0.5,
ease: 'power1.inOut',
})
})
marquee.addEventListener('mouseleave', () => {
gsap.to(tween, {
timeScale: 1,
duration: 0.5,
ease: 'power1.inOut',
})
})
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment