jsとsassで文字を一文字ずつ表示させる
文字をアニメーションするのに、いろいろなパターンがあります。
- 1文字つづ頭から表示
- ランダムに表示
- svgの文字を書いているようにアニメーションする
などなどでしょうか。
今回は1文字ずつ頭から間隔を開けて表示させるひとつの方法をご紹介します。
HTMLとJs
下記のような文字を出力させます。
<p class="main-title">Hello World!</p>
もちろんこれだけでは動かないので、私なりにもどんなjsの書き方があるのか調べたのですが、こちらがわかりやすかったのでアレンジしてみました。
const element = document.querySelector('.main-title'); const elementText = element.textContent; const split = elementText.split(''); const AfterText = ''; for(let i = 0; i < split.length; i++){ AfterText += '<span>' + split[i] + '</span>'; } element.innerHTML = AfterText;
上の説明は以下のようになります。
区切った後のhtmlはこのようになりました。
前後の空白を取り除くtrim()を使用して、
const split = elementText.trim().split(”);なんてしてもよいと思います。
それと後述するreduceメソッドでもspanタグは囲むことができます。
Sassの設定
これを時間差で回していこうと思います。
Sassだとfor文があるのでとても便利です。
@keyframes randomText { from { opacity: 0; } to { opacity: 1; } } .main-title span{ font-size: 36px; opacity: 0; animation-name: randomText; @for $i from 1 through 16 { &:nth-of-type(#{$i}) { animation-delay: .1s * $i; animation-duration:.4s; animation-fill-mode: forwards; } } }
randomTextというkeyframesを作り、文字が見えない状態から見える状態に設定します。
その設定をmain-titleに設定しています。
for文ですが、
@for $i from 1 through 16 { &:nth-of-type(#{$i}) { animation-delay: .1s * $i; animation-duration:.4s; animation-fill-mode: forwards; } } }
ここの部分で文字の2文字目から16文字目までを回します。
(「hello world!」は16文字ではないですがスルーしてください)
終わりの文字を16文字としたのですが、もしかしたらその文字数を取得するコードがあるかもしれませんが、短い文字なので私は数えて入力しました。
nth-of-type(#{$i}) とう書き方について
nth-of-typetとは擬似クラスと呼ばれて、兄弟要素の何番目かを指定する方法です。
今回の例だと、spanタグの1番目から16番が()の中に入ります。
実際はnth-of-type(1)のような感じですね。
()の中は(i)とは直接書けなくて、#{…}といように鉤括弧と#で囲むことになるのですが、その書き方をインターポレーションといいます。
$i は1,2,3・・・と増えていくので、animation-delayの 0.1秒にひとつづつ掛けていって、0.1s、0.2s、0.3s・・・と表示される速度を一文字づつ遅くしていきます。
animation-fill-mode:forwardsにしたのは、最初opacity:0にしてあるので一旦表示されてもすぐ消えてしまったからです。
reduceを使う
配列で使うreduceはちょっとスマートな印象がある関数です。
こちらでも文字spanタグで囲うことができます。
reduce(コールバック関数、積み重なった値、現在の値、インデックス、対象の配列)
これら4つの引数を取ることができます。
使い方なのですが、言葉で説明するよりも結果を見た方がわかりやすいかもしれません。
mdnのreduceに関するページの例を引用します。
const array = [15, 16, 17, 18, 19]; function reducer(accumulator, currentValue, index) { const returns = accumulator + currentValue; console.log( `accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`, ); return returns; } array.reduce(reducer);
上記のreducerという関数の引数ですが、
- accumulator → 積み重なった値(名付け方が上手ではありませんが・・・)
- currentValue →現在の値
- index →インデックス番号になります。
「積み重なった値」「現在の値」「インデックス」「積み重なった値+現在の値」の順で表示される関数です。
これがどのように表示されるかというと以下のようになります。
1列目は、最初の配列の値15、積み重なった値16、インデックス 1、合計値31。
2列目は、前の合計値31が積み重なった値になり、現在地が配列の2番目の値になります。
2つめなのでインデックスは2。
そして31 + 17の合計は48です。
同様に3列目も処理されます。
積み重なった値に48が入ります。
そして配列の3番目が現在値の18なので、その合計値は48+18=66。
このように値が積み重なっていく感じに処理されます。
文字をspanタグで1文字ずつ囲う
それでは、splitで1文字ずつ分けたように、reduceでもやってみることにします。
<p class="main-title_02">Hello world!</p>
javascriptはこのように書きました。
const title = document.querySelector('.main-title_02'); const splitTitle = title.textContent.split(''); title.innerHTML = splitTitle.reduce(function(accu,curr) { return `${accu}<span>${curr}</span>`; });
文字をとってきて、splitで分割するまでは同じです。
そこからspanタグで1文字ずつ囲います。
ここでは2つの引数があり、ひとつめはaccuで積み重なった値。2つめはcurrで現在の値です。
結果は以下のようにになります。
spanタグで囲われてますね!
これも1文字目からspanで囲まれた文字が、2文字目も、3文字目も・・・とどんどん積み重なっていくからです。数字を合計していって合計値が積み重なった値にaccumlationになるのと同じです。
spanタグで囲まれた文字もどんどん積み重なっていくんですね。
しかしここでもう一度、上の画像をご覧になってほしいのですが、
最初の文字「H」がspanタグで囲われていません。
どうすればよいかというと、まず結果を書くと以下のようになります。
const title = document.querySelector('.main-title_02'); const splitTitle = title.textContent.split(''); title.innerHTML = splitTitle.reduce(function(accu,curr) { return `${accu}<span>${curr}</span>`; },"");
最初と99%同じなのですが、違うところと言えば1番最後カギカッコで閉じる直前に初期値””を加えただけです。
最初のHからspanタグで囲われてますね!
初期値が与えられていれば、それを現在値として処理することになっています。
最初は初期値を設定していなかったので、2番目から処理されていたんですね。
最初はreduce関数を理解するのに苦労したのですが、わかってしまえばとても便利な関数だと気づきます。
アニメーションでもガンガン使ってみて下さい。