LoginSignup
9
5

More than 3 years have passed since last update.

【CSS】一文字ずつ出現するアニメーション【Animation】

Last updated at Posted at 2019-09-06

はじめに

こういうアニメーションが作りたい

See the Pen xxKPBzw by m_n_r (@m_n_r) on CodePen.

See the Pen NWKwJOL by m_n_r (@m_n_r) on CodePen.

環境

Sass(Sass 記法)
Compass

■ のうごき

白い画面で ■ が現れて、 ■ が消えて、文字が現れる
一文字につき、一つの ■ がある

一文字につき、一つの ■ をつくる

HTML

<div class="fade-in-word__vertical">
    <span>W</span><span>E</span><span>L</span><span>C</span><span>O</span><span>M</span><span>E</span>
</div>

一文字につき、一つの ■ をつけるために span で囲います

<span><span></span>W</span>

■ と文字を連動させるために、span の中に ■ を入れる必要があります
今回は疑似要素で対応できるので、中の span はいりません

<span>W</span>

文字を表示する SASS

.fade-in-word
    font-size: 10px
    &__vertical
        font-size: 4em
        line-height: 1.1em

この状態では文字がちょっと大きく表示されるだけです
アニメーション部分は mixin に書きます

mixin ってなに?

何度も使うスタイルをあらかじめ定義しておくことができます
@include で呼び出して使用します

[CSS]Sassの@mixinとは?使い方もサクッと紹介

引数を渡すこともできるので、関数的な使い方ができちゃいます

一文字につき、一つの ■ をつくる SASS

@mixin fadeInWordY()
    span
        position: relative
        display: inline-block

        &:before
            content: ''
            display: block
            height: 100%
            width: 100%
            background-color: #000
            position: absolute
            top: 0
            left: 0
.fade-in-word
    font-size: 10px
    &__vertical
        font-size: 4em
        line-height: 1.1em
        @include fadeInWordY()

こんな感じのができあがりました

■■■■■■■

一文字につき、一つの ■ を、文字サイズにあわせて覆うことができました

■ を交互に動かす

文字数を渡して関数化

@mixin fadeInWord-animationY($length)

文字数を渡して繰り返し利用できるように mixin にわけます

.fade-in-word
    font-size: 10px
    &__vertical
        font-size: 4em
        line-height: 1.1em
        @include fadeInWordY(7)
@mixin fadeInWordY($length: 0)
    span
        position: relative
        display: inline-block

        &:before
            content: ''
            display: block
            height: 100%
            width: 100%
            background-color: #000
            position: absolute
            top: 0
            left: 0

    @include fadeInWord-animationY($length)

文字数は @include fadeInWordY() で直接指定します

■ を交互にフェードアウトさせる SASS

