タイピング風のアニメーションを作成

目次

デモ

つれづれなるまゝに、日暮らし、硯にむかひて、心にうつりゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。(Wikipediaより)つれづれなるまゝに、日暮らし、硯にむかひて、心にうつりゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。(Wikipediaより)

解説

使用技術

以下のコードでAI Chatのようなタイピングのアニメーションを作成することができます。

スクリーンリーダーを使用しているユーザーにも情報を正確に伝える必要があるため aria-labelには本文と同じものを指定してください。


<div class="js-container">
  <p class="text-xl leading-relaxed" data-text-type="typing" aria-label="つれづれなるまゝに、日暮らし、硯にむかひて、心にうつりゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。(Wikipediaより)">
    つれづれなるまゝに、日暮らし、硯にむかひて、心にうつりゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。(Wikipediaより)つれづれなるまゝに、日暮らし、硯にむかひて、心にうつりゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。(Wikipediaより)
  </p>
</div>

またanimationのCSSは独自のものを使用するのでtailwind.cofig.jsには以下の記述を追加してください

export default {
  theme: {
    keyframes: {
      blink: {
        "0%, 100%": { opacity: 1 },
        "50%": { opacity: 0 },
      },
    },
    // カスタムアニメーションの設定
    animation: {
      blink: "blink 600ms steps(1) infinite",
    },
  },
};

テキストをマスクしてタイピング風のアニメーションも作成できるのですが、カーソルを1文字づつ横に移動させるようなアニメーションを実装しようと思うとCSSだけでは難しかったのでJSを使用しています。 テキストを配列に格納し、1文字ずつ表示するようにしています。 1文字表示後末尾にカーソルを移動させるようにしています。

// JavaScript
  window.addEventListener("DOMContentLoaded", () => {
    const pauseStart = 100;
    const typeSpeed = 30;
    const textContainer = document.querySelector(".js-container");
    const textElement = textContainer.querySelector(
      '[data-text-type="typing"]',
    );

    // テキストを配列に格納

    if (!textElement || !textElement.textContent) return;

    const textArray = textElement.textContent.split("");

    // 文字を非表示にする
    textElement.textContent = "";

    // 1文字ずつ表示する
    let i = 0;
    setTimeout(() => {
      const interval = setInterval(() => {
        if (i >= textArray.length) {
          setTimeout(() => {
            clearInterval(interval); // アニメーション完了時にclearIntervalでインターバルを停止
            // ここに、アニメーション完了後に削除したい要素を消去する処理を追加
            const elementToRemove = document.querySelector(".js-cursor");
            elementToRemove?.remove();
            return;
          }, 1500);
        }
        if (i >= textArray.length) {
          clearInterval(interval);
          return;
        }

        textElement.textContent += textArray[i];
        textElement.innerHTML +=
          '<span class="js-cursor animate-blink pointer-events-none absolute inline-block aspect-[1/20] h-[1lh] bg-black align-top duration-1000"></span>';
        i++;
      }, typeSpeed); // 1文字あたりの表示速度を調整
    }, pauseStart);
  });