First of all, what is a typewriter effect? A picture is worth a thousand words. Here you are:

The typewriter effect is to output text one by one, which is essentially a Web animation. In Web applications, there are many ways to achieve the animation effect. In JavaScript, you can use setTimeout timer to achieve it, cSS3 can use Transition and animation to achieve it, and html5 canvas can also achieve it. In addition, html5 provides a dedicated API for request animation called requestAnimationFrame (rAF), which literally means “requestAnimationFrame.” Next, let’s look at several implementations of the typewriter effect. I’m going to try to do it in a simple way to make it easier to understand, but if you’re interested, you can make it a little bit more compelling and artistic, because programming is an art.

Typewriter effect N kinds of realization

Implement 1: setTimeout()

The implementation of the setTimeout version is as simple as cutting up the text to be displayed, using the timer to continuously append text to the DOM element, and using the ::after pseudo-element to create a cursor flicker behind the DOM element. The code and renderings are as follows:

<! - style -- -- >
<style type="text/css">
  /* Set the container style */
  #content {
    height: 400px;
    padding: 10px;
    font-size: 28px;
    border-radius: 20px;
    background-color: antiquewhite;
  }
  /* Create a blinking cursor effect */
  #content::after{
      content: '|';
      color:darkgray;
      animation: blink 1s infinite;
  }
  @keyframes blink{
      from{
          opacity: 0;
      }
      to{
          opacity: 1; }}</style>

<body>
  <div id='content'></div>
  <script>
    (function () {
    // Get the container
    const container = document.getElementById('content')
    // Cut all the text that needs to be displayed
    const data = 'The simplest typewriter effect to achieve'.split(' ')
    // The literal subscript that needs to be appended to the container
    let index = 0
    function writing() {
      if (index < data.length) {
        // Append text
        container.innerHTML += data[index ++]
        let timer = setTimeout(writing, 200)
        console.log(timer) // Print 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
      }
    }
    writing()
  })();
  </script>
</body>
Copy the code

The return value of the setTimeout() method is a unique value (ID). In the code above, we also printed the return value of setTimeout(), so what is the use of this value? If you want to terminate the setTimeout() method, you must do so using clearTimeout(), and when you use this method, The system must know which setTimeout() method you want to terminate (because you may have called several setTimeout() methods at the same time), so clearTimeout() requires an argument, which is the return value of setTimeout(). Use this value to uniquely determine which setTimeout() method to terminate.

Implement 2: setInterval()

SetInterval to achieve the typewriter effect, in fact, in MDN window.setInterval case 3 has a, but also to achieve the play, pause and terminate the control, the effect can be click here to view, here is only one of the simplest implementation of setInterval typewriter effect, In fact, the code is similar to the previous setTimeout implementation, and the effect is the same.

(function () {
  // Get the container
  const container = document.getElementById('content')
  // Cut all the text that needs to be displayed
  const data = 'The simplest typewriter effect to achieve'.split(' ')
  // The literal subscript that needs to be appended to the container
  let index = 0
  let timer = null
  function writing() {
    if (index < data.length) {
      // Append text
      container.innerHTML += data[index ++]
      // Yes, you can also cancel setInterval execution by clearTimeout
      // index === 4 && clearTimeout(timer)
    } else {
      clearInterval(timer)
    }
    console.log(timer) // This will print 1, 1, 1, 1...
  }
  // When using setInterval, do not forget to clearInterval after the end
  timer = setInterval(writing, 200)
})();
Copy the code

Like setTimeout, setInterval returns an ID (number) that can be passed to clearInterval() or clearTimeout() to cancel the execution of the timer.

It is important to emphasize here that the time interval specified by the ** timer indicates when the timer’s code is added to the message queue, not when the code is executed. ** So the actual execution time of the code is not guaranteed and depends on when it is picked up by the main thread’s event loop and executed.

Implementation 3: requestAnimationFrame()

In the implementation of animation, requestAnimationFrame has undoubtable advantages over setTimeout and setInterval. Let’s first look at the typewriter-effect requestAnimationFrame implementation:

