Looping marquee built with GSAP Timeline using custom directions and reverse playing.
A Pen by Patrick F. Mayer on CodePen.
Looping marquee built with GSAP Timeline using custom directions and reverse playing.
A Pen by Patrick F. Mayer on CodePen.
| <div class="marquee" aria-label="You spin me right round baby right round"> | |
| <div class="marquee__text" aria-hidden="true"> | |
| <div id="marquee-first-line" class="marquee__line"> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| </div> | |
| <div id="marquee-second-line" class="marquee__line"> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| </div> | |
| <div id="marquee-third-line" class="marquee__line"> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| <div class="marquee__sentence">You spin me right round baby right round</div> | |
| </div> | |
| </div> | |
| <a href="https://youtu.be/PGNiXGX2nLU?t=59" target="_blank"> | |
| <img | |
| id="maquee-image" | |
| class="marquee__image" | |
| src="https://assets.freedommayer.com/codepen/right-round.gif" | |
| /> | |
| </a> | |
| </div> |
| const configs = { | |
| duration: 30, | |
| ease: 'none', | |
| }; | |
| const lines = { | |
| first: { | |
| direction: 'right', | |
| element: document.querySelector('#marquee-first-line'), | |
| }, | |
| second: { | |
| direction: 'left', | |
| element: document.querySelector('#marquee-second-line'), | |
| }, | |
| third: { | |
| direction: 'right', | |
| element: document.querySelector('#marquee-third-line'), | |
| }, | |
| }; | |
| let timeline = gsap.timeline(); | |
| let sentenceWidth = document.querySelector('.marquee__sentence').clientWidth; | |
| // Init timeline and register events. | |
| function init() { | |
| setTimeline(); | |
| const marqueeImage = document.querySelector('#maquee-image'); | |
| marqueeImage.addEventListener('mouseenter', flipDirection); | |
| marqueeImage.addEventListener('mouseout', flipDirection); | |
| window.addEventListener('resize', handleResize); | |
| } | |
| // Add marquee animations to timeline. | |
| function setTimeline() { | |
| timeline | |
| .add(createMarquee(lines.first.element, lines.first.direction), 0) | |
| .add(createMarquee(lines.second.element, lines.second.direction), 0) | |
| .add(createMarquee(lines.third.element, lines.third.direction), 0); | |
| } | |
| // Create single marquee animation. | |
| function createMarquee(element, direction) { | |
| const distance = sentenceWidth * 2; | |
| return gsap.timeline() | |
| .to(element, { | |
| ...configs, | |
| x: direction === 'left' ? distance : -distance, | |
| onComplete() { | |
| timeline.play(0); | |
| }, | |
| onReverseComplete() { | |
| timeline.reverse(0); | |
| }, | |
| }, | |
| ); | |
| } | |
| // Reverse the timeline direction. | |
| function flipDirection() { | |
| timeline.reversed(!timeline.reversed()); | |
| } | |
| // Reset timeline on resize. | |
| function handleResize() { | |
| sentenceWidth = document.querySelector('.marquee__sentence').clientWidth; | |
| timeline.seek(0); | |
| timeline.clear(); | |
| setTimeline(); | |
| } | |
| init(); |
| <script src="https://unpkg.co/gsap@3/dist/gsap.min.js"></script> |
| @import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@800&display=swap'); | |
| .marquee { | |
| font-size: clamp(4rem, 4.5vw, 9rem); | |
| font-weight: 800; | |
| line-height: 1.25; | |
| position: relative; | |
| text-transform: uppercase; | |
| user-select: none; | |
| width: 100%; | |
| } | |
| .marquee__text { | |
| overflow: hidden; | |
| pointer-events: none; | |
| } | |
| .marquee__line { | |
| display: flex; | |
| position: relative; | |
| &:nth-child(2) { | |
| justify-content: flex-end; | |
| z-index: 1; | |
| } | |
| } | |
| .marquee__sentence { | |
| padding-right: 30px; | |
| white-space: nowrap; | |
| } | |
| .marquee__image { | |
| border-radius: 12px; | |
| height: 120%; | |
| left: 0; | |
| margin: 0 auto; | |
| position: absolute; | |
| right: 0; | |
| top: -10%; | |
| } | |
| html, body { | |
| background-color: #0b0b0b; | |
| color: #ede1ff; | |
| font-family: 'Open Sans', Arial; | |
| } | |
| body { | |
| align-items: center; | |
| display: flex; | |
| justify-content: center; | |
| min-height: 100vh; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| } |