Search code examples
javascripthtmlcssanimationgradient

Apply Text Color Gradient During Text Output Animation (HTML, CSS, JS) without showing HTML tags


I have a text animation that loads when the page loads. The text will be written to the screen character by character, I want to have some words with a gradient applied. I have been able to successfully apply the gradient, however, it pauses when it first gets to the span tag and then outputs each character with the gradient after the pause (seemingly the length of outputting each character)

I created a CodePen with the closest working version to the requirements. View Code: https://codepen.io/Kelly-Gold/pen/oNPJaYL

I managed to capture a screenshot at the beginning and end of the gradient so you can see the unexpected/unwanted HTML characters When the line is complete, the output is what I desired, but it's not fluid, and shows unwanted characters along the way.

Text when output arrives at span tag enter image description here

Text when output finishes gradient and arrives at closing span tag enter image description here

Text when output reaches end of line enter image description here

I can't figure out how to make it smooth without pausing, and without showing the characters at the beginning and closing of the span tag

Requirements/Desired output

  1. Each line is output character by character, with pause at end of line
  2. Some words can have a gradient
  3. The output does not pause when it arrives at a gradient word, so it should flow just like the regular black text
  4. No HTML tag characters should be visible at runtime

Solution

  • This code does a check to see if the letterCount is at the start of either <span... or </span..., and if so it adjusts letterCount to point to the character following the end of that tag.

    This is by no means perfect. One problem is that when we write out the html from between the opening and closing span tags, we don't include a closing span tag. I also broke the blink thing.

    const lines = [
      "This is the first line",
      "This is the second line with a <span class='gradient'>gradient</span> word",
      "This is the last line with a <span class='gradient'>gradient</span> word",
    ];
    
    
    let lineCount = 0;
    let letterCount = 0;
    let currentText = "";
    let isCursorBlinking = true;
    
    const textElement1 = document.querySelector(".line1");
    const textElement2 = document.querySelector(".line2");
    const textElement3 = document.querySelector(".line3");
    
    function type() {
      if (letterCount === lines[lineCount].length) {
        pauseTyping();
        return;
      }
    
      letterCount++;
      currentText = lines[lineCount];
      
      const startOpenGradient = currentText.indexOf("<span class='gradient'>");
      const endOpenGradient = startOpenGradient + "<span class='gradient'>".length;
      const startCloseGradient = currentText.indexOf("</span>");
      const endCloseGradient = startCloseGradient + "</span>".length;
      
      
      if (letterCount === startOpenGradient)
        letterCount = endOpenGradient;
      else if (letterCount === startCloseGradient)
        letterCount = endCloseGradient;
      
      currentText = lines[lineCount].slice(0, letterCount);
    
        textElement1.innerHTML = currentText;
    
      isCursorBlinking = true;
      textElement3.classList.toggle("blink");
    
      
      
      setTimeout(type, 100);
    }
    
    function pauseTyping() {
      setTimeout(() => {
        letterCount = 0;
        lineCount++;
    
        if (lineCount === lines.length) {
          return;
        }
    
        type();
      }, 1000);
    }
    
    type();
    .text-animation {
      font-size: 24px;
      font-weight: bold;
      color: #000;
      text-align: center;
      margin-top: 50px;
    }
    
    h1 {
      display: inline-block;
    }
    
    .blink {
      animation: blink 0.7s infinite;
    }
    
    @keyframes blink {
      0% {
        opacity: 1;
      }
      50% {
        opacity: 0;
      }
      100% {
        opacity: 1;
      }
    }
    
    .gradient {
      background: linear-gradient(to right, #FFC107, #FF5733);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
    <div class="text-animation">
      <h1 class="line1"></h1>
      <h1 class="line2"></h1>
      <h1 class="line3"></h1>
    </div>