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

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

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

デモ

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

概要

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

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

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

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

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

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

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

モーション条件の準備

このプロジェクトの CSS と JavaScript から、便利な @media (prefers-reduced-motion: reduce) メディアクエリが使用されます。このメディアクエリは、テキストを分割するかどうかを決定するための主な条件です。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 の spread 構文のおかげで、迅速なタスクが可能になりました。

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

文字の分割と同じように、この関数は文字列を受け取り、各単語を配列に返します。

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

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

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

この効果には文字ごとにボックスが必要です。これらの関数では、map()span() 関数で呼び出されています。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 を作成するか独自のデモをホストして、ツイートしてください。以下のコミュニティ リミックスのセクションに追加します。

ソース

その他のデモとアイデア

コミュニティ リミックス