Storybookを使ってCanvasアニメーションのパラメータを調整する(Vue版)


![sample.gif](https://qiita-image-store.s3.amazonaws.com/0/117549/adfb5878-be9a-1407-211e-8bcaa01054e2.gif)

最近[Storybook](https://storybook.js.org/)というコンポーネント単位でデザインの確認などが行えるツールがあることを知りました。このツールをリサーチしてみて、現在開発しているプロジェクトでアニメーションのパラメータなどの設定を調整するのにちょうど良いと思い、サンプルを作ってみました。

## Storybookのセットアップとエラー対処
StorybookはVueにも対応していますが、当初はReact向けに作られたようで、Vue向けのガイドラインがあまり充実していない部分もあると感じます。私は[こちらの記事](https://qiita.com/masaakikunsan/items/dad8d84807918f3a43cb)を参考にセットアップしました。ただ、記事内の`webpack.config.js`だと私の環境ではうまく動作しなかったため、少し変更しています。おそらくstylus関連だと思います。loadersをごっそり削除したら`npm run storybook`でStorybookが起動しました。

Storybookが無事起動できてホッとしていたら、`npm run dev`でエラー。。。

```shell
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! carousel-motion@1.0.0 dev: `webpack-dev-server --inline --progress --config build/webpack.dev.conf.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the carousel-motion@1.0.0 dev script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
```

詳しく調べていませんが、storybook/cliのバージョンを落としたら大丈夫でした。

```
- "@storybook/cli": "^4.1.4",
+ "@storybook/cli": "^3.2.17",
```

## Canvasアニメーション
StorybookではVueのpropsに値を渡してスタイルガイドの変更を行う仕組みになっているので、Canvasアニメーションのスピードやサイズなどの各種パラメーターをこのpropsで管理しておけば、Storybook上で調整できます。

```js:src/stories/index.js
...

storiesOf('CarouselMotion', module)
  .addDecorator(withKnobs)
  .add('Simple', withNotes(
    `
      column: レーンの数
      lineSpace: 行間
    `
  )(() => {
    const column = number('column', 12)
    const lineSpace = number('lineSpace', 5)

    return {
      components: { CarouselMotion },
      template: `<carousel-motion :column="${column}" :lineSpace="${lineSpace}"/>`
    }
  }))

...
```

## スマートフォン対応
Storybookには様々なaddonがありますが、ViewPortの切り替えには[addon-viewport](https://www.npmjs.com/package/@storybook/addon-viewport)というaddonがよく使われているようです。このaddonを入れるだけで、Storybook内にViewPortを切り替えるボタンが出現し、iPhone6やiPhon6 Plusなど各端末サイズやPortrait, Landscapeといった端末の向きを変更することができます。このStorybookの仕組みは、iframe内に自分のVueプロジェクトがhtmlとして埋め込まれる仕組みになっているようです。埋め込まれたプロジェクトのhtmlですが、デフォルトで`margin 8px`がついていて、なおかつスマートフォンモードだとbodyサイズが上手く取得できずレイアウトが崩れてしまいます。調べたところ、`.storybook`フォルダに`preview-head.html`というファイルを作成すると、iframeのhtmlのスタイルを変更できるようなので、以下のように設定して解消しました。

```vuejs:.storybook/preview-head.html
<style type="text/css">
  body {
    margin: 0;
    padding: 0;
    width: 100vw;
    height: 100vh;
  }
</style>
```

また、現状だとViewPortを切り替えてもCanvasサイズは自動で変更されないため、デバッグモード時だけViewPortの切り替えをフックして、Canvasサイズを再設定するようにしました。ここで少しハマったのが、iframe内にあるとscreenサイズが正しい値を返してくれず（おそらくPCサイトのサイズになっている）、デバッグモードだけCanvasサイズの設定を変更しています。


```vuejs:Canvasアニメーションのコンポーネント
<template>
  <canvas id="world" :width=screenWidth :height=screenHeight></canvas>
</template>

<script>
props: {
	...

	isDebug: {
		type: Boolean,
		default: false
	} 

	...
},
computed: {
	...

	screenWidth () {
		return this.isDebug ? window.innerWidth * 2 : window.screen.width * 2
	},
	screenHeight () {
		return this.isDebug ? window.innerHeight * 2 : window.screen.height * 2
	}

	...
},
mounted () {

...

	if (this.isDebug) {
		window.addEventListener('resize', this.handleResize)
	}

...

},
methods: {
	handleResize () {
		this.resetCanvasSize ()

		// NOTE: Other initialization code here
	},	
	resetCanvasSize () {
		const canvas = this.canvas
		canvas.width = window.innerWidth * 2
		canvas.height = window.innerHeight * 2
		canvas.style.width = String(canvas.width / 2) + 'px'
		canvas.style.height = String(canvas.height / 2) + 'px'
	}
}
</script>
```


サンプルコードは[GitHub](https://github.com/mitsuyacider/CarouselMotion)に置いてあります。