オープニングアニメーションを作れるようになる記事

前に、noteでロゴアニメーションの記事を作ったんですが、プログラミング的なことを発信するようなアカウントじゃなかったので、こちらで再びオープニングアニメーションの記事として再利用することにしました(;・∀・)

オープニングアニメーションは、作ろうと思うと時間差で作らないといけないのでちょっと手間がかかると思うので、是非参考にしていただけたらなと思います。

ロゴに普通の画像をつかったものと、svgアニメーションを使ったものとで２つ用意しています。(上がsvg、下が画像)

完成品はこちらになります。
https://sakaij.github.io/logo-animation/index.html
https://sakaij.github.io/logo-animation/index2.html

SVGの方は、前使ったロゴアニメーションの使いまわしなので、サイズ感がおかしいですがご了承ください(=_=)
(春はあけぼの～などのアニメーションはオープニングアニメーションには関係ないですが、オープニングの幕が開いたときに本文に何もないと寂しいので入れています)

また非同期処理を使っているので、もし非同期処理について分からないという方がいれば
[Javascript初級者がAsync/Awaitが使えるようになる記事](https://qiita.com/aulait/items/c245ee7c4a11bda939ae)をお読みください。

# コードの解説

### 画像のロゴの場合
まず、ロゴに普通の画像を使ったものを載せます。


```html

<div class="opening">
    <div class="opening-logo">
        <img src="./logo2.svg" alt="" width="196" height="48">
    </div>
</div>
```
```css
.container {
    width: 1080px;
    margin: 120px auto;
}
.opening {
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 1;
    position: fixed;
    background-color: #FFFDD
    transition: all 0.5s eas
}
.opening--out {
    left: 100%;
}
.opening-logo {
    opacity: 0;
    transition: opacity 0.5s
}
.opening-logo img {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
}
.opening-logo--visible {
    opacity: 1;
}

```
```javascript
const timer = (time) => //タイマー
    new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, time);
    })
async function openingAnimation() {
    const opening = document.querySelector(".opening"),
        openingLogo = document.querySelector(".opening-logo"),
        openingTexts = Array.from(document.querySelectorAll(".opening-logo_text")), //noteの文字配列
        contentLoadWait = () => new Promise(resolve => {
            if (document.readyState === 'complete') {
                resolve();
            } else {
                window.onload = () => resolve();
            }
        });
    await timer(1000);
    /*ここで、ロゴをフェードインさせる。   */
    openingLogo.classList.add("opening-logo--visible");
    await timer(2000);
    /*画像などのロードが全て完了するまで待機*/
    await contentLoadWait();
    /*ロゴをフェードアウト*/
    openingLogo.classList.remove("opening-logo--visible");
    await timer(1000);
    /*背景をフェードアウトさせる*/
    opening.classList.add("opening--out")
}
openingAnimation();

```

まず、jsについてですがgithubの方ではやっていませんが、Await/Asyncを使ってますのでbabelなどでトランスパイルすることをお勧めします。面倒くさければAsync/AwaitをPromiseで書き換えてしまってください。

流れは、時間差によってタグにクラスをつけていってcssによるtransitionでアニメーションさせているだけなので、簡単だと思います。

ただ、

```javascript
await contentLoadWait();
↓
↓
contentLoadWait = () => new Promise(resolve => {
    if (document.readyState === 'complete') {
        resolve();
    } else {
        window.onload = () => resolve();
    }
});

```
ここの部分の処理ですが、ロゴを出した時点で画像などを含めた全てのコンテンツのロードがまだ終了していなければここで待機させています。
完璧にロードされるまで待たせる必要がなければ、ここはいらないです。

あと、

```javascript
openingAnimation();
```
ですが、ここも

```javascript
(async function() {
    await openingAnimation();
})();
```
このように、するとオープニングアニメーションが終わった段階で何かしらできます。今回は、オープニングが終わった段階で

```javascript
(async function() {
    await openingAnimation();
    await timer(500);
    await leadAnimation();
})();
```
このように「春はあけぼの～」の処理を呼び出しています（「春はあけぼの～」は[Javascript初級者がAsync/Awaitが使えるようになる記事](https://qiita.com/aulait/items/c245ee7c4a11bda939ae)のやつを使いまわしたやつです）


### SVGロゴの場合

```html
<div class="opening">
    <svg class="opening-logo" xmlns="http://www.w3.org/2000/svg" width="98.723" height="24.256" viewBox="0 0 98.723 24.256">
        <g transform="translate(147 -611.834)">
            <g class="opening-logo_part opening-logo_icon" transform="translate(-147 611.834)">
                <path d="M9.224,10.681H6.364a1.82,1.82,0,0,1-.624-.07,1.227,1.227,0,0,1,0-2.324,1.868,1.868,0,0,1,.625-.068H8.218V6.363a1.82,1.82,0,0,1,.07-.624A1.2,1.2,0,0,1,9.45,4.906a1.21,1.21,0,0,1,1.162.833,1.851,1.851,0,0,1,.069.624V9.223a2.349,2.349,0,0,1-.035.5,1.
            </g>
            <path class="opening-logo_part opening-logo_text-visible opening-logo_text opening-logo_text-n" d="M18.323-30H13.99v-7.827a3.078,3.078,0,0,0-.528-2,1.738,1.738,0,0,0-1.395-.623,2.7,2.7,0,0,0-1.219.313,3.877,3.877,0,0,0-1.124.86,3.921,3.921,0,0,0-.772,1.251V-30
            <path class="opening-logo_part opening-logo_text-visible opening-logo_text opening-logo_text-o" d="M9.626-29.726a8.416,8.416,0,0,1-3.236-.587,6.959,6.959,0,0,1-2.4-1.6,6.856,6.856,0,0,1-1.476-2.321,7.615,7.615,0,0,1-.5-2.748,7.69,7.69,0,0,1,.5-2.775A6.856,6.85
            <path class="opening-logo_part opening-logo_text-visible opening-logo_text opening-logo_text-t" d="M11.892-39.8q-.6.244-1.313.487a11.1,11.1,0,0,1-1.5.393,8.272,8.272,0,0,1-1.544.149,5.346,5.346,0,0,1-1.99-.36,2.96,2.96,0,0,1-1.435-1.188,4.019,4.019,0,0,1-.528-
            <path class="opening-logo_part opening-logo_text-visible opening-logo_text opening-logo_text-e" d="M9.653-29.726A8.459,8.459,0,0,1,6.43-30.3a7.171,7.171,0,0,1-2.4-1.566,6.707,6.707,0,0,1-1.5-2.289,7.312,7.312,0,0,1-.515-2.718,7.561,7.561,0,0,1,.907-3.683,7.025
        </g>
    </svg>
</div>
```
```css
.container {
    width: 1080px;
    margin: 120px auto;
}
.opening {
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 1;
    position: fixed;
    background-color: #FFFDD4;
    transition: all 0.5s ease-in-out;
}
.opening--out {
    left: 100%;
}
.opening-logo {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    opacity: 0;
    transition: opacity 0.5s;
}
.opening-logo--visible {
    opacity: 1;
}
.opening-logo_text {
    stroke-width: 1;
    fill-rule: evenodd;
    stroke: #000;
    stroke-dasharray: 80;
    opacity: 0;
    transition: opacity 0.5s, fill 0.5s, stroke-dashoffset 2s;
    stroke-dashoffset: 80;
}
.opening-logo_text.opening-logo_text--visible {
    fill: #000;
    opacity: 1;
}
.opening-logo_text.opening-logo_text--stroke {
    fill: transparent;
    stroke-dashoffset: 0;
}
.opening-logo_text.opening-logo_text--filled {
    stroke: transparent;
    fill: #000;
    transition: all 1s;
}
```
```javascript
const timer = (time) => //タイマー
    new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, time);
    })
async function openingAnimation() {
    const opening = document.querySelector(".opening"),
        openingLogo = document.querySelector(".opening-logo"),
        openingTexts = Array.from(document.querySelectorAll(".opening-logo_text")), //noteの文字配列
        contentLoadWait = () => new Promise(resolve => {
            if (document.readyState === 'complete') {
                resolve();
            } else {
                window.onload = () => resolve();
            }
        });
    await contentLoadWait();
    await timer(1000);
    /*ここで、ロゴをフェードインさせる。(この時点では左のアイコン部分だけしかされない)    */
    openingLogo.classList.add("opening-logo--visible");
    await timer(1000);
    /*ここで、noteの文字を一文字づつ表示させる。（線のアウトラインだけがアニメーションしながら表示される）*/
    await openingTexts.reduce((p, e) => {
        return p.then(() => {
            e.classList.add("opening-logo_text--visible");
            e.classList.add("opening-logo_text--stroke");
            return timer(500); //ここの数字を操作すると、次の文字へのアニメーションが遅くなる
        });
    }, Promise.resolve());
    await timer(1500);
    /*noteの文字を黒く塗りつぶす*/
    openingTexts.forEach(e => e.classList.add("opening-logo_text--filled"));
    await timer(1500);
    /*画像などのロードが全て完了するまで待機*/
    await contentLoadWait();
    /*ロゴをフェードアウト*/
    openingLogo.classList.remove("opening-logo--visible");
    await timer(1000);
    /*背景をフェードアウトさせる*/
    opening.classList.add("opening--out")
}
openingAnimation();
```

先ほどの、画像のロゴバージョンの時と大きく違いはないですが

```javascript
    await openingTexts.reduce((p, e) => {
        return p.then(() => {
            e.classList.add("opening-logo_text--visible");
            e.classList.add("opening-logo_text--stroke");
            return timer(500); //ここの数字を操作すると、次の文字へのアニメーションが遅くなる
        });
    }, Promise.resolve());


```

ここの部分が違う点になります。今回の場合、特にSVG系のライブラリは使っておらずSVGのタグにクラスをつけてtransitionによるアニメーションで対応しています。

ここでは、0.5秒おきに1文字づつ線のアニメーションをさせるクラスを付与しています。


# 最後に

おそらくコピペして使うことはできないと思いますが、オープニングアニメーションを実装する際に参考にしていただければなと思います。SVGアニメーションは、自分は使ったときに動かなかったりしてなんか面倒くさかったので使っていませんが普通はJSのライブラリを使ってやるみたいなので、JSのライブラリを使って対応した方が良いかもしれません

また、githubでコードも公開しています。
https://github.com/Sakaij/logo-animation