こんにちは、「ふ」です。
テキスト要素を1文字ずつアニメーションさせたい場合。
<p>TEXT</p>
TEXT
<p>などのテキスト要素自体にアニメーションを掛けると、⬆︎のように要素全体がアニメーションしてしまいます。文字単位でアニメーションさせたい場合には、1文字ずつ<span>タグなどでマークアップして参照できるようにする必要があります。
<p> <span>T</span> <span>E</span> <span>X</span> <span>T</span> </p>
⬆︎のようにしてCSSでセレクトし、アニメーションを施すことになります。
しかしながら。手動で1文字ずつマークアップしていくのも骨が折れるというもの💧
そこで今回はJavaScriptを使って自動でテキストを1文字ずつマークアップ→アニメーションさせるところまでやってみましょう。記事の後半では一連の処理を関数化し、汎用性を持たせたサンプルも紹介します。
<p id = "sample">ABCDE</p>
ABCDE
テキスト要素を用意しました。
JavaScriptでテキストを1文字ずつ取得し、<span>〜</span>で囲んでいきます。
「「3//要素を取得」」「「1 ..@1@」」 const sample = document.getElementById("sample"); 「「3//変換後のHTMLをここに配置」」「「1 ..@2@」」 let inner = ""; 「「3//1文字ずつ処理をする」」「「1 ..@3@」」 for(i=0;i<sample.innerText.length;i++) { 「「3//<span>〜</span>で囲み、innerに追加」」「「1 ..@4@」」 inner += `<span>${sample.innerText[i]}</span>` } 「「3//sampleのHTMLを変更」」「「1 ..@5@」」 sample.innerHTML = inner;
@1@ <p>要素を取得。
@2@ 変換後のHTMLコードをいったん、この変数innerに収めます。
@3@ for文で1文字ずつ取り出して処理をしましょう。sample.innerTextで取り出したものは文字列(string)ですが、配列のようにインデックスで1文字ずつ取り出すことができます。
@4@ 取り出した文字を<span>〜</span>で囲み、先ほどの変数innerに追加していきます。
@5@ ループ処理で各文字の変換が終わったら、要素sampleの内容を書き換えます。
console.log(sample); 「「3▶︎ <p id = "sample"> <span>A</span> <span>B</span> <span>C</span> <span>D</span> <span>E</span> </p>」」
処理後の#sampleの内容を出力してみました。
ちゃんと1文字ずつマークアップされていますね。
〽️ クオテーション祭り、さようなら。
マークアップされたそれぞれの文字に渡すアニメーションを、CSSで用意しました。
.each { display:inline-block;「「1 ..@1@」」 animation:each 2s linear infinite; } @keyframes each { 50% {transform:rotate(1turn);} to {transform:rotate(1turn)} }
キーフレームの50%で1回転するアニメーションにしています。
アニメーションを指定するついでに、@1@の部分でdisplayの指定をしています。
<span>はもともとインライン要素です。
インライン要素のテキストは、CSSアニメーションを指定しても動いてくれません。そこでdisplay:inline-blockとし、インラインブロック要素に変更しておきましょう。
アニメーションが用意できたら、「each」クラスをそれぞれの<span>要素に付与していきましょう。
各要素へのアクセスは親要素sampleの子要素(children)として、取り出すことにしました。
for(i=0;i<sample.children.length;i++){ sample.children[i].classList.add("each"); }
ABCDE
各文字が独立してアニメーションしています。
いい感じですね。
せっかくなので(←何が?)、それぞれの文字のアニメーションに時間差をつけてみましょう。
それぞれの<span>要素に、値を変化させたanimation-delayを指定します。
先ほどのfor文にそのまま追記。
for(i=0;i<sample.children.length;i++){ sample.children[i].classList.add("each"); sample.children[i].style.animationDelay = `${i*0.2}s` }
時間差の値には、子要素のindexを利用しました。インデックスは「0,1,2..」と増えていくので、それに0.2を掛けて「0.0,0.2,0.4..」とanimation-delayを指定していきます。
ABCDE
各文字が0.2秒ずつ時間差でアニメーションするようになりました。
「独立感」が引き立ちましたね。
〽️ アニメーションの時間差攻撃。
ここまでの処理を関数定義にし、引数入力のみで「文字ごとのアニメーション」が実装できるようにしてみました。
「「3//要素/クラス/時間差を引数に」」「「1 ..@1@」」
function each_letter(「「5element,cls,delay」」) {
「「3//要素はidで取得。」」「「1 ..@2@」」
const elm = document.getElementById(element);
let inner = "";
for(i=0;i<elm.textContent.length;i++) {
inner += `${elm.textContent[i]}`;
}
elm.innerHTML = inner;
for(i=0;i<elm.children.length;i++) {
elm.children[i].classList.add(cls);
「「3//displayプロパティの変更もここにした。」」「「1 ..@3@」」
elm.children[i].style.display = "inline-block";
「「3//時間差の入力は任意」」「「1 ..@4@」」
if(delay) {
elm.children[i].style.animationDelay = `${i*delay}s`;
}
}
}
@1@ 関数each_letterを作成。引数のところに要素/付与するCSS/時間差を入力できるようにしています。
@2@ アニメーションさせたい要素には、id名をつけて参照させることとします。
displayプロパティの変更処理。先ほどは付与するCSSの中に含めていましたが、それも関数内に組み込んであります。
@4@ 時間差の入力は任意ということにします。そのためdelay処理は条件文を使いました。
その他の処理は、これまでのものと同じです。
要素とアニメーションのCSSを用意し、試してみましょう。
<p id = "auto_ani">ABCDE</p>
要素にはid名をつけておいてください。
.scale { animation:scale 2s linear alternate infinite; } @keyframes scale { from {transform:scale(0.1);} }
アニメーションは、大きさを変化させるものにしました。
それでは関数をつかってみます。
each_letter("auto_ani","scale",0.1);
ABCDE
引数指定した関数を実行するだけで「1文字ずつアニメーション」が実装できました。
これは便利です。
最後までお読みくださり、ありがとうございました。
今後も面白いチュートリアルができたら、紹介していこうと思います。
ではまた〜 ♪
ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。