2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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

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

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

SplitText2.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連続で定義してます。
こちらのSplitTextimportしてないとエラーになります。

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/

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?