Infinite Horizontal Scroller - Copy this Angular, Tailwind Component to your project
mejor toma encuenta este codigo y hazlo <!-- PROGRESSIVELY ENHANCED If a user has `prefers-reduced-motion: reduced`, there will be no animation and the items will wrap, instead of being hidden. If they have not opted for reduced motion, the items will be duplicated with JS and the duplicated content will have `aria-hidden="true"` to prevent duplicate content for screen readers. If a user has JS disabled or it fails for whatever reason, they will get the same experience as a user with `prefers-reduced-motion: reduced`, so no content is hidden, and there is no animation. === OPTIONS === CONTROL SPEED If you don't assign anything, it will use a default speed. To change the speed, on the `.scroller` you can use `data-speed="fast"` or `data-speed="slow" CONTROL DIRECTION By default, it will scroll from right to left. To change the direction, on the `.scroller` you can use `data-direction="right"` (`data-direction="left" also works, but it is the default) --> <h1 style="text-align: center">Infinite Scroll Animation</h1> <div class="scroller" data-speed="fast"> <ul class="tag-list scroller__inner"> <li>HTML</li> <li>CSS</li> <li>JS</li> <li>SSG</li> <li>webdev</li> <li>animation</li> <li>UI/UX</li> </ul> </div> <div class="scroller" data-direction="right" data-speed="slow"> <div class="scroller__inner"> <img src="https://i.pravatar.cc/150?img=1" alt="" /> <img src="https://i.pravatar.cc/150?img=2" alt="" /> <img src="https://i.pravatar.cc/150?img=3" alt="" /> <img src="https://i.pravatar.cc/150?img=4" alt="" /> <img src="https://i.pravatar.cc/150?img=5" alt="" /> <img src="https://i.pravatar.cc/150?img=6" alt="" /> </div> </div> <a class="yt" href="https://youtu.be/pKHKQwAsZLI"> Watch the tutorial </a>.scroller { max-width: 600px; } .scroller__inner { padding-block: 1rem; display: flex; flex-wrap: wrap; gap: 1rem; } .scroller[data-animated="true"] { overflow: hidden; -webkit-mask: linear-gradient( 90deg, transparent, white 20%, white 80%, transparent ); mask: linear-gradient(90deg, transparent, white 20%, white 80%, transparent); } .scroller[data-animated="true"] .scroller__inner { width: max-content; flex-wrap: nowrap; animation: scroll var(--_animation-duration, 40s) var(--_animation-direction, forwards) linear infinite; } .scroller[data-direction="right"] { --_animation-direction: reverse; } .scroller[data-direction="left"] { --_animation-direction: forwards; } .scroller[data-speed="fast"] { --_animation-duration: 20s; } .scroller[data-speed="slow"] { --_animation-duration: 60s; } @keyframes scroll { to { transform: translate(calc(-50% - 0.5rem)); } } /* general styles */ :root { --clr-neutral-100: hsl(0, 0%, 100%); --clr-primary-100: hsl(205, 15%, 58%); --clr-primary-400: hsl(215, 25%, 27%); --clr-primary-800: hsl(217, 33%, 17%); --clr-primary-900: hsl(218, 33%, 9%); } html { color-scheme: dark; } body { display: grid; min-block-size: 100vh; place-content: center; font-family: system-ui; font-size: 1.125rem; background-color: var(--clr-primary-800); } .tag-list { margin: 0; padding-inline: 0; list-style: none; } .tag-list li { padding: 1rem; background: var(--clr-primary-400); border-radius: 0.5rem; box-shadow: 0 0.5rem 1rem -0.25rem var(--clr-primary-900); } /* for testing purposed to ensure the animation lined up correctly */ .test { background: red !important; } const scrollers = document.querySelectorAll(".scroller"); // If a user hasn't opted in for recuded motion, then we add the animation if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) { addAnimation(); } function addAnimation() { scrollers.forEach((scroller) => { // add data-animated="true" to every `.scroller` on the page scroller.setAttribute("data-animated", true); // Make an array from the elements within `.scroller-inner` const scrollerInner = scroller.querySelector(".scroller__inner"); const scrollerContent = Array.from(scrollerInner.children); // For each item in the array, clone it // add aria-hidden to it // add it into the `.scroller-inner` scrollerContent.forEach((item) => { const duplicatedItem = item.cloneNode(true); duplicatedItem.setAttribute("aria-hidden", true); scrollerInner.appendChild(duplicatedItem); }); }); }