@mixin fadeInWord-animationY($length, $from: 1)
    $delay: 0

    @for $i from $from through $length
        @if $i%2==0
            span:nth-of-type(#{$i})
                transform-origin: bottom
                &:before
                    transform-origin: bottom
        @else
            span:nth-of-type(#{$i})
                transform-origin: top
                &:before
                    transform-origin: top

        span:nth-of-type(#{$i})
            &:before
                animation: notScaleY 0.3s ease forwards

        $delay: $delay + 0.1
@keyframes notScaleY
    100%
        transform: scaleY(0)

■ がこんな感じで動くようになりました

■■■■■■■

■   ■
W E L ■■■■
    ■

■   ■    ■    ■
W E L C O M E
    ■   ■    ■

待機中の ■ をみえないように

アニメーションが開始する直前(ロード直後)
■■■■■■■
が全部表示されてしまっています

最初は ■ を非表示にしておいて、フェードアウトし始める少し前に表示されるようにしましょう

@mixin fadeInWordY($firstLine: 0, $secondLine: 0)
    span
        position: relative
        display: inline-block
        transform: scaleY(0)
@mixin fadeInWord-animationY($length, $from: 1)
    span:nth-of-type(#{$i})
        animation: scaleY 0.36s ease (0.1s + $delay) forwards
        &:before
            animation: notScaleY 0.3s ease (0.45s + $delay) forwards

うーん・・・なんかちょっとちがう・・・

真骨頂「cubic-bezier」

アニメーションの変化量を細かく調整する

span:nth-of-type(#{$i})
    animation: scaleY 0.36s cubic-bezier(0.84, 0.01, 0.78, 0.13) (0.1s + $delay) forwards
    &:before
        animation: notScaleY 0.3s cubic-bezier(0.11, 1.05, 0.32, 1.03) (0.45s + $delay) forwards

サンプル通りに動くアニメーションができました
設定するとしないで、まったく違う動きになっているように見えます

cubic-bezier ってなに?

の、説明のまえに

CSS animation では次の設定を行うことができます

  • animation-name
  • animation-duration
  • animation-timing-function
  • animation-delay
  • animation-iteration-count
  • animation-direction

この中で animation-timing-function を指定する際に使用しています

animation - CSS: カスケーディングスタイルシート | MDN

cubic-bezier の使い方

アニメーションの変化量をカスタムできます
CSS3のtransition-timing-functionの値、cubic-bezier()に関して

次のような形で値を設定します

animation-timing-function: cubic-bezier(0.11, 1.05, 0.32, 1.03)

変化量を直感的に決められるサイト

細かい数字を自分で調整しなくても、便利なサイトがあります

cube-bezier.com

今の数字は、ここでちょっとずつ調整しながら決めました

2 行でもできるように

1 行目の先頭文字の ■ は下から上に
2 行目の先頭文字の ■ は上から下に

変形がはじまる原点を変える

1 行ずつ mixin を適用する

要素が変形する際の原点は、次のプロパティで設定可能です

transform-origin - CSS: カスケーディングスタイルシート | MDN

先頭文字の transform-origin を、引数で指定するのはどうでしょう

<div class="fade-in-word__vertical-oneline">
    <span>W</span><span>E</span><span>L</span><span>C</span><span>O</span><span>M</span><span>E</span>
</div>
<br>
<div class="fade-in-word__vertical-twoline">
    <span>P</span><span>A</span><span>G</span><span>E</span>
</div>
.fade-in-word
    font-size: 10px
    &__vertical
        font-size: 4em
        line-height: 1.1em
        &-oneline
            @include fadeInWordY(7, top)
        &-twoline
            @include fadeInWordY(7, bottom)

それぞれの要素を div で囲っています
これでも構わないのですが、同じ要素内でないと都合が悪い場合もあります

今回はもともと構造が決まっていたうえで、アニメーションを追加する対応だったので
同じ要素内で 2 行が対応できるようにしました

1 行目と 2 行目を判定する

2 行目の文字数も引数で渡すようにします
2 行目の場合、先頭文字の transform-origin に設定される方向を逆にしました

<div class="fade-in-word__vertical">
    <span>W</span><span>E</span><span>L</span><span>C</span><span>O</span><span>M</span><span>E</span>
    <br>
    <span>P</span><span>A</span><span>G</span><span>E</span>
</div>
.fade-in-word
    font-size: 10px
    &__vertical
        font-size: 4em
        line-height: 1.1em
        @include fadeInWordY(7, 4) // (1 行目の文字数, 2 行目の文字数)
@mixin fadeInWordY($firstLine: 0, $secondLine: 0)
    span
        position: relative
        display: inline-block
        transform: scaleY(0)

        &:before
            content: ''
            display: block
            height: 100%
            width: 100%
            background-color: #000
            position: absolute
            top: 0
            left: 0

    @if $firstLine != 0
        @include fadeInWord-animationY($firstLine)
    @if $secondLine != 0
        @include fadeInWord-animationY($firstLine + $secondLine, $firstLine + 1, TRUE)
@mixin fadeInWord-animationY($length, $from: 1, $vector: FALSE)
    $delay: 0
    $status: 0

    @if $vector==TRUE
        $status: 2
    @else
        $status: 1

    @for $i from $from through $length
        @if $status%2==0
            span:nth-of-type(#{$i})
                transform-origin: bottom
                &:before
                    transform-origin: bottom
        @else
            span:nth-of-type(#{$i})
                transform-origin: top
                &:before
                    transform-origin: top

        $status: $status + 1

        span:nth-of-type(#{$i})
            animation: scaleY 0.36s cubic-bezier(0.84, 0.01, 0.78, 0.13) (0.1s + $delay) forwards
            &:before
                animation: notScaleY 0.3s cubic-bezier(0.11, 1.05, 0.32, 1.03) (0.45s + $delay) forwards

        $delay: $delay + 0.1

追記(2019/9/18)

関数の書き方こっちの方がいいかもしれない
きれいな書き方ってむずかしい

@mixin fadeInWord-animationY($length, $from: 1, $vector: FALSE)
    $delay: 0
    $start: ''
    $next: ''

    span
        animation: scaleY 0.36s cubic-bezier(0.84, 0.01, 0.78, 0.13) forwards
        &:before
            animation: notScaleY 0.3s cubic-bezier(0.11, 1.05, 0.32, 1.03) forwards

    @if $vector==TRUE
        $start: top
        $next: bottom
    @else
        $start: bottom
        $next: top

    span:nth-of-type(odd)
        transform-origin: $start
        &:before
            transform-origin: $start

    span:nth-of-type(even)
        transform-origin: $next
        &:before
            transform-origin: $next

    @for $i from $from through $length
        span:nth-of-type(#{$i})
            animation-delay: 0.1s + $delay
            &:before
                animation-delay: 0.45s + $delay

        $delay: $delay + 0.1

縦向き

変更点をあげる

@mixin fadeInWordX($firstLine: 0, $secondLine: 0)
    writing-mode: vertical-rl

    span
        position: relative
        display: inline-block
        transform: scaleX(0)

        &:before
            content: ''
            display: block
            height: 100%
            width: 100%
            background-color: #000
            position: absolute
            top: 0
            left: 0

    @if $firstLine != 0
        @include fadeInWord-animationX($firstLine)
    @if $secondLine != 0
        @include fadeInWord-animationX($firstLine + $secondLine, $firstLine + 1, TRUE)
  • writing-mode: vertical-rl

テキストのレイアウトを垂直方向にしました

  • transform: scaleX(0)

横向きに広がるように scaleX に変えました
基本的に横と縦では scaleXscaleY が逆になります

@mixin fadeInWord-animationX($length, $from: 1, $vector: FALSE)
    $delay: 0
    $status: 0

    @if $vector==TRUE
        $status: 2
    @else
        $status: 1

    @for $i from $from through $length
        @if $status%2==0
            span:nth-of-type(#{$i})
                transform-origin: left
                &:before
                    transform-origin: left
        @else
            span:nth-of-type(#{$i})
                transform-origin: right
                &:before
                    transform-origin: right

        $status: $status + 1

        span:nth-of-type(#{$i})
            animation: scaleX 0.35s cubic-bezier(0.84, 0.01, 0.78, 0.13) (0.1s + $delay) forwards
            &:before
                animation: notScaleX 0.3s cubic-bezier(0.11, 1.05, 0.32, 1.03) (0.45s + $delay) forwards

        $delay: $delay + 0.1
  • transform-origin

縦と横が変わるので、上下と左右も変わります

  • animation-name: scaleXanimation-name: notScaleX

keyframes はこちら

@keyframes scaleX
    100%
        transform: scaleX(1)

@keyframes notScaleX
    100%
        transform: scaleX(0)
  • animation-duration: 0.35s

これは好みの問題かもしれませんが、同じ秒数だと印象が違ったのでほんの少し調整しました

おわりに

1 行のみ、または 2 行に対応しています
現在の書き方は複数行に対応していませんが、引数を増やせば拡張可能です

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5