分割テキスト アニメーションの作成

分割文字と単語のアニメーションを作成する方法の基本的な概要。

この記事では、最小限でアクセス可能で、ブラウザ間で動作するウェブ向けの分割テキスト アニメーションとインタラクションを解決する方法について考えを共有します。デモをお試しください。

デモ

動画で確認したい場合は、YouTube 版の投稿をご覧ください。

概要

分割テキスト アニメーションは素晴らしい効果を発揮します。この記事では、アニメーションの可能性のほんの一部しか紹介しませんが、今後の開発の基盤となる内容です。目標は、段階的にアニメーション化することである。テキストはデフォルトで読みやすく、アニメーションがその上に構築されている必要があります。分割テキストのモーション エフェクトは派手になり、邪魔になる可能性があるため、HTML のみを操作するか、ユーザーがモーションを許可している場合にのみモーション スタイルを適用します。

ワークフローと結果の概要は次のとおりです。

  1. CSS と JS の低モーション条件変数を準備します。
  2. JavaScript で分割テキスト ユーティリティを準備します。
  3. ページの読み込み時に条件とユーティリティをオーケストレートします。
  4. 文字や単語の CSS 遷移とアニメーションを記述します(これが楽しい部分です)。

条件付きの結果のプレビューを次に示します。

要素パネルが開いていて、モーションの軽減が [軽減] に設定され、h1 が分割されていない状態の Chrome デベロッパー ツールのスクリーンショット
ユーザーはモーションの低減を好む: テキストが読みやすく、分割されていない

ユーザーがモーションの低減を希望する場合は、HTML ドキュメントをそのままにして、アニメーションを行いません。動きが問題ない場合は、動画を分割します。以下は、JavaScript によってテキストが文字ごとに分割された後の HTML のプレビューです。

要素パネルが開いていて、モーションの軽減が [軽減] に設定され、h1 が分割されていない状態の Chrome デベロッパー ツールのスクリーンショット
ユーザーはモーションに問題なし。テキストが複数の <span> 要素に分割されている

モーション条件の準備

このプロジェクトでは、便利な @media (prefers-reduced-motion: reduce) メディアクエリを CSS と JavaScript で使用します。このメディアクエリは、テキストを分割するかどうかを決定するための主な条件です。CSS メディアクエリは遷移とアニメーションの保持に使用され、JavaScript メディアクエリは HTML 操作の保持に使用されます。

CSS 条件の準備

PostCSS を使用して メディアクエリ レベル 5 の構文を有効にしました。これにより、メディアクエリのブール値を変数に格納できます。

@custom-media --motionOK (prefers-reduced-motion: no-preference);

JS 条件の準備

JavaScript では、ブラウザでメディアクエリを確認する方法が用意されています。ここでは、デストラクチャリングを使用して、メディアクエリチェックからブール値の結果を抽出し、名前を変更しました。

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

motionOK をテストし、ユーザーがモーションの低減をリクエストしていない場合にのみドキュメントを変更できます。

if (motionOK) {
  // document split manipulations
}

PostCSS を使用して ネスト ドラフト 1@nest 構文を有効にすると、同じ値を確認できます。これにより、アニメーションに関するすべてのロジックと、親と子のスタイル要件を 1 か所に保存できます。

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

PostCSS カスタム プロパティと JavaScript ブール値を使用して、エフェクトを条件付きでアップグレードできます。次のセクションでは、文字列を要素に変換する JavaScript を分解します。

テキストの分割

テキストの文字、単語、行などを CSS または JS で個別にアニメーション化することはできません。この効果を実現するには、ボックスが必要です。各文字をアニメーション化するには、各文字を要素にする必要があります。各単語をアニメーション化するには、各単語を要素にする必要があります。

  1. 文字列を要素に分割する JavaScript ユーティリティ関数を作成する
  2. これらのユーティリティの使用をオーケストレートする

文字を分割するユーティリティ関数