(function () {
    const container = document.getElementById('content')
    const data = The biggest advantage of requestAnimationFrame over setTimeout is that the system decides when to execute the callback function. In particular, the callback function in the requestAnimationFrame is invoked before each drawing. If the system's drawing rate is 60Hz, the callback function is executed every 16.7ms. If the system's drawing rate is 75Hz, the callback function is executed every 16.7ms. Then the interval becomes 1000/75=13.3ms. In other words, the requestAnimationFrame executes at the same pace as the system's drawing frequency. It ensures that the callback function is executed only once each time the screen is drawn, so that no frames are lost or the animation gets stuck. '.split(' ')
    let index = 0
    function writing() {
      if (index < data.length) {
        container.innerHTML += data[index ++]
        requestAnimationFrame(writing)
      }
    }
    writing()
  })();
Copy the code

The biggest advantage of requestAnimationFrame over setTimeout is that the system decides when to execute the callback function. Specifically, if the screen refresh rate is 60Hz, then the callback function is executed every 16.7ms. If the refresh rate is 75 hz, then the interval becomes 1000/75=13.3ms. In other words, the requestAnimationFrame keeps pace with the system refresh. It ensures that the callback function is executed only once per screen refresh interval, so that no frames are lost or the animation gets stuck.

Implementation 4: CSS3

In addition to the above three JS methods, we can actually use CSS to achieve the typewriter effect. The idea is to use CSS3’s @keyframes to constantly change the width of the container that contains the text, and hide the text beyond the container.

<style>
  div {
    font-size: 20px;
    /* The initial width is 0 */
    width: 0;
    height: 30px;
    border-right: 1px solid darkgray;
    /* Steps(
       
        , 
        
         ) Steps The second argument, which is optional, accepts the values start and end and specifies whether the step change occurs at the start or end of each interval. The default is end. * /
        
    animation: write 4s steps(14) forwards,
      blink 0.5 s steps(1) infinite;
      overflow: hidden;
  }

  @keyframes write {
    0% {
      width: 0;
    }

    100% {
      width: 280px; }}@keyframes blink {
    50% {
      /* transparent is shorthand for black, a value like rgba(0,0,0,0). * /
      border-color: transparent; / * # 00000000 * /}}</style>

<body>
  <div>To the east of the river, the waves washed all the heroes of the ages</div>
</body>
Copy the code

The principle of the above CSS typewriter effect is clear:

  • The initial text is all on the page, but the width of the container is 0, set the text beyond the part hidden, and then keep changing the width of the container;
  • Set up theborder-rightAnd change on the key frameborder-colortransparent, the right border is like a blinking cursor.

Implementation 5: Typed. Js

Typed.js is a library that types. Enter in any string, and watch it type at the speed you’ve set, backspace what it’s typed, and begin a new sentence for however many strings you’ve set.

Typed. Js is a lightweight typing animation library that allows you to create cool typewriter effects on your project with just a few lines of code. (The first GIF of this article is Typed. Source code is relatively simple, if you are interested, you can go to GitHub to study.

<! DOCTYPEhtml>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
</head>

<body>
  <div id="typed-strings">
    <p>Typed.js is a <strong>JavaScript</strong> library.</p>
    <p>It <em>types</em> out sentences.</p>
  </div>
  <span id="typed"></span>
</body>
<script>
  var typed = new Typed('#typed', {
    stringsElement: '#typed-strings'.typeSpeed: 60
  });
</script>

</html>
Copy the code

Using Typed. Js, we can also easily control animation start, pause, etc. :

<body>
  <input type="text" class="content" name="" style="width: 80%;">
  <br>
  <br>
  <button class="start">start</button>
  <button class="stop">suspended</button>
  <button class="toggle">switch</button>
  <button class="reset">reset</button>
</body>
<script>
const startBtn = document.querySelector('.start');
const stopBtn = document.querySelector('.stop');
const toggleBtn = document.querySelector('.toggle');
const resetBtn = document.querySelector('.reset');
const typed = new Typed('.content', {strings: ['Rain over the white heron state, nostalgia tongjak building, the setting sun dyed quiet grass, several degrees of red, swaying the far sail on the river, looking back at the lights like flowers, not language people first shame. '].typeSpeed: 200.startDelay: 100.loop: true.loopCount: Infinity.bindInputFocusEvents:true
});
startBtn.onclick = function () {
  typed.start();
}
stopBtn.onclick = function () {
  typed.stop();
}
toggleBtn.onclick = function () {
  typed.toggle();
}
resetBtn.onclick = function () {
  typed.reset();
}
</script>
Copy the code

Resources: Typed. | js website (Typed, js, making the address

Of course, the realization of the typewriter effect, is not limited to the above mentioned several methods, the purpose of this article, is not to search for all the typewriter effect, if that will be meaningless. Next, we’ll do some comparisons between CSS3 animation and JS animation, and summarize some of the details of setTimeout, setInterval, and requestAnimationFrame.

CSS3 animation and JS animation comparison

Regarding CSS animation and JS animation, there is a saying that CSS animation is smoother than JS, in fact, this fluidity is a prerequisite. We take this opportunity to CSS3 animation and JS animation for a simple comparison.

JS animation

  • Advantages:
    • JS animation control ability is strong, can be in the process of animation playback animation fine control, such as start, pause, terminate, cancel;
    • JS animation effect is richer than CSS3 animation, with a wide range of functions, such as curve movement, impact flicker, parallax scrolling and other effects that CSS is difficult to achieve;
    • JS animation has no compatibility problems in most cases, while CSS3 animation has compatibility problems;
  • Disadvantages:
    • JS is run in the main thread of the browser, and there are other JS scripts, style calculation, layout, drawing tasks that need to be run in the main thread, which interference may cause the thread to block, resulting in the loss of frames;
    • JS animations often require frequent manipulation of THE DOM’s CSS properties to achieve visual animation effects. At this time, the browser has to constantly perform redraws and rearrangements, which can be very costly for performance, especially on the mobile side, where the browser’s memory allocation is less generous.

CSS 3 animation

  • Advantages:
    • In some cases, the browser can optimize the animation (for example, create a new layer for running the animation). In some cases, there are conditions:
      • In chromium-based browsers
      • The CSS animation does not trigger the layout or paint. When the CSS animation or JS animation triggers the paint or layout, the main thread needs to redo the Layer tree. In this case, the CSS animation or JS animation will block the subsequent operations.
    • Some effects can be enforced using hardware acceleration (improved animation performance via GPU)
  • Disadvantages:
    • The code is verbose. If you implement a slightly more complex CSS animation, your CSS code can get pretty clunky;
    • The operation process control is weak. Css3 animation can only control the animation pause and continue in certain scenes, can not add a callback function in a specific location.

Main thread and Compositor Thread

  • The render thread is divided into the Main thread and the Compositor thread. The main thread maintains a Layer tree (LayerTreeHost) that manages the TiledLayer, and the Compositor thread maintains the same LayerTreeHostImpl that manages the LayerImpl. The contents of the two trees are copied. The Compositor Thread can render with LayerTreeHostImpl while the main thread is manipulating the LayerTreeHost. When Javascript is busy and the main thread gets stuck, the synthesis to the screen is smooth.
  • To prevent false death, mouse and keyboard messages are first distributed to the Compositor Thread and then to the main Thread. This way, the Compositor Thread can still respond to a portion of the messages when the main thread is busy. For example, if the mouse is scrolling, the Compositor Thread will also process the scrolling messages if the main Thread is busy. Scroll through the parts of the page that have been submitted (the parts that have not been submitted will be whitewashed).

CSS animations are smoother than JS animations

  • CSS animations are rare or do not trigger pain and layout, i.e. redraw and rearrange. For example, by changing the following properties to generate a CSS animation, the entire CSS animation is completed in the Compositor Thread (while the JS animation is executed in the Main thread, triggering the Compositor to proceed to the next step) :
    • Backface-visibility: This property specifies whether the element is visible when the back of the element is oriented toward the observer (3D, experimental function);
    • Opacity: Sets the opacity level of div elements;
    • Perspective sets the element view, which only affects the 3D transformation element;
    • Perspective-origin: This property allows you to change the bottom position of a 3D element;
    • Transform: This property applies to the 2D or 3D transformation of the element. This property allows you to rotate, scale, move, tilt, and so on.
  • While javascript performs some expensive tasks, the main Thread is busy, and CSS animations are kept smooth by using the Compositor Thread.
  • Some properties enable 3D acceleration and GPU hardware acceleration, such as when using Transform’s translateZ.
  • By setting thewill-changeAttributes, the browser can know in advance which elements’ attributes are about to change and prepare for them in advance. When the time comes to change elements, they can be implemented immediately. To avoid problems like getting stuck.
    • Don’t apply will-change to too many elements; if overused, it can cause pages to respond slowly or consume too many resources.

    • For example the following code is told in advance rendering engine box element geometry transform and transparency transform operation will be done, then rendering engine will be the element individually implementation frame, such as the transformation occurs, the rendering engine will directly by synthetic thread to processing transformation, the transformation is not involved in the main thread, thus greatly improve the rendering efficiency.

      .box{will-change: transform, opacity; }Copy the code

Summary of setTimeout, setInterval, and requestAnimationFrame

SetTimeout and setInterval

  • The execution time of setTimeout is not determined. In JavaScript, a setTimeout task is placed in an asynchronous queue and is checked to start execution only after the task on the main thread has completed, so the actual execution time of the setTimeout task is usually later than the set time.
  • The refresh rate is affected by the screen resolution and screen size, and the screen drawing rate may vary from device to device. SetTimeout can only set a fixed interval, which is not necessarily the same as the refresh time of the screen.
  • ** The execution of setTimeout only changes an element attribute in memory, which is not updated to the screen until the next time the screen is drawn. If the two are out of step, it may cause the operation of one frame to be skipped and the element of the next frame to be updated. ** If the screen is refreshed every 16.7ms and setTimeout sets the image to move 1px to the left every 10ms, the following drawing process will occur:
    • Ms 0: screen not drawn, waiting, setTimeout not executed, waiting;
    • 10ms: Screen is not drawn, waiting, setTimeout starts and sets element property left=1px;
    • 16.7ms: The screen starts to draw, the elements on the screen move 1px to the left, setTimeout is not executed, continue to wait;
    • 20ms: Screen is not drawn, waiting, setTimeout starts and left=2px;
    • 30 ms: Screen not drawn, waiting, setTimeout starts and left=3px;
    • 33.4 ms: The screen starts to draw, the element on the screen moves 3px to the left, setTimeout does not execute, continue to wait;
    • .

As you can see from the drawing above, the screen does not update the frame left=2px, and the element jumps directly from the position left=1px to the position left=3px. This is called frame loss, which causes the animation to lag.

  • The actual delay between callback calls for setInterval is smaller than the delay set in the code, because the time required for the callback to execute “consumes” part of the interval, and the error becomes larger as the callback is executed longer and more times:
// repeat with the interval of 2 seconds
let timerId = setInterval(() = > console.log('tick', timerId), 2000);
// after 50 seconds stop
setTimeout(() = > {
  clearInterval(timerId);
  console.log('stop', timerId);
}, 50000);
Copy the code

  • Nested setTimeout guarantees a fixed delay:
let timerId = setTimeout(function tick() {
  console.log('tick', timerId);
  timerId = setTimeout(tick, 2000); / / (*)
}, 2000);
Copy the code

requestAnimationFrame

In addition to the advantages of requestAnimationFrame mentioned above, requestAnimationFrame has two other advantages:

  • CPU power saving: Animation implemented with setTimeout, when the page is hidden or minimized, setTimeout is still performing the animation task in the background, because the page is not visible or unavailable at this time, there is no sense to refresh the animation, a complete waste of CPU resources. RequestAnimationFrame, on the other hand, is completely different. When a page is inactive, the screen refresh task of the page is also paused by the system, so the requestAnimationFrame that follows the system will also stop rendering. The animation picks up where it left off, saving CPU overhead.

  • Function throttling: In high frequency events (resize, Scroll, etc.), in order to prevent multiple function executions within a refresh interval, use requestAnimationFrame to ensure that the function is executed only once per refresh interval, which not only ensures smoothness, but also saves the cost of function execution. It makes no sense to execute the function multiple times within a refresh interval, because the display is refreshed every 16.7ms, and multiple draws do not show up on the screen.

About the minimum time interval

  • The 2011 standard”, which states:
    • SetTimeout: If the currently running task is one created by the setTimeout () method and the interval is less than 4ms, increase the interval to 4ms;
    • SetInterval: If the interval is less than 10ms, increase the interval to 10ms.
  • In the latest standard: If the interval is less than 0, the interval is set to 0. If the nesting level is greater than 5 and the time interval is less than 4ms, set the time interval to 4ms.

Timer clearance

  • Since clearTimeout () and clearInterval () clear entries in the same list (active timer list), you can use these two methods to clear timers created by setTimeout () or setInterval ().

The resources

  • HTML 8.6 Timers
  • requestAnimationFrame
  • CSS3 animation and JS animation comparison
  • Scheduling: setTimeout and setInterval