最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Infinite Marquee with gradient text - Stack Overflow

programmeradmin2浏览0评论

I need to make an endless running line with gradient text that changes as it moves, as in the example, it works, but there are problems in Safari, everything twitches there and the letters seem to run into each other, maybe something can be done so that it also works in Safari without freezes. Maybe there are other solutions for such an implementation.

Example marquee

document.querySelectorAll('.running-lines__wrapper').forEach(
  (line) => {
    const content = line.innerHTML;
    const duplicateCount = line.getAttribute('data-duplicate-count') || 1;
    const duration =
      parseInt(line.getAttribute('data-duration'), 10) || 10000;
    const direction = line.getAttribute('data-direction') || 'normal';
    let duplicatedContent = '';

    for (let i = 0; i < duplicateCount; i++) {
      duplicatedContent += content;
    }

    line.innerHTML = duplicatedContent;

    const lineText = line.querySelectorAll('.running-lines__items');

    lineText.forEach((text) => {
      const fromFrame = {
        textIndent: 0
      };
      const toFrame = {
        textIndent: '-100%'
      };
      const options = {
        duration,
        iterations: Infinity,
        direction
      };
      text.animate([fromFrame, toFrame], options);
    });
  });
.running-lines {
  font-family: "Poppins";
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
  -ms-flex-direction: column;
  flex-direction: column;
  row-gap: 0.5rem;
  overflow: hidden;
  background-image: linear-gradient(92.55deg,
      #852fff 0%,
      #dd26ed 48.5%,
      #ed2662 100%);
  -webkit-background-clip: text;
  background-clip: text;
  padding-top: 4rem;
  padding-bottom: 4rem;
  mix-blend-mode: lighten;
}

@media (min-width: 956px) {
  .running-lines {
    row-gap: 1rem;
    padding-top: 3.5rem;
    padding-bottom: 3.5rem;
  }
}

@media (min-width: 1184px) {
  .running-lines {
    row-gap: 1.25rem;
  }
}

.running-lines__wrapper {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  padding-top: 0.625rem;
  padding-bottom: 0.625rem;
}

.running-lines__items {
  margin-bottom: 0;
  text-wrap: nowrap;
  will-change: text-indent;
  /*   animation: marquee 15s linear infinite; */
}

.running-lines__item {
  margin-right: 2rem;
  font-size: 1.25rem;
  line-height: 1.5rem;
  font-style: italic;
  color: transparent;
}

@media (min-width: 956px) {
  .running-lines__item {
    margin-right: 3rem;
    font-size: 1.5rem;
    line-height: 2rem;
  }
}

@media (min-width: 1184px) {
  .running-lines__item {
    margin-right: 4rem;
  }
}

@keyframes marquee {
  from {
    text-indent: 0;
  }

  to {
    text-indent: -100%;
  }
}
<div class="running-lines">
  <div class="running-lines__wrapper" data-duration="28000" data-duplicate-count="7">
    <p class="running-lines__items">
      <span class="running-lines__item">Security Kit</span>
      <span class="running-lines__item">Honeypot</span>
      <span class="running-lines__item">Antibot</span>
      <span class="running-lines__item">Flood Control</span>
      <span class="running-lines__item">Move 403 to 404</span>
      <span class="running-lines__item">Rabbit Hole</span>
      <span class="running-lines__item">Username Enumeration Prevention</span>
    </p>
  </div>
  <div class="running-lines__wrapper" data-duration="50000" data-direction="reverse" data-duplicate-count="7">
    <p class="running-lines__items">
      <span class="running-lines__item">Content Moderation</span>
      <span class="running-lines__item">Workflows</span>
      <span class="running-lines__item">Simple Menu Permissions</span>
      <span class="running-lines__item">Group Node</span>
      <span class="running-lines__item">Scheduler</span>
      <span class="running-lines__item">Media</span>
    </p>
  </div>
  <div class="running-lines__wrapper" data-duration="40000" data-duplicate-count="7">
    <p class="running-lines__items">
      <span class="running-lines__item">Paragraphs & Paragraphs Browser</span>
      <span class="running-lines__item">Group</span>
      <span class="running-lines__item">Gin & Gin Toolbar</span>
      <span class="running-lines__item">Crop & Image Widget Crop</span>
      <span class="running-lines__item">Migrate & Migrate Plus</span>
    </p>
  </div>
</div>

I need to make an endless running line with gradient text that changes as it moves, as in the example, it works, but there are problems in Safari, everything twitches there and the letters seem to run into each other, maybe something can be done so that it also works in Safari without freezes. Maybe there are other solutions for such an implementation.

Example marquee

document.querySelectorAll('.running-lines__wrapper').forEach(
  (line) => {
    const content = line.innerHTML;
    const duplicateCount = line.getAttribute('data-duplicate-count') || 1;
    const duration =
      parseInt(line.getAttribute('data-duration'), 10) || 10000;
    const direction = line.getAttribute('data-direction') || 'normal';
    let duplicatedContent = '';

    for (let i = 0; i < duplicateCount; i++) {
      duplicatedContent += content;
    }

    line.innerHTML = duplicatedContent;

    const lineText = line.querySelectorAll('.running-lines__items');

    lineText.forEach((text) => {
      const fromFrame = {
        textIndent: 0
      };
      const toFrame = {
        textIndent: '-100%'
      };
      const options = {
        duration,
        iterations: Infinity,
        direction
      };
      text.animate([fromFrame, toFrame], options);
    });
  });
.running-lines {
  font-family: "Poppins";
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
  -ms-flex-direction: column;
  flex-direction: column;
  row-gap: 0.5rem;
  overflow: hidden;
  background-image: linear-gradient(92.55deg,
      #852fff 0%,
      #dd26ed 48.5%,
      #ed2662 100%);
  -webkit-background-clip: text;
  background-clip: text;
  padding-top: 4rem;
  padding-bottom: 4rem;
  mix-blend-mode: lighten;
}

@media (min-width: 956px) {
  .running-lines {
    row-gap: 1rem;
    padding-top: 3.5rem;
    padding-bottom: 3.5rem;
  }
}

@media (min-width: 1184px) {
  .running-lines {
    row-gap: 1.25rem;
  }
}

.running-lines__wrapper {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  padding-top: 0.625rem;
  padding-bottom: 0.625rem;
}

.running-lines__items {
  margin-bottom: 0;
  text-wrap: nowrap;
  will-change: text-indent;
  /*   animation: marquee 15s linear infinite; */
}

.running-lines__item {
  margin-right: 2rem;
  font-size: 1.25rem;
  line-height: 1.5rem;
  font-style: italic;
  color: transparent;
}

@media (min-width: 956px) {
  .running-lines__item {
    margin-right: 3rem;
    font-size: 1.5rem;
    line-height: 2rem;
  }
}

@media (min-width: 1184px) {
  .running-lines__item {
    margin-right: 4rem;
  }
}

@keyframes marquee {
  from {
    text-indent: 0;
  }

  to {
    text-indent: -100%;
  }
}
<div class="running-lines">
  <div class="running-lines__wrapper" data-duration="28000" data-duplicate-count="7">
    <p class="running-lines__items">
      <span class="running-lines__item">Security Kit</span>
      <span class="running-lines__item">Honeypot</span>
      <span class="running-lines__item">Antibot</span>
      <span class="running-lines__item">Flood Control</span>
      <span class="running-lines__item">Move 403 to 404</span>
      <span class="running-lines__item">Rabbit Hole</span>
      <span class="running-lines__item">Username Enumeration Prevention</span>
    </p>
  </div>
  <div class="running-lines__wrapper" data-duration="50000" data-direction="reverse" data-duplicate-count="7">
    <p class="running-lines__items">
      <span class="running-lines__item">Content Moderation</span>
      <span class="running-lines__item">Workflows</span>
      <span class="running-lines__item">Simple Menu Permissions</span>
      <span class="running-lines__item">Group Node</span>
      <span class="running-lines__item">Scheduler</span>
      <span class="running-lines__item">Media</span>
    </p>
  </div>
  <div class="running-lines__wrapper" data-duration="40000" data-duplicate-count="7">
    <p class="running-lines__items">
      <span class="running-lines__item">Paragraphs & Paragraphs Browser</span>
      <span class="running-lines__item">Group</span>
      <span class="running-lines__item">Gin & Gin Toolbar</span>
      <span class="running-lines__item">Crop & Image Widget Crop</span>
      <span class="running-lines__item">Migrate & Migrate Plus</span>
    </p>
  </div>
</div>

Share Improve this question asked Mar 15 at 10:02 Clop1986Clop1986 311 silver badge3 bronze badges 3
  • 1 I don't see any problem on Safari (IOS 18). What version/OS are you testing on? – A Haworth Commented Mar 15 at 11:10
  • Because text-indent is animated – imhvost Commented Mar 15 at 21:55
  • It's clear, the text gradient doesn't work with transform, that's why I'm asking for solutions, maybe someone has already done something similar. I noticed that the problem here is not only in Safari, but in all browsers, and on different systems, there if you look closely, the letters run left and right. – Clop1986 Commented Mar 15 at 23:51
Add a comment  | 

1 Answer 1

Reset to default 2

I have reworked my solution to the question, now there are no jerks on all devices, I am posting it here, maybe it will be useful to someone

document.querySelectorAll('.running-lines__wrapper').forEach((line) => {
  const $items = line.querySelector('.running-lines__items');
  const content = $items.innerHTML;
  const duplicateCount = line.getAttribute('data-duplicate-count') || 1;; 
  let duration = parseFloat(line.getAttribute('data-duration')) || 1;
  const direction = line.getAttribute('data-direction') || 'normal';
  let scrollAmount = 0;
  let bgPosition = 0;

  if (direction === 'reverse') {
    line.style.direction = 'rtl';
    duration = -duration;
  }

  $items.innerHTML = content.repeat(duplicateCount);

  const startScrolling = () => {
    const scrollStep = () => {
      scrollAmount += duration;
      bgPosition += duration;

      line.scrollLeft = scrollAmount;

      $items.style.backgroundPositionX =
        direction === 'reverse'
          ? `calc(100% - ${Math.abs(bgPosition)}px)`
          : `${bgPosition}px`;

      const itemsRect = $items.getBoundingClientRect();
      const isReverse = direction === 'reverse';
      const threshold = itemsRect.width / duplicateCount;

      if (
        (isReverse && scrollAmount <= -threshold) ||
        (!isReverse && scrollAmount >= threshold)
      ) {
        const adjustment = isReverse ? threshold : -threshold;
        scrollAmount += adjustment;
        bgPosition += adjustment;
      }

      requestAnimationFrame(scrollStep);
    };

    scrollStep();
  };

  startScrolling();
});
body {
  margin: 0;
  padding: 0;
  font-family: "Poppins";
}

.running-lines {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
  -ms-flex-direction: column;
  flex-direction: column;
  row-gap: 0.5rem;
  padding-top: 4rem;
  padding-bottom: 4rem;
}
@media (min-width: 956px) {
  .running-lines {
    row-gap: 1rem;
    padding-top: 3.5rem;
    padding-bottom: 3.5rem;
  }
}
@media (min-width: 1184px) {
  .running-lines {
    row-gap: 1.25rem;
  }
}
.running-lines__wrapper {
  overflow: hidden;
  padding-top: 0.625rem;
  padding-bottom: 0.625rem;
}
.running-lines__items {
  display: inline-block;
  white-space: nowrap;
  background-image: linear-gradient(
    92.55deg,
    #852fff 0%,
    #dd26ed 48.5%,
    #ed2662 100%
  );
  background-size: 100vw;
  -webkit-background-clip: text;
  background-clip: text;
  background-position: left;
  background-repeat: no-repeat;
}
.running-lines__item {
  margin-right: 2rem;
  font-size: 1.25rem;
  line-height: 1.5rem;
  font-style: italic;
  color: transparent;
}
@media (min-width: 956px) {
  .running-lines__item {
    margin-right: 3rem;
    font-size: 1.5rem;
    line-height: 2rem;
  }
}
@media (min-width: 1184px) {
  .running-lines__item {
    margin-right: 4rem;
  }
}
<div class="running-lines">
  <div class="running-lines__wrapper" data-duration="0.4" data-duplicate-count="7">
    <div class="running-lines__items">
      <span class="running-lines__item">Security Kit</span>
      <span class="running-lines__item">Honeypot</span>
      <span class="running-lines__item">Antibot</span>
      <span class="running-lines__item">Flood Control</span>
      <span class="running-lines__item">Move 403 to 404</span>
      <span class="running-lines__item">Rabbit Hole</span>
      <span class="running-lines__item">Username Enumeration Prevention</span>
    </div>
  </div><div class="running-lines__wrapper" data-duration="0.8" data-direction="reverse" data-duplicate-count="7">
    <div class="running-lines__items">
      <span class="running-lines__item">Content Moderation</span>
      <span class="running-lines__item">Workflows</span>
      <span class="running-lines__item">Simple Menu Permissions</span>
      <span class="running-lines__item">Group Node</span>
      <span class="running-lines__item">Scheduler</span>
      <span class="running-lines__item">Media</span>
    </div>
  </div><div class="running-lines__wrapper" data-duration="0.6" data-duplicate-count="7">
    <div class="running-lines__items">
      <span class="running-lines__item">Paragraphs & Paragraphs Browser</span>
      <span class="running-lines__item">Group</span>
      <span class="running-lines__item">Gin & Gin Toolbar</span>
      <span class="running-lines__item">Crop & Image Widget Crop</span>
      <span class="running-lines__item">Migrate & Migrate Plus</span>
    </div>
  </div>
</div>

发布评论

评论列表(0)

  1. 暂无评论