Vue.js:カッコイイナビゲーションを作る

最近マイブームの「アレ、どうやって作るんだろう？」的なメモです。
たまに見かける「メニューを開くと文字がフワフワっと出てくる」アレ。
実は以前から「どうやって作るんだろう？？」と思ってたんです。

`transition-delay`を付けても出来そうなんですけど、
何か１文字ずつズレて表示されてるし、
人力で手作業でやってるワケないしな〜と思ったので、調べました。

調べた結果、おそらく`SplitText`っていうエフェクトの
カスタムの一種だろうと思いました。
`gsap`っていう`Javascriptアニメーションフレームワーク`があるんですけど、
それの高機能なバージョンに付属している機能です。

![SplitText2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216132/1d1366e6-d60d-16ba-88ce-356eca3ae483.gif)


###Vue.jsで試してみる。
今回も`Vue.js`です。前回の投稿で「≡」を作った時のソースの流用をします。
https://qiita.com/DaisukeNishi/items/999709a4636f600ffcfa

```App.vue
<template>
  <div id="app">
    <header class="header">
      <h1 class="title">
        Website
      </h1>
      <div
        class="menu"
        data-state="hoge"
        data-mouse="fuga"
        @click="click"
        @mouseenter="enter"
        @mouseleave="leave"
        >
        <div class="line line-1"></div>
        <div class="line line-2"></div>
        <div class="line line-3"></div>
        <div class="line-close line-close-1"></div>
        <div class="line-close line-close-2"></div>
      </div>
    </header>
    <div class="cnt-cover">
      <div class="cover"></div> <!--白背景-->
    </div>
    <nav class="split-text">
       <div id="quote" style="text-align:left;">
         メニュー表示<br/>
         会社概要<br/>
         プライバシーポリシー<br/>
         あいうえお<br/>
         かきくけこ
       </div>
    </nav>
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>
```

```App.vue.scss
.cnt-cover {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 10;
  pointer-events: none
}

.cnt-cover .cover {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #fff;
  z-index: 1;
  will-change: opacity;
  opacity: 0;
}

.split-text {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  color: #000;
  opacity: 0;
}
```
（あれっ！？しまった、書いてる途中に気がついたんですが、
これ`opacity`だけだとメニューの後ろにあるリンクが押せなくなりますね。
`z-index`も一緒に変えれば動くとおもう・・・）→（未検証）

```App.vue
<script>
import gsap from 'gsap';
import SplitText from './js/SplitText.js'
import $ from 'jquery';
import HelloWorld from './components/HelloWorld.vue'
export default {
  name: 'App',
  components: {
    HelloWorld
  },
  methods:{
    //ハンバーガーメニューのイベント
    click(e){
      if(e.target.dataset.state !== 'batsu'){
        e.target.dataset.state = 'batsu';
        this.show();
      }else{
        e.target.dataset.state = 'hamburger';
        this.hide();
      }
    },
    enter(e){
      e.target.dataset.mouse = 'enter';
    },
    leave(e){
      e.target.dataset.mouse = 'leave';
    },
    show(){
      //gsapで表示
      const wt = $(".cnt-cover .cover");
      const tl = new gsap.timeline({});
      tl.to(wt, .15, { autoAlpha:1 });
      //jQueryで表示
      $(".split-text").css("opacity","1")
      //テキストアニメーション
      const tl2 = gsap.timeline(),
        mySplitText = new SplitText("#quote", {type:"words,chars"}), 
        chars = mySplitText.chars;
      gsap.set("#quote", {perspective: 400});
      tl2.from(chars, {duration: 0.8, opacity:0, scale:0, y:80, rotationX:180, transformOrigin:"50% 50% -20",  ease:"back", stagger: 0.01}, "+=0");
    },
    hide(){
      //gsapで非表示
      const wt = $(".cnt-cover .cover ");
      const tl = new gsap.timeline({});
      tl.to(wt, 0.15, { autoAlpha:0 });
      //jQueryで非表示
      $(".split-text").css("opacity","0")
    },
  },
}
</script>
```

使用方法の説明にはこう書いてあります。
`an array of all the divs that wrap each character`
`div`の中にある全ての文字をアニメーションさせる、的な意味かと。
多分、親要素に書いてあげれば子要素に適用できるはず。

変数をconst3連続で定義してます。
こちらの`SplitText`は`import`してないとエラーになります。

```App.vue.js
const tl2 = gsap.timeline(),
mySplitText = new SplitText("#quote", {type:"words,chars"}), 
chars = mySplitText.chars;
```
パースペクティブの意味はちょっと調べてないですが、文字数制限的な感じかと。
ココで要素の`id`を指定しておきます。

```App.vue.js
gsap.set("#quote", {perspective: 400});
```

```App.vue.js
tl2.from(
  chars, {
    duration: 0.8,  //スピード
    opacity:0,      //開始時の透明度
    scale:0,        //開始時の大きさ
    y:80,           //開始時のy軸？
    rotationX:180,  //開始時にx軸の方向に180度倒す
    transformOrigin:"50% 50% -20", //元の大きさとか?
    ease:"back",    //イージングとか
    stagger: 0.01   //ぐらつきの度合い
  },
"+=0");             //?なんだろう？
```
動きました。細かいセッティングが可能です。

###プラグインが微妙に高い。
```App.vue.js
import SplitText from './js/SplitText.js'
```
こちら、本来は`gsap`のパッケージに入ってるのだろうと思います。
有料プラグインは、買うしかないんじゃないですかね商用ライセンスだし。

いやはや「試しにコレ使ってみたいな」で1万円は高いッス。。高いッスよ・・・
`CodePen`では無料で使えるらしいですが、まず案件に使えるのかを試したい。
`JSプラグイン`を有料で買うって文化が、日本にはまだないので稟議を通らないと思うんですよね。。
オシャレなデザイン事務所様だったら稟議通るかもですけど。

お試しでローカルで使ってみたい（案件で使う時は買う）という話だったら、
β版の`SplitText`がどこかに転がってないか探してみたら良いかと思います。
動作保証できないですが、案外バージョンが合わなくても動く気がします。
https://github.com/wh1100717/me.emptystack.net/blob/9b54f1aba0e5140bb873df63c1c5ea614e622e4f/asset/gsap/utils/SplitText.js
https://github.com/pslhrd/PORTFOLIO_V2/blob/50a0224456ed4de14d88f8cfb550d80e425183c1/src/js/SplitText.js

####■参考資料
https://goworkship.com/magazine/text-effect-typography/
https://greensock.com/SplitText
https://codepen.io/collection/KiEhr?grid_type=list
https://www.webprofessional.jp/fancy-web-animations-made-easy-greensock-plugins/