Vue.jsでスクロール連動の演出つくるときのscrollmagicの便利なつかいかた


ウェブサイトをつくるとき、スクロールして画面に入ったらアニメーションをする
といったことがよくあると思います。
Vue.jsでウェブサイトをつくることが多くなってきたので、なにか便利な方法ないかと
検討した結果、この記事で紹介する方法がシンプルでいいかなと思ったのでご紹介します。

## scrollmagicに関して

srocll連動でアニメーションをさせる系のライブラリで[scrollmagic](http://scrollmagic.io/)という
ライブラリがあります。以前のjqueryとかでやっていた制作環境で重宝していたのですが、
Vue.js環境でもうまいこと使う方法はないかと模索してみたという感じです。
(jquery依存はしていないライブラリですしね！)
ライブラリの細かな説明はしませんので各自ググってみてください。
[example](http://scrollmagic.io/examples/)が充実しているので困ることはないと思います。

## 本題

### プラグインの作成

scrollmagicの仕様上、Vue.use()で使えるようになるプラグインとして
使うのがいいかなと思い、プラグインというほど大げさなものではないですが
プラグインの作成から始めてみたいと思います。

適当なディレクトリに以下のようなjsファイルを作成します。
(`vue-scrollmagic/index.js`とか)

```index.js
export default {
  install (Vue, options = {}) {

    const { global = true} = options

    if (typeof window !== 'undefined' && typeof document !== 'undefined') {
      const ScrollMagic = require('scrollmagic')
      Vue.mixin({
        computed: {
          $smController() { return new ScrollMagic.Controller() },
          $sm() { return ScrollMagic }
        }
      })
    } 

  }
}
```

プラグインに関する細かい話は[ドキュメント](https://jp.vuejs.org/v2/guide/plugins.html)等を見ていただくとして、
やっていることはシンプルでmixinでcomputedにscrollmagicのコントローラーを追加しているだけです。
(一応ライブラリ全体も)

globalというオプションを渡していますがこちらについては後述します。

controllerはアプリケーション内に1つあればいいのでこういう実装にしてみました。

### イベントコンポーネントの作成

この記事の肝になってくるところですが、画面内に入った/出たらイベントを発行してくれるだけの 
機能を持ったコンポーネントを作成して、アニメーションをさせたいコンポーネントで使う。 
ということにしました。文字では微妙に伝わりにくいかと思いますのでとりあえず実装をみてみましょう。
(`vue-scrollmagic/SMEvent.vue`とかつくります)

```SMEvent.vue
<template>
<div></div>
</template>

<script>
export default {
  props:{
    triggerHook:{
      type: [Number, String],
      default: "onEnter",
    },
    offset:{
      type: [Number],
      default: 0
    },

  },
  components: {},
  computed: {},
  methods: {
    onEnter(){
      this.$emit('onEnter')
    },
  },
  mounted () {
    this.scene = new this.$sm.Scene({
      triggerElement: this.$el,
    })
    this.scene.on('enter', this.onEnter)
    this.scene.triggerHook(this.triggerHook)
    this.scene.offset(this.offset)
    this.$smController.addScene(this.scene);

  },
  beforeDestory(){
    this.$smController.removeScene(this.scene)
    this.scene.off('enter', this.onEnter)
    this.scene = this.scene.destory(true)
  },
  watch: {
    offset: function(next){
      this.scene.offset(next);
    },
    triggerHook: function(next){
      this.scene.triggerHook(next);
    }
  }
}
</script>

<style scoped>
</style>
```

このコンポーネントは一例似すぎないのですが、(propsで渡すoption、発行するイベント等)
今回は"Enter"のイベントをemitする、指定できるオプションはtriggerHook(イベントが発生する位置)と
offset(発生する位置のオフセット)のみとするコンポーネントを作成しました。
beforeDestoryで消す処理をいれたり、watch関数を使ってオプションが変わったときに
設定し直すとかしてますが(Vueはこういうところがべんり！)やっていることはシンプルなはずです。

もしかしたらまだピンときていないかもしれませんが次にこれの使い方を説明します。

### イベントコンポーネントの使用

使用する前に一度プラグインのファイルに戻って以下のようにします。
(install関数内のみ抜粋、１番上に`import SMEvent from './SMEvent'`を書く)

```index.js
const { global = true} = options

if (typeof window !== 'undefined' && typeof document !== 'undefined') {
  const ScrollMagic = require('scrollmagic')
  Vue.mixin({
    computed: {
      $smController() { return new ScrollMagic.Controller() },
      $sm() { return ScrollMagic }
    }
  })
}

//新しく追加
if (global) {
  Vue.component('sm-event', SMEvent)
}
```

Vueコンポーネントをグローバルにしてどのコンポーネントでもmodulesで指定しなくても
使えるようにしました。Vue.use()のoptionに`{global: false}`(defaultはtrue)
を渡せばグローバルではなくなるようにしています。

さて、使用してみる話からは少しはずれましたがスクロール連動アニメーションを
伴う適当なコンポーネントを作成してみます。画面内の中央にきたらフェードインするだけにしました。
Vue.use()されている想定です。

```HogeComponent.vue
<template lang="pug">
.root
　sm-event(triggerHook="onCenter" @onEnter="onEnter" :offset="0")
  p(ref="fadeText") フェードするテキストです。
</template>

<script>

export default {
  methods: {
    onEnter(){
      this.$refs.fadeText.classList.add('active');
    }
  },
}
</script>

<style lang="stylus" scoped>
p
  opacity 0
  transition opacity 1s ease 0s
p.active
  opacity 1
</style>
```

自分でつくったものを簡略化して雰囲気で書き直して動作検証してないので
動作するかはわかりませんがおそらくこれで動くかなというものです。
`em-event`コンポーネントの`@onEnter`でイベントを受け取るメソッドを
指定してonEnter内でrefs使って'active'というclassを追加してみます。
アニメーションはcss(stylus)に書いてみました。

SMEventをコンポーネント内に追加して受け取るメソッド名をすれば
任意のコンポーネントでスクロール連動の演出が作りたい放題ですね！ 
また今回は簡単にすべくclassを付与してcssアニメーションにしましたが、
TweenMaxを使ってもいいしなんでもできそうです。

SMEventの方も、今回はEnterイベントのみでしたが別のイベントを発行する
ようにしてもいいし、別のコンポーネントをつくってもいいので好きに拡張していけると思います。

以上、現時点で自分が思う、Vue.jsでのスクロール演出つくりかたでした。
ここまで読んでくれた方ありがとうございます。