最初は、文字列を受け取って各文字を配列で返す関数から始めてみましょう。

export const byLetter = text =>
  [...text].map(span)

ES6 のスプレッド構文は、このタスクを迅速に行ううえで非常に役立ちました。

単語分割ユーティリティ関数

文字列を分割する場合と同様に、この関数は文字列を受け取り、各単語を配列で返します。

export const byWord = text =>
  text.split(' ').map(span)

JavaScript 文字列の split() メソッドを使用すると、スライスする文字を指定できます。単語の分割を示す空白スペースを渡しました。

ボックスを作成するユーティリティ関数

このエフェクトには文字ごとにボックスが必要です。これらの関数では、span() 関数で map() が呼び出されています。span() 関数は次のとおりです。

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

--index というカスタム プロパティが配列の位置で設定されていることに注意してください。文字アニメーション用のボックスは便利ですが、CSS で使用するインデックスは、小さな追加のように見えても大きな影響があります。この大きな影響の中で最も注目すべきは、段階的であることです。--index を使用して、アニメーションをオフセットしてずらした外観にすることができます。

ユーティリティのまとめ

完成した splitting.js モジュール:

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

export const byLetter = text =>
  [...text].map(span)

export const byWord = text =>
  text.split(' ').map(span)

次に、これらの byLetter() 関数と byWord() 関数をインポートして使用します。

分割オーケストレーション

分割ユーティリティが使用可能になったら、すべてをまとめます。

  1. 分割する要素の検出
  2. 分割してテキストを HTML に置き換える

その後、CSS が要素やボックスのアニメーション化を引き継ぎます。

要素の検出

属性と値を使用して、目的のアニメーションとテキストの分割方法に関する情報を保存することにしました。これらの宣言型オプションを HTML に配置するのは便利です。属性 split-by は、JavaScript から要素を見つけて、文字または単語のボックスを作成する場合に使用します。属性 letter-animation または word-animation は、CSS から要素の子要素をターゲットにし、変換とアニメーションを適用するために使用されます。

次の HTML の例は、2 つの属性を示しています。

<h1 split-by="letter" letter-animation="breath">animated letters</h1>
<h1 split-by="word" word-animation="trampoline">hover the words</h1>

JavaScript から要素を検索する

属性の存在を確認する CSS セレクタ構文を使用して、テキストを分割する要素のリストを収集しました。

const splitTargets = document.querySelectorAll('[split-by]')

CSS から要素を検索する

また、CSS の属性存在セレクタを使用して、すべての文字アニメーションに同じベーススタイルを適用しました。後で、属性値を使用してより具体的なスタイルを追加し、効果を実現します。

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

テキストの分割

JavaScript で検出された分割ターゲットごとに、属性の値に基づいてテキストを分割し、各文字列を <span> にマッピングします。次に、要素のテキストを作成したボックスに置き換えます。

splitTargets.forEach(node => {
  const type = node.getAttribute('split-by')
  let nodes = null

  if (type === 'letter') {
    nodes = byLetter(node.innerText)
  }
  else if (type === 'word') {
    nodes = byWord(node.innerText)
  }

  if (nodes) {
    node.firstChild.replaceWith(...nodes)
  }
})

オーケストレーションの結論

完了時の index.js:

import {byLetter, byWord} from './splitting.js'

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

if (motionOK) {
  const splitTargets = document.querySelectorAll('[split-by]')

  splitTargets.forEach(node => {
    const type = node.getAttribute('split-by')
    let nodes = null

    if (type === 'letter')
      nodes = byLetter(node.innerText)
    else if (type === 'word')
      nodes = byWord(node.innerText)

    if (nodes)
      node.firstChild.replaceWith(...nodes)
  })
}

この JavaScript は、次のように読み取ることができます。

  1. ヘルパー ユーティリティ関数をインポートします。
  2. このユーザーがモーションに問題がないか確認します。問題がある場合は何もしません。
  3. 分割する要素ごとに。
    1. 分割方法に基づいて分割します。
    2. テキストを要素に置き換えます。

アニメーションと遷移を分割する

上記のドキュメント分割操作により、CSS または JavaScript でさまざまなアニメーションや効果を実現できるようになりました。分割の可能性を探る際に役立つリンクを、この記事の下部にいくつか示します。

さっそく、この機能を使ってできることをお見せしましょう。CSS ドリブンのアニメーションと遷移を 4 つご紹介します。🤓?

文字を分割する

分割文字エフェクトの基盤として、次の CSS が役立ちました。すべての遷移とアニメーションをモーション メディア クエリの後に配置し、各新しい子文字 span にディスプレイ プロパティと、空白文字の処理方法を指定するスタイルを設定します。

[letter-animation] > span {
  display: inline-block;
  white-space: break-spaces;
}

空白文字のスタイルは、スペースのみのスパンがレイアウト エンジンによって折りたたまれないようにするために重要です。次に、ステートフルな楽しい機能について説明します。

移行分割通知書の例

この例では、CSS 遷移を使用して分割テキスト効果を実現しています。遷移では、エンジンがアニメーション化する状態が必要です。私は、ホバーなし、文中のホバー、文字のホバーの 3 つの状態を選択しました。

ユーザーが文(コンテナ)にカーソルを合わせると、ユーザーが遠ざけたかのように、すべての子要素が縮小されます。ユーザーが文字にカーソルを合わせると、その文字を前面に表示します。

@media (--motionOK) {
  [letter-animation="hover"] {
    &:hover > span {
      transform: scale(.75);
    }

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:hover {
        transform: scale(1.25);
      }
    }
  }
}

分割文字のアニメーションの例

この例では、事前定義された @keyframe アニメーションを使用して各文字を無限にアニメーション化し、インライン カスタム プロパティ インデックスを利用してずらす効果を作成します。

@media (--motionOK) {
  [letter-animation="breath"] > span {
    animation:
      breath 1200ms ease
      calc(var(--index) * 100 * 1ms)
      infinite alternate;
  }
}

@keyframes breath {
  from {
    animation-timing-function: ease-out;
  }
  to {
    transform: translateY(-5px) scale(1.25);
    text-shadow: 0 0 25px var(--glow-color);
    animation-timing-function: ease-in-out;
  }
}

単語の分割

これらの例では、Flexbox がコンテナ タイプとして機能し、ch 単位を適切なギャップ長としてうまく活用しています。

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
単語間のギャップを示す Flexbox デベロッパーツール

遷移分割単語の例

この遷移の例では、ホバーを再度使用します。この効果は最初、ホバーするまでコンテンツを非表示にするため、デバイスにホバー機能がある場合にのみ、インタラクションとスタイルが適用されるようにしました。

@media (hover) {
  [word-animation="hover"] {
    overflow: hidden;
    overflow: clip;

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:not(:hover) {
        transform: translateY(50%);
      }
    }
  }
}

分割単語をアニメーション化する例

このアニメーションの例では、CSS @keyframes を使用して、通常の段落のテキストにずらして無限のアニメーションを作成します。

[word-animation="trampoline"] > span {
  display: inline-block;
  transform: translateY(100%);
  animation:
    trampoline 3s ease
    calc(var(--index) * 150 * 1ms)
    infinite alternate;
}

@keyframes trampoline {
  0% {
    transform: translateY(100%);
    animation-timing-function: ease-out;
  }
  50% {
    transform: translateY(0);
    animation-timing-function: ease-in;
  }
}

まとめ

私の方法をご覧になったところで、あなたならどうしますか?🙂

アプローチを多様化し、ウェブで構築するすべての方法を学びましょう。Codepen を作成するか、独自のデモをホストして、そのデモをツイートしてください。デモを以下のコミュニティ リミックスのセクションに追加します。

ソース

その他のデモとアイデア

コミュニティ リミックス