Nuxt, カスタムディレクティブを使ってみたいんだが!?

目的(purpose)

Nuxtで作成したwebサイト上でマウス操作した際、
何か対応したアニメーションを起こしたいなと思うときありませんか?
例えば……
マウスをクリックした際そこから同心円状に円が広がっていくとか…ね!?
今回、「カスタムディレクティブ」を使用することで
Nuxtでいろんなページを作成しても再利用することができます。
ではやっていきましょう!!

開発環境(Development environment)

ディレクトリ構成(Directory structure)


js_spa/
  ├ pages/
  │   └ index.vue
  │
  ├ plugins/
  │   └ circle.js
  │
  └ nuxt.config.js

※今回使用するファイルのみ表示しています。

サイト画面の作成 ~ローカル~(Make Site Screen ~local~)

まずはサイトを立ち上げたときの最初に表示されるメイン画面を作ります。
この画面上でマウスのボタンを押し続けるとその場所から円模様が広がっていきます。
まず、ローカルでカスタムディレクティブの処理を追加しましょう。
pages/index.vue

<template>
  <section class="container">
    <div>
      <div class="circle" v-click="handleScale"/>
    </div>
  </section>
</template>

<script>
export default {
  directives: {
   'click': {
     inserted: function (el, binding) {
      let f = function (evt) {
        const position = binding.value(evt, el)
        window.onmouseup = () => {
          el.setAttribute(
            "style",
            `background-color: blue; transform:scale(1,1); top: ${position.posY}px; left: ${position.posX}px;`
          )
        }
      }
      window.addEventListener('mousedown', f)
    }
   }
 },
  methods: {
    handleScale: function(evt, el) {
      const posX = evt.clientX
      const posY = evt.clientY
      el.setAttribute(
        "style",
        `background-color: red; transform:scale(20,20); top: ${posY}px; left: ${posX}px;`
      )
      return {
        posX: posX,
        posY: posY
      }
    },
  }
}
</script>

<style>
.container {
  min-height: 150vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.circle {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background-color:red;
  transition-property: transform;
  transition-duration: 1.5s;
}
</style>

このvueファイルをそれぞれtempletescriptstyle
の3つのタグに分けて説明します。
templeteタグ(templete tag)


  <section class="container">
    <div>
      <div class="circle" v-click="handleScale"/>  <!-- 1 -->
    </div>
  </section>

  • 1: 円模様を表示するための部分です。
    class="circle"が後に記述するstyleを反映させるためにclassに"circle"を加えています。
    v-click="handleScale"が今回のメインとなる円模様にアニメーションを加えるためのものです。
    またv-clickはこの後、カスタムディレクティブで作成します。

scriptタグ(script tag)


export default {
  directives: {
   'click': {
     inserted: function (el, binding) {
      let f = function (evt) {
        const position = binding.value(evt, el)  // 1
        window.onmouseup = () => {  // 2
          el.setAttribute(  // 3
            "style",
            `background-color: blue; transform:scale(1,1); top: ${position.posY}px; left: ${position.posX}px;`
          )
        }
      }
      window.addEventListener('mousedown', f)
    }
   }
 },
  methods: {
    handleScale: function(evt, el) {
      const posX = evt.clientX  // 4
      const posY = evt.clientY  // 4
      el.setAttribute(
        "style",
        `background-color: red; transform:scale(20,20); top: ${posY}px; left: ${posX}px;`  // 5
      )
      return {
        posX: posX,
        posY: posY
      }
    },
  }
}

  • 1: ブラウザ内の現在マウスのある座標を取得しています。
  • 2: マウスの左クリックを押し、放したときに3番が実行されます。
  • 3: 実行されたときにcssの新しい属性を追加しています。
    transform: scale(1,1)で円模様の拡大、縮小をしています。

    into-the-program.com

  • 4: マウスの左クリックボタンを押したときの座標を取得するためのコードです。
    座標を取得するのにブラウザの座標を基準としたclientXclientYを使っています。
    マウスの座標を取得メンバはいくつかありますが用途に合わせて使用してください。
    メンバ
    screenX, screenY イベントが発生した位置のX or Y座標をディスプレイを基準として取得する
    clientX, clientY イベントが発生した位置のX or Y座標をブラウザを基準として取得する
    offsetX, offsetY イベントが発生した位置のX or Y座標を要素を基準として取得する
    pageX, pageY イベントが発生した位置のX or Y座標をページを基準として取得する
  • 5: transform:scale(20,20);が円模様の大きさを変更するコードです。
    ここに追加することでマウスの左クリックボタンを押している間だけ円模様が徐々に拡大していきます。

styleタグ(style tag)


.container {
  min-height: 150vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.circle {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;  // 1
  background-color:red;
  transition-property: transform;  // 2
  transition-duration: 1.5s;  // 3
}

  • 1: ボックスの角を丸くするのに使いますが、50%くらいに指定すると円模様を作れる。
    widthheightをいじることで、 楕円や正円に変えることができます。

    original-game.com

  • 2: transition(時間的変化)における効果を適用する範囲を指定できます。
    scriptタグtransform:scaleのみに適用したいので、 transformを選びます。
  • 3: 変化における時間間隔を指定しています。
    これはわたし的に1.5秒くらいがちょうど良かったので、1.5sを指定しています。

サイト画面の作成 ~グローバル~(Make Site Screen ~global~)

pages/index.vue

<template>
  <section class="container">
    <div>
      <div class="circle" v-click="handleScale"/>
    </div>
  </section>
</template>

<script>
export default {
  methods: {
    handleScale: function(evt, el) {
      const posX = evt.clientX
      const posY = evt.clientY
      el.setAttribute(
        "style",
        `background-color: red; transform:scale(20,20); top: ${posY}px; left: ${posX}px;`
      )
      return {
        posX: posX,
        posY: posY
      }
    },
  }
}
</script>

<style>
.container {
  min-height: 150vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.circle {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background-color:red;
  transition-property: transform;
  transition-duration: 1.5s;
}
</style>

scriptタグ(script tag)


export default {
  methods: {
    handleScale: function(evt, el) {
      const posX = evt.clientX 
      const posY = evt.clientY
      el.setAttribute(
        "style",
        `background-color: red; transform:scale(20,20); top: ${posY}px; left: ${posX}px;` 
      )
      return {
        posX: posX,
        posY: posY
      }
    },
  }
}

サイト画面の作成 ~ローカル~(Make Site Screen ~local~)のscriptタグ内にdirectivesで作成した処理を外部のjavascriptファイルに分けたので、directivesを削除します。

circle.js

import Vue from 'vue'

Vue.directive('click', {
  inserted: function (el, binding) {
    let f = function (evt) {
      const position = binding.value(evt, el)
      window.onmouseup = () => { 
        el.setAttribute( 
          "style",
          `background-color: blue; transform:scale(1,1); top: ${position.posY}px; left: ${position.posX}px;`
        )
      }
    }
    window.addEventListener('mousedown', f)
  }
})

ディレクティブ処理を書いたjavascriptファイル
nuxt.config.js

export default {
~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~~
  plugins: [
    './plugins/circle.js',
  ],
~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~
}

nuxt.config.jsplugins内に外部のディレクティブファイルのパスを追加する

qiita.com

qiita.com

実行(Execution)

youtu.be

最後に(Finally)

これでカスタムディレクティブを使った画面効果を簡単に作ることができました。
対して高度なことはしていないですが、何か参考になれば嬉しいです。
ここまで読んでくれてありがとう!!