Last active
November 28, 2024 00:20
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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