実例で学ぶCSSアニメーション(vue)【第70回駒場祭公式ウェブサイトでの例】

[第70回駒場祭公式ウェブサイト](https://www.komabasai.net/70/visitor)
こちらのサイトの、

- お気に入りスター
- ローディング画面

のCSSアニメーションをどう実装したかについて書きます。Qiitaで「CSSアニメーション」で検索したら1787件ヒットしてしまって「これどれくらいの人に役に立つのかな、、？」とびくびくしていますが、実例をコードと一緒に見ることのできる機会はいくらあっても（様々なレベルでの実装があるので）初学者にとっては参考になるかもなあと思い投稿いたした次第です。

## お気に入りスター
星に名前はついてなかったので「お気に入りスター」という名称は僕が今ここで呼んでいるだけなのですが、企画の情報が載っているこいつの右下の星です。企画あるいはグッズの情報が載っているページ/コンポーネントすべてについています。ぽちぽちしてると楽しい。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545780/8e4e03bd-8c13-e442-98aa-1bc1d3900202.png)
ブラウザが重かったりするとまわりの~~タピオカみたいなやつ~~点々が見えないかもしれません。

星のvueファイルにおけるhtml部分はこんな感じです。

```vue:Star.vue
<template lang="pug">
.star
    svg(:class="starOn?'s-colored':'s-uncolored'" @click.prevent.stop="favorite" 以後省略)
        polygon(:fill="starOn? '#F5CE2F': '#CFD0D0'" points="省略")
    .particle(v-for="i in 10" :key="`particle${i}`" :id="starOn?`particle${i}`:''" :style="`background: ${particleColor[i-1]}`")
</template>
```
script部分はこんな感じです（要らないものは消しています）（localStorage部分が気になる方は[こちら](https://qiita.com/SatoshiNonomura/items/6a54bbe8cbf54879ed9b)へ）

```javascript:Star.vue
<script>
    export default {
        methods: {
            favorite () {
                //localStorageを見てお気に入り登録がすでにされていたらstarOnをfalse、されていなかったらtrueにするコード
            }
        },
        data() {
            return {
                starOn: false,
                particleColor:[
                    '#f54e42', 
                    '#42daf5', 
                    '#f5d142',
                    '#42f596',
                    '#4242f5',
                    '#f59642',
                    '#9c42f5',
                    '#f5f542',
                    '#f542c2',
                    '#42adf5'
                ]
            }
        },
    }
</script>
```

それでクリックしたときに星が大きくなるcssがこちら

```scss:Star.vue
@keyframes anim{
    from{
        transform: scale(0);
    }
    70%{
        transform: scale(1.3);
    }
    to{
        transform: scale(1);
    }
}
.s-colored{
    animation: anim 0.3s ease;
}
```


```scss:Star.vue
@for $i from 1 through 20{
    #particle#{$i} {
        opacity: 1;
        animation : particleanim#{$i} 0.3s ease forwards;
    }
}
@keyframes particleanim1{
    from{
        transform: translate(0px,0px);
        opacity:1;
    }
    70%{
        transform: translate(0.98px*15,0.17px*15);
        opacity: 1;
    }
    to{
        transform: translate(0.98px*15,0.17px*15);
        opacity: 0;
    }
}
//particleanim2以降も同様のノリ。
```

まあつまり、星をクリックすると変数starOnが状況に合わせてtrueになったりfalseになったりして、それにあわせて星であるsvgにクラスが付与されたり点々であるdivにidが付与されたりした結果animationが始まる、という仕組みになっています。
keyframeのparticleanimですが、点々は36度(360°/10)ずつずれて飛んでいってほしいので、飛んでいく方向の角度を36度ずつずらしてそれぞれのx方向の変位、y方向の変位を計算して全部書くってことをやっていました。[そのうちsin()とか使えるようになる](https://drafts.csswg.org/css-values-4/#trig-funcs)みたいですね。楽しみです...!

ちなみにお察しかと思いますが点々である.particleはsvgではなくdivなのでcssはこんな感じです。ふつうにsvgのcircleタグを使えばよかったと思うのでアンチパターンですね。

```css:Star.vue
.particle{
    width: 1.6px;
    height: 1.6px;
    border-radius: 0.8px;
    position: absolute;
    top: /*省略*/;
    left: /*省略*/;
    z-index: -1;
}
```

##ローディング画面
次はこちら。他のサイトから遷移してきたときや再読み込み時に起動するこれです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545780/7a241e71-de58-5c2f-76e5-1241765b96c0.png)
正直あまり書くことは無く、けっこうかんたんにできます。これコストパフォーマンスがいい、みたいな話になったりもしました。
このコンポーネントでは、（せっかくvueだし）親のタグとして[transition](https://jp.vuejs.org/v2/guide/transitions.html#CSS-%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3)を設定しています。そして、子要素としてv-ifを設定しているdivを置き、その中にロゴタイプと七角形のsvgを置いています。

```vue:Star.vue
<template lang="pug">
    transition(name="loading")
      .loading-anim(v-if="whethershow")
        img.logotype(src="略" alt="")
        svg.hepta(以後省略)
</template>
```
cssを使って、これらロゴタイプと七角形にposition: absoluteを設定してtop, left, transform:translate()またはdispay: flexを使って中央にそろえます。
そして、双方に一定周期で点滅するようなkeyframeを適用、七角形にだけ回転するkeyframeを適用します。

```scss:LoadingAnimation.vue
@keyframes anim1{
    0%{
        opacity:0;
    }
    100%{
        opacity:0.8;
    }
}


@keyframes anim2{
    0%{
        transform:translate(-50%,-50%) rotate(0deg);
        //translateの値も書いておかないと中央揃えが維持されない
    }
    100%{
        transform:translate(-50%,-50%) rotate(51.4286deg); //360/7
    }
}
.logotype{
    //略
    animation: anim1 0.9s ease infinite alternate forwards;
}
.hepta{
    //略
    animation: anim2 1.8s ease infinite, anim1 0.9s ease infinite alternate forwards;
}

```

transitionタグ（？）で囲っているのでコンポーネント自体のトランジションは以下のcssで設定します。

```scss:LoadingAnimation.vue
.loading-enter,.loading-leave-to{
    opacity:0;
}
.loading-enter-to, .loading-leave{
    opacity:1;
}
.loading-enter-active, .loading-leave-active {
    transition: opacity 1s ease-in-out;
}
```

これで見た目はほぼ完了で、あとはページが読み込みおわるまで表示して読み込み終わったときに消す仕組みの作成ですが、vueさんはmountedというものを提供してくれているのでかんたん、

```javascript:LoadingAnimation.vue
<script>
export default {
  data() {
    return { whethershow: true };
  },
  methods:{
      func(){
          this.whethershow=false
      }
  },
  mounted: function(){
      setTimeout(this.func,1000)
  }
};
</script>
```
これを書いてこのコンポーネントをlayout/default.vueに貼り付ければ、サイト全体がmountされた一秒後までこのコンポーネントが表示されて、消えていくということになります。ここはvueのものすごく便利なところだと思います。

おしまい！