实现打字机效果

打字机效果

效果预览

源码演示

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      :root {
        font-size: 20px;
      }

      body {
        display: flex;
        justify-content: center;
        align-items: center;
        min-height: 100vh;
      }

      h1 {
        margin: 0;
        padding: 0;
        /* 必须是等宽字体 */
        font-family: monospace;
        /* 由于是等宽字体,1ch 等于 任何数字、英文、符号的宽度 */
        /* width: 1ch; */
        position: relative;
        /* overflow: hidden; */
        /* animation: 1s typing forwards steps(13); */
      }

      h1::after {
        content: "";
        display: inline;
        position: absolute;
        width: 5px;
        height: 2ch;
        background-color: #000;
        border-radius: 2px;
        right: -0.5ch;
      }

      h1.ended::after {
        animation: 1.1s cursor steps(2, jump-none) infinite;
      }

      h1 span {
        --delay: 10s;
        display: inline-block;
        overflow: hidden;
        width: 0ch;
        animation: 0.1s text-in ease-in-out forwards;
        /* animation: 0.1s text-out ease-in-out forwards; */
        animation-delay: var(--delay);
      }

      @keyframes text-in {
        from {
          width: 0ch;
        }

        to {
          width: 2ch;
        }
      }

      @keyframes text-out {
        from {
          width: 2ch;
        }

        to {
          width: 0ch;
        }
      }

      @keyframes cursor {
        from {
          opacity: 0;
        }

        to {
          opacity: 1;
        }
      }
    </style>
  </head>

  <body>
    <script>
      let strs = [
        {
          title: "没有梦想,和咸鱼有什么区别",
          stop: 5,
        },
        {
          title: "没有什么能够顾阻你,前进的步伐",
          stop: 10,
          // stop: [4, 13]
        },
      ];
      // 当前进行到第几行
      let currentIndex = 0;
      let h1 = null;
      let spans = null;

      setTimeout(() => {
        h1 = document.createElement("h1");
        document.body.appendChild(h1);

        init();
      }, 2000);

      function init() {
        if (currentIndex == strs.length) {
          currentIndex = 0;
        }
        h1.innerHTML = strs[currentIndex].title;
        h1.innerHTML = h1.textContent.replace(/\S/g, "<span>$&</span>");

        let delay = 0;
        spans = [...document.querySelectorAll("span")];
        spans.forEach((span, i) => {
          delay += 0.1;
          if (strs[currentIndex].stop instanceof Array) {
            if (strs[currentIndex].stop.includes(i)) {
              delay += 0.3;
            }
          } else {
            if (strs[currentIndex].stop == i) {
              delay += 0.3;
            }
          }

          span.style.setProperty("--delay", `${delay}s`);
        });

        h1.addEventListener("animationend", lastEnd);
      }

      function lastEnd(e) {
        if (e.target == document.querySelector("h1 span:last-child")) {
          h1.classList.add("ended");
          setTimeout(() => {
            h1.removeEventListener("animationend", lastEnd);
            let delay = 0;

            spans.reverse().forEach((span, i) => {
              h1.classList.remove("ended");
              span.style.width = "2ch";
              span.style.animation = "0.1s text-out ease-in-out forwards";
              delay += 0.05;
              // 回去停顿功能
              // if (strs[currentIndex].stop instanceof Array) {
              //   if (strs[currentIndex].stop.includes(spans.length - i)) {
              //     delay += 0.3
              //   }
              // } else {
              //   if (strs[currentIndex].stop == spans.length - i) {
              //     delay += 0.3
              //   }
              // }
              span.style.animationDelay = `${delay}s`;
            });
            h1.addEventListener("animationend", firstEnd);
          }, 2000);
        }
      }

      function firstEnd(e) {
        if (e.target == document.querySelector("h1 span:first-child")) {
          spans.forEach((item) => {
            item.remove();
          });
          h1.removeEventListener("animationend", firstEnd);
          currentIndex++;
          // h1.classList.add('ended')
          // h1.classList.remove('ended')
          init();
        }
      }
    </script>
  </body>
</html>


posted @ 2021-12-05 20:40  博客zhu虎康  阅读(106)  评论(0编辑  收藏  举报