Search code examples
htmlcsshorizontal-scrollingmarquee

Seamless infinite scrolling text marquee CSS/width issue


I am trying to create a HTML/CSS scrolling text feature however I am having some issues with it appearing seamless.

The scrolling text starts on the right and scrolls across correctly but then when it reaches the left hand side, it disappears for 6-7 seconds because of the width: 100%; on the #marqueeText so it is continuing to scroll off the left hand side for about 1020px (the width of the container).

If I remove width: 100%; then #marqueeText is only around 150px and it scrolls from the right across the screen about 300px (-100%) and then loops again.

const timePerPixel = 5;
const containerWidth = 1120;
const text = document.getElementById('marqueeText');
const textWidth = text.offsetWidth;
const distance = textWidth + containerWidth;
const duration = timePerPixel * distance;
text.style.animationDuration = `${duration}ms`;
@keyframes marquee-animation {
  from {
    /* Start right out of view */
    transform: translate3d(100%, 0, 0);
  }
  to {
    /* Animate to the left of the container width */
    transform: translate3d(-100%, 0, 0);
  }
}

.topmarquee {
  width: 100%;
  border: 1px solid #000;
}

.topmarquee .marquee {
  width: calc(100% - 200px);
  position: relative;
  overflow: hidden;
  height: 31px;
}

.topmarquee .marquee #marqueeText {
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
  white-space: nowrap;
  transform: translate3d(100%, 0, 0);
  animation-name: marquee-animation;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  line-height: 31px;
  width: 100%;
}
<div class="d-none d-lg-flex row topmarquee mt-4">
  <div class="marquee">
    <p id="marqueeText" class="marqueeText mb-0">
      This is a test website
    </p>
  </div>
</div>

(The reason I have done the animation properties separate is that I have some JS to add animation-duration depending on the length of the text so that the speed always stays the same, based on this post: Marquee text effect. Same scrolling speed no matter the length of the text

Ideally i'd like it to re-appear on the right hand side as soon as it all disappears off the left to appear seamless.


Solution

  • To make the animation look continuous the system will need to know about the width of the actual text. At the moment the innermost element has the width of the outer one.

    This snippet sets its width to fit-content.

    The translates will now not work as they depend on the width of the animated element being the same as the outer (100%) so instead we position the element 100% left to start with which gets it out of view, and then at the end of the animation we put it left 0 and also translate it by -100% (which is 100% of its width, not that of its parent).

    const timePerPixel = 5;
    const containerWidth = 1120;
    const text = document.getElementById('marqueeText');
    const textWidth = text.offsetWidth;
    const distance = textWidth + containerWidth;
    const duration = timePerPixel * distance;
    text.style.animationDuration = `${duration}ms`;
    @keyframes marquee-animation {
      from {
        /* Start right out of view */
      }
      to {
        /* Animate to the left of the container width */
        left: 0;
        transform: translateX(-100%);
      }
    }
    
    .topmarquee {
      width: 100%;
      border: 1px solid #000;
    }
    
    .topmarquee .marquee {
      width: calc(100% - 200px);
      position: relative;
      overflow: hidden;
      height: 31px;
    }
    
    .topmarquee .marquee #marqueeText {
      display: block;
      position: absolute;
      top: 0;
      right: 0;
      margin: 0;
      white-space: nowrap;
      left: 100%;
      transform: translateX(0);
      animation-name: marquee-animation;
      animation-iteration-count: infinite;
      animation-timing-function: linear;
      line-height: 31px;
      width: 100%;
      width: fit-content;
    }
    <div class="d-none d-lg-flex row topmarquee mt-4">
      <div class="marquee">
        <p id="marqueeText" class="marqueeText mb-0">
          This is a test website
        </p>
      </div>
    </div>