Go to list of users who liked
More than 5 years have passed since last update.
Vue.js ( Nuxt.js ) でアニメーションやってみたら最高だった話。
Nuxt.jsで自己紹介サイトを作りました。
https://nitta.studio/
見ていただくと分かる通りアニメーションをしまくったのですが、、
https://t.co/CXj31medDj
— 新田聡一郎 (@soichiro_nitta)2018年4月26日
Nuxt.jsで自己紹介サイト作りました。NetlifyホスティングでPWA対応してます。
いろいろ自分のイカれた略歴など晒しました。宜しくお願いします。
VuexとVue.jsのウォッチャをつかって、
- イベントハンドリング
- ステート変更
- ウォッチャで検知
- 複数のコンポーネントでアニメーション発火🔥
のような書き方をしたら最高だったので、ご紹介です。
アニメーションって、どこにどの処理書けばいいのか困りませんか?
凝ったものを実装するとめちゃめちゃなコードになりがちですよね...
しかーし!Vue.jsのデータ駆動
とよばれる設計のおかげで、アニメーションの制御がとっても綺麗にできます。
(※データ駆動とは、データを中心にDOM描画をおこなったり、アクションを起こしたり、振る舞いを変えることができる設計のこと)
インタラクションが得意なデザイナ・フロントエンドさんには超おすすめですよ!
必要となる前提知識
- Nuxt.js
- Vue.js
- Vuex
- TweenMax
筆者はいつもPugとStylusを使いますが、
今回はみんな大好きHTML
とSCSS
でやってみますね!てかどう考えてもPug
とStylus
が一番いいだろ〜何でみんな嫌がるの全然意味がわからない😩
やること
Nitta.Studioのアニメーションは、解説の題材にしてはちょっと複雑なので、
簡単なサンプルを用意してみました。まずは完成形から。
- ボタンをクリック(イベントハンドリング)
- Vuexでストアのステートを変更
TheLeft
,TheCenter
,TheRight
,TheBackground
コンポーネントのウォッチャで検知- 各コンポーネントでアニメーション🔥
と、このような流れになっています。今回は順にポイントを確認していこうと思います。
サンプルコードは以下から。
https://github.com/soichiro-nitta/qiita-animation
各自cloneするなどして、
$ npm install # Or yarn install$ npm run dev # Or yarn dev
して開発環境を立ち上げてみましょう。
それでは解説していきまーす!Qiita初投稿でテンションあがってるぜフォォォ!
イベントハンドリング
まず、components/TheButton.vue
を見ていきましょう。
ここでは、クリックイベントをトリガーに、ステートの変更をおこないます。
(Vuexのストアの状態を変更するのでミューテーションをコミットしています。)
<template><divclass='TheButton'><divclass='TheButton_Start'@click='click'<!-- メソッドを指定 --> >
↑click
イベントをバインディング。
<script>import{mapGetters,mapMutations}from'vuex'exportdefault{// ...methods:{// ......mapMutations({click:'click'// `this.click()`にマッピングされます})}}</script>
↑mapMutations
ヘルパーでミューテーションをマッピング。
これでクリック時にストアのミューテーションが実行されますね。
ここではclickイベント
にミューテーションをバインドするだけです。
ここにアニメーションのコードは書きません。
ストア
ストアはstore/index.js
で定義しています。
exportconststate=()=>({entered:false// このステートが変更されるとアニメーションが🔥})exportconstgetters={entered:state=>state.entered// 各コンポーネントのウォッチャで監視するので}exportconstmutations={click(state){state.entered=!state.entered// クリックされたらステートを切り替えます}}
↑ タイプclick
のミューテーションは、state.entered
をトグルする形になっています。
これでクリックされたら、state.entered
が切り替わるようになりますね。
あとで説明しますが、各コンポーネントのウォッチャでは、state.entered
が「true
になったとき」と「false
になったとき」で書き分けをしていきます。
state.entered
はウォッチャで監視するので、ゲッターも定義しておきます。
ウォッチャ
ここからが本番!
各コンポーネントのウォッチャでステートの変更を検知して、アニメーションを発火します🔥
ここではTheLeft
コンポーネントのウォッチャ(watch
オプション)を見てみましょう。
<script>import{mapGetters}from'vuex'import{TweenMax,Expo,Elastic}from'gsap'exportdefault{// ...watch:{entered(val){// ステートの`entered`が切り替わるたび、この処理が実行されるthis.flash()// アニメーション🔥val?this.enter():this.leave()// `entered`の値によってアニメーションを書き分け🔥}},methods:{// アニメーションの宣言はここflash(){requestAnimationFrame(()=>{TweenMax.to(this.$refs.title,0.05,{// `this.$refs`でDOMにアクセスcolor:'red',scale:1.3,ease:Expo.easeIn,repeat:19,yoyo:true})})},enter(){// `entered`が`true`になったとき発火requestAnimationFrame(()=>{TweenMax.to(this.$refs.background,1,{scaleX:1,ease:Expo.easeOut})})},leave(){// `entered`が`false`になったとき発火requestAnimationFrame(()=>{TweenMax.to(this.$refs.background,1,{scaleX:0,ease:Expo.easeOut})})}}}</script>
↑ どうでしょう。綺麗じゃないですか?
ストアのentered
が切り替わるたびに、watch
オプションで宣言した処理が実行されます。
筆者のおすすめは、watch
オプションにはロジックだけ書くようにして、
実際のアニメーションのコードはmethods
オプションに定義する形です。
ウォッチャに書くのはシンプルなロジックだけ!と決めておくと、汚れなくてよいと思います。
他のTheCenter
,TheRight
,TheBackground
コンポーネントも同様の流れです。
要は、動かしたい対象のDOMがあるコンポーネントに、そのアニメーションも書くという設計です。
どのアニメーションがどこにあるのか、わかりやすくてイイですね!
これで今回のように、イベントハンドリングするDOMと、アニメーションしたいDOMが別コンポーネントでも、
Vuexとウォッチャのおかげで役割をハッキリさせることができました。
おまけ
async/awaitを使ったsetTimeout()
の筆者オレオレmixinをご紹介。
まずは通常のsetTimeout()
、こんな感じですよね?
<script>exportdefault{// ...watch:{isHell(){setTimeout(()=>{this.hell1()setTimeout(()=>{this.hell2()setTimeout(()=>{this.hell3()setTimeout(()=>{this.hell4()setTimeout(()=>{this.hell5()},500)},400)},300)},200)},100)}},// ...}</script>
ここは地獄か😇?
次に、mixinを使用したコードを見てみましょう。
<script>exportdefault{// ...watch:{asyncisHeaven(){awaitthis.$delay(100)this.heaven1()awaitthis.$delay(200)this.heaven2()awaitthis.$delay(300)this.heaven3()awaitthis.$delay(400)this.heaven4()awaitthis.$delay(500)this.heaven5()}},// ...}</script>
神じゃないですか?😻
plugins/mixin.js
にメソッドの詳細がありますので、見てみましょう。
importVuefrom'vue'Vue.mixin({methods:{$delay(ms){returnnewPromise(resolve=>setTimeout(resolve,ms))}}})
どうですか? これでコールバック地獄卒業しましょう。
(今回紹介しませんでしたが、TheCenter
,TheRight
,TheBackground
コンポーネントでも使用しています。)
おわりに
いかがでしたでしょうか?
筆者は他のJSフレームワークも経験がありますが、
イベントハンドリング → アニメーション発火の設計には悩む場面が多く、
アニメーションどこに定義すべきか、DOMへのアクセスどうしよう、と試行錯誤の日々でした。
結局、自分ルールを作っても、できたアニメーションのコードはひどく読みづらいものでした。
しかしVue.jsを試してみると、Vuexとウォッチャのおかげで、
ロジックと、実際のアニメーションのコードを、スッキリと分けることができました!
当面はこの形に落ち着きそうだなと思っています。
また、今回はアニメーション制御の解説でしたが、
Vue初心者には、イベントハンドリングやウォッチャの使い方、Vuex、メソッド定義の設計など、いい感じに学べて、かつ視覚的にも理解がすすみそうなので、入門によい題材なのではと思います。
Vue.jsはインタラクションが得意なデザイナ・フロントエンドさんには本当にオススメなので、
ぜひ題材にしていただいて、Vue使いデザイナー・フロントエンドが増えてくれれば筆者は嬉しいです〜😸
(またTheButton
コンポーネントでは、今回トランジションを使用しましたが解説を省略しています。Vue.jsのアニメーションの真髄は、まだまだいっぱいあります。もしご興味のある方は公式ガイドでわかりやすく記されていますので、ご覧になられてはいかがでしょうか。)
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme