0
2

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

スマホナビゲーションの定番、ハンバーガーメニューを
Vue.jsで作ってみたメモ。
hamburger.gif

Vue.jsで作る時は、templateの中に3つイベントを書きます。
クリックした時、マウスが乗った時、マウスが離れた時です。

状態をdata属性に保存しておかないといけないので、
data-statedata-mouseを用意しています。

stateはメニューを開いているか、閉じているか?を記録します。
mouseは、マウスが乗ったか、離れたか?を記録します。

App.vue
<template>
  <div id="app">
    <header class="header">
      <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>
</template>

次に、Javascriptを書きます。クリックしたらstatebatsuを入れます。
もしstatebatsuだったらhamburgerを入れます。

マウスが乗ったらmouseenter、離れたらleaveを入れます。

App.vue.js
<script>
export default {
  name: 'App',
  methods:{
    click(e){
      if(e.target.dataset.state !== 'batsu'){
        e.target.dataset.state = 'batsu';
      }else{
        e.target.dataset.state = 'hamburger';
      }
    },
    enter(e){
      e.target.dataset.mouse = 'enter';
    },
    leave(e){
      e.target.dataset.mouse = 'leave';
    },
  }
}
</script>

コレでイベントの制御は完了です。書き方さえ覚えてしまえばjQuery並に…
いや、$('').toggleClass()の方がカンタンですけどね。。
DOM操作に関してはjQueryが神すぎるって事で。。

##スタイルを作っていく。
動きがあるので、CSSが面倒ですよね。いくつかに分割して書きます。
まずは基本セットから。

App.vue.scss
.menu {
	position: absolute;
	top: 18px;
	right: 18px;
	width: 44px;
	height: 40px;
	cursor: pointer;
	touch-action: manipulation;

  .line {
    position: absolute;
    left: 12px;
    width: 20px;
    height: 3px;
    overflow: hidden;
    pointer-events: none;

    &.line-1 { top: 12px }
    &.line-2 { top: 19px }
    &.line-3 { top: 26px }
    &::before,
    &::after {
      content: "";
      display: block;
      position: absolute;
      top: 0;
      left: 0;
      width: 20px;
      height: 3px;
      background-color: #222;
      transition-property: transform;
      transition-duration: .15s;
      transition-timing-function: cubic-bezier(0.5, 0.1, 0.1, 1)
    }
    &::after {
      transform: translate3d(-100%, 0, 0);
    }
  }
}

ちょっと見慣れない奴は解説します。
まずtouch-action: manipulation;ですが、
これは標準的なタップ以外を無視するヤツです。ダブルタップとか(?)

次にtransition-timing-function: cubic-bezier(0.5, 0.1, 0.1, 1)
ですが、これはアニメーションのイージングをどうするかってヤツです。
「ベジェ曲線」の「アンカーポイント」の位置を示しています。

transform: translate3d(-100%, 0, 0);は、
これは昔text-indent:-100%っていうCSSの使い方があったと思うんですが、
今はこの書き方をしてる、って感じです。左にしまっておく感じです。

##アニメーション
data属性にenterが入った時のアニメーションです。
最初に表示されているハンバーガーは疑似要素のbeforeですね。
それを右に100%(消えるまで)送ってしまい、
次に疑似要素afterを左からbeforeが元々あった位置に持ってきます。

App.vue.scss
.menu[data-mouse="enter"] .line::before {
	transform: translate3d(100%, 0, 0);
}

.menu[data-mouse="enter"] .line::after {
	transform: translate3d(0, 0, 0);
}

このままだと「≡が右に消えて、左から出てきましたね。」
というだけなんですが、3本の線それぞれにdelayを付けてあげることで
なんか、「斜めに消えてる」っていうより、「キラーンと光ってる」みたいな感じに見えます。
(アニGIFだと表現できてないですが、実際はマウスが乗った時にも動きます。)

コレ最初に考えた人天才ですね。。

App.vue.scss
.menu[data-mouse="enter"] .line-1::before{
	transition-delay: 0s
}

.menu[data-mouse="enter"] .line-1::after{
	transition-delay: 0.15s;
}

.menu[data-mouse="enter"] .line-2::before{
	transition-delay: 0.05s
}

.menu[data-mouse="enter"] .line-2::after{
	transition-delay: 0.2s;
}

.menu[data-mouse="enter"] .line-3::before{
	transition-delay: 0.1s;
}

.menu[data-mouse="enter"] .line-3::after{
	transition-delay: 0.25s;
}

ちなみに「×印」になる場合、この3本は右に消えます。

App.vue.scss
.menu[data-state="batsu"] .line::before,
.menu[data-state="batsu"] .line::after {
	transform: translate3d(100%, 0, 0)
}

##バツ印を作る。
つぎはバツ印です。バツ印はstatebatsuが入った時のアニメーションです。
基本セットはこの2本です。

App.vue.scss
.menu .line-close {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 22px;
	height: 2px;
	background-color: #111;
	overflow: hidden;
	transition-property: opacity, transform;
	transition-duration: .4s;
	transition-timing-function: cubic-bezier(0.5, 0.1, 0.1, 1);
	pointer-events: none
}

.menu .line-close-1 {
	opacity: 0;
	transform: translate(-50%, -50%) rotate(-80deg)
}

.menu .line-close-2 {
	opacity: 0;
	transform: translate(-50%, -50%) rotate(-80deg)
}

透明だから見えないんですけど、こんな感じで疑似要素が2本重なっています。
それを−50%して真ん中に持ってきます。

image.png
 ↓
image.png

さらに80度回転した状態にして透明にしておきます。

image.png

それを表示する時は角度を「45度」、「−45度」にアニメーションします。

App.vue.scss
.menu[data-state="batsu"] .line-close-1 {
	opacity: 1;
	transform: translate(-50%, -50%) rotate(45deg);
	transition-delay: 0.3s
}

.menu[data-state="batsu"] .line-close-2 {
	opacity: 1;
	transform: translate(-50%, -50%) rotate(-45deg);
	transition-delay: 0.3s
}

そうすると、クルリンと折りたたみ椅子が開くようなバツマークになります。

hamburger.gif

上から下までつなげたCSSです。

App.vue.scss
<style lang="scss" scoped>
.menu {
	position: absolute;
	top: 18px;
	right: 18px;
	width: 44px;
	height: 40px;
	cursor: pointer;
	touch-action: manipulation;

  .line {
    position: absolute;
    left: 12px;
    width: 20px;
    height: 3px;
    overflow: hidden;
    pointer-events: none;

    &.line-1 { top: 12px }
    &.line-2 { top: 19px }
    &.line-3 { top: 26px }
    &::before,
    &::after {
      content: "";
      display: block;
      position: absolute;
      top: 0;
      left: 0;
      width: 20px;
      height: 3px;
      background-color: #222;
      transition-property: transform;
      transition-duration: .15s;
      transition-timing-function: cubic-bezier(0.5, 0.1, 0.1, 1)
    }
    &::after {
      transform: translate3d(-100%, 0, 0);
    }
  }
}

.menu[data-mouse="enter"] .line::before {
	transform: translate3d(100%, 0, 0);
}

.menu[data-mouse="enter"] .line::after {
	transform: translate3d(0, 0, 0);
}

.menu[data-state="batsu"] .line::before,
.menu[data-state="batsu"] .line::after {
	transform: translate3d(100%, 0, 0)
}

.menu .line-close {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 22px;
	height: 2px;
	background-color: #111;
	overflow: hidden;
	transition-property: opacity, transform;
	transition-duration: .4s;
	transition-timing-function: cubic-bezier(0.5, 0.1, 0.1, 1);
	pointer-events: none
}

.menu .line-close-1 {
	opacity: 0;
	transform: translate(-50%, -50%) rotate(-80deg)
}

.menu .line-close-2 {
	opacity: 0;
	transform: translate(-50%, -50%) rotate(-80deg)
}

.menu[data-state="batsu"] .line-close-1 {
	opacity: 1;
	transform: translate(-50%, -50%) rotate(45deg);
	transition-delay: 0.3s
}

.menu[data-state="batsu"] .line-close-2 {
	opacity: 1;
	transform: translate(-50%, -50%) rotate(-45deg);
	transition-delay: 0.3s
}

.menu[data-mouse="enter"] .line-1::before,
.menu[data-state="batsu"] .line-1::before {
	transition-delay: 0s
}

.menu[data-mouse="enter"] .line-1::after,
.menu[data-state="batsu"] .line-1::after {
	transition-delay: 0.15s;
}

.menu[data-mouse="enter"] .line-2::before,
.menu[data-state="batsu"] .line-2::before {
	transition-delay: 0.05s
}

.menu[data-mouse="enter"] .line-2::after,
.menu[data-state="batsu"] .line-2::after {
	transition-delay: 0.2s;
}

.menu[data-mouse="enter"] .line-3::before,
.menu[data-state="batsu"] .line-3::before {
	transition-delay: 0.1s;
}

.menu[data-mouse="enter"] .line-3::after,
.menu[data-state="batsu"] .line-3::after {
	transition-delay: 0.25s;
}

.menu[data-state="hamburger"] .line::before,
.menu[data-state="hamburger"] .line::after {
	transform: translate3d(0, 0, 0)
}

.menu[data-state="hamburger"] .line-1::before,
.menu[data-state="hamburger"] .line-1::after {
	transition-delay: 0.3s
}

.menu[data-state="hamburger"] .line-2::before,
.menu[data-state="hamburger"] .line-2::after {
	transition-delay: 0.35s
}

.menu[data-state="hamburger"] .line-3::before,
.menu[data-state="hamburger"] .line-3::after {
	transition-delay: 0.4s
}
</style>

さわってて面白いアイコンだなと。。以上です。

##参考資料
https://jp.vuejs.org/v2/guide/transitions.html
https://jp.vuejs.org/v2/guide/events.html
http://www.htmq.com/css3/transition-timing-function.shtml
https://developer.mozilla.org/ja/docs/Web/CSS/transform-function/translate3d

0
2
2

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?