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

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

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

デモ

動画をご覧になる場合は、この投稿の YouTube バージョンをご覧ください。

概要

分割テキストのアニメーションは素晴らしいものです。この投稿では、アニメーションの可能性についてほとんど触れませんが、土台として役立ちます。目標は、アニメーションを段階的に進めることです。デフォルトでは、テキストが判読可能で、上部にアニメーションが表示されます。分割テキストのモーション効果は、大げさで混乱を招く可能性があるため、HTML を操作するか、ユーザーがモーションを許容できる場合にのみモーション スタイルを適用します。

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

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

以下は、実行する条件付き結果のプレビューです。

[Elements] パネルが開き、軽減されたモーションが [reduce] に設定され、h1 が分割されていない状態になっている、Chrome DevTools のスクリーンショット
動きが少ない方が好まれる: テキストは判読可能で、分割されていない

ユーザーが動きを控えめにしたい場合は、HTML ドキュメントはそのままにして、アニメーションを表示しません。動きに問題がなければ、これを細かく切り分けます。これは、JavaScript がテキストを文字ごとに分割した後の HTML のプレビューです。

[Elements] パネルが開き、軽減されたモーションが [reduce] に設定され、h1 が分割されていない状態になっている、Chrome DevTools のスクリーンショット
ユーザーはモーションを許容(テキストは複数の <span> 要素に分割)

モーション条件の準備

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

CSS 条件の準備

PostCSS を使用して Media Queries Level 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 を使用して Nesting Draft 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 で要素の子をターゲットにして、変換とアニメーションを適用するために使用されます。

次に、2 つの属性を示す HTML のサンプルを示します。

<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 DevTools

トランジション分割単語の例

この遷移の例では、もう一度ホバーを使用します。この効果により、最初はカーソルを合わせるまでコンテンツが非表示になるため、デバイスにホバー機能がある場合のみ操作とスタイルが適用されるようにしました。

@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 を作成するか、独自のデモをホストして、それをツイートしてください。以下のコミュニティ リミックス セクションに追加します。

ソース

その他のデモとアイデア

コミュニティのリミックス