JSFiddle - React, Tailwind, and code Playground

by Константин Дралюк

HTML

<div class='news-ticker'>
  <div class="news-ticker__inner">
    <div class="news-ticker__label">News</div>
    <div class="news-ticker__list">
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
      <div class='news-ticker__item'>Lorem ipsum dolor sit amet!</div>
  </div>
</div>

SCSS

body {
  margin: 0;
  font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}

.news-ticker {
  color: #000;
  &, * {
    box-sizing: border-box;
  }
  &__inner {
    display: flex;
    height: 4rem;
  }
  &__label, &__list, &__item {
    display: flex;
    align-items: center;
  }
  &__label {
    padding: 0 1.25rem;
    background: #f00;
  }
  &__list {
    overflow: hidden;
  }
  &__item {
    padding: 0 .75rem;
    white-space: nowrap;
    height: 100%;
    user-select: none;
  }
}

JavaScript

const $js = (selector) => document.querySelectorAll(`[data-js='${selector}']`)
const $nts = $js('news-ticker')

;[...$nts].map($nt => {
  let isStopped = false

  $nt.onmouseover = () => isStopped = true
  $nt.ontouchstart = () => isStopped = true
  $nt.onmouseout = () => isStopped = false
  $nt.ontouchend = () => isStopped = false

  const move = () => {
    const firstChild = $nt.firstElementChild
    const lastChild = $nt.lastElementChild
    const scrollViewWidth = $nt.scrollWidth - $nt.offsetWidth
    const isScrollable = scrollViewWidth > 0
    const needsCloning = scrollViewWidth < firstChild.offsetWidth + lastChild.offsetWidth

    requestAnimationFrame(move)

    if (!isScrollable) {
      $nt.scrollLeft = 0
    }

    if (isStopped || !isScrollable) {
      return false
    }

    if (needsCloning) {
      [...$nt.children].map($el => {
        const $cloned = $el.cloneNode(true)
        $cloned.className += ' -cloned'
        return $nt.appendChild($cloned)
      })
    }

    if ($nt.scrollLeft < scrollViewWidth) {
      $nt.scrollLeft += 1
    } else {
      $nt.scrollLeft -= firstChild.offsetWidth
      $nt.appendChild(firstChild)
    }
  }

  requestAnimationFrame(move)

  const reset = () => [...$nt.querySelectorAll('.-cloned')].map($el => $el.remove())
  window.addEventListener('resize', reset)
})