No Regrets in Bathing

カレーを週に一度食っていく

グリグリ動くUIをVueとSVGでサクッと書く

これは Vue.js #3 Advent Calendar 2017 – Qiita 4日目の記事です。

こんにちは。SVGで色々なコンポーネントを作っているものです。最近の作品は下記のような感じです。

f:id:hashrock:20171204200705g:plain

f:id:hashrock:20171204200933g:plain

Webでグリグリ動くUIを作りたい!!という一心でやっています。

これらはほとんどSVGとVueの組み合わせのみで作っています。依存が少ないというのは大事で、ライブラリ間の相性でハマったり、いろんなドキュメント間を往復することがなくなります。

Webでグリグリ動くUIを作るのは基本的にめんどくさいです。jQuery pluginを駆使して作るのも闇が多いですし、divやcanvasをゴリゴリするのも結構手間がかかります。

ですが、最近はSVGで高度なUI実装されることが増えてきた気がします。特に自分が衝撃を受けたのは、CacooがFlashからSVGにスイッチしたことです。

nulab-inc.com

SVGに遅いイメージを持っていた自分にとってこのニュースは衝撃的で、SVGやれば出来るじゃん!と勇気づけられました。というわけで、VueがSVGを標準で扱えることもあって、最近はSVGのポテンシャルを試していたわけです。他にもMSのSarahさんのVueConfでの発表もきっかけの一つでした。

SVGcanvasに比べて出来ることが多いわけではないのですが、だいぶ実装の手数が削減できます。サクッと実装できるかどうかの差は大きく、僕はこれでグラフィカルなUIを書こうというモチベーションが湧いてくるようになりました。 ※個人の感想です

SVGの基本的な使い方&バインディング

で、どのくらいサクッとなのかというと、例えば線を引きたければ、下記のように書くだけです。

<svg viewbox="0 0 300 300" width="300" height="300">
  <line x1=100 y1=100 x2=200 y2=200 stroke="black"></line>
</svg>

f:id:hashrock:20171204203229p:plain

で、canvasとは違って、この線はDOM要素なので、Vue.jsを使うとバインディングが効きます。すなわちこういうことが出来ます。

f:id:hashrock:20171204203728g:plain

コードはこれだけです。

<div id="app">
  <svg viewbox="0 0 300 300" width="300" height="300">
    <line :x1="x1" y1=100 x2=200 y2=200 stroke="black"></line>
  </svg>
  <div>
    <input type="range" v-model="x1">
  </div>
</div>
<script>
  new Vue({
    el: "#app",
    data: {
      x1: 100
    }
  })
</script>

バインディングが出来るフレームワークであれば、VueでなくてもReactでもRiotでもElmでもいいのですが、とにかくSVGバインディングとの相性が良いです。バインディングだらけになるとスパゲッティになる懸念がありますが、コンポーネントに切り分けることである程度防ぐことが出来ます。

SVG要素にイベントリスナをつける

また、SVG要素はイベントリスナをひっつけられます。例えば円をクリックした時にmethodを呼びたければ、これだけで良いです。

<circle @click="myMethod" cx=100 cy=100 r=50></circle>

これを利用して、クリックした時にトグルする何かを作りました。

f:id:hashrock:20171204211409g:plain

<svg viewbox="0 0 300 300" width="300" height="300">
  <circle r=50 cx=150 cy=150 @click="active = !active" :class="{'active': active}"></circle>
</svg>
new Vue({
  el: "#app",
  data: {
    active: false
  }
})
circle{
  fill: #333;
  transition: all 0.4s cubic-bezier(.96,.04,.04,.96);
  stroke: rgb(119, 0, 255);
  stroke-width: 1px;
}
.active{
  fill: white;
  stroke-width: 50px;
}

クリックした時に、active変数をトグルし、連動してclassもトグルさせています。で、切り替わる時にCSSアニメーションさせています。楽しいですね。

2017-12-05追記:IE11ではSVG要素のCSS Transitionは動かないそうなので、プロダクションで使う方はご注意下さい。

更に複雑なUIを作るには

以前書いた記事の中で、ヒントになりそうなものをピックアップしました。どれもVue + SVGでの実装です。

また、SVG要素に精通するのが大事です。MDNのSVG要素リファレンスを熟読しましょう。

  • コンポーネントとしてSVG要素をまとめる際は、groupという要素でラップします。groupにxやyという属性はなく、transform属性で変形する必要があります。
  • SVG子要素でクリック位置を取得する場合は、offsetX / offsetYの解釈がIE+Chrome / Firefoxで異なることに注意する必要があります。offsetX / offsetYは使わないほうが無難です。

終わりに

さて、高度なUIといえばドラッグ&ドロップですが、ざっくり言うとmousedown, mousemove, mouseupを各要素に付けてよしなにします。これに関しては紹介すると長くなるので、またいつかやり方を書こうと思います。

ドラッガブルな要素の実装についてはまだ研究中で、シンプルに書く方法を見いだせていません。ただカスタムディレクティブでさっくり行けるんじゃないかという手応えは感じています。一緒に研究してくれる仲間を募集しています。

おまけ

最後に、この記事を読んでSVGをやってみたくなった人に向けて、雛形HTMLを作ったので置いていきます。

svg-sandbox.html · GitHub

よく使うSVG要素を幾つか表示させているので、属性をいじるなり、Vueとバインドさせるなりさせてみてください。

f:id:hashrock:20171204214411p:plain

上記に表示されている基本図形の組み合わせで表現できるUIを考えて、実装してみるのがおすすめです。

ボツネタ

この記事を書くにあたって、無駄に作りかけてしまったSVGエディタです。何も使ってないじゃん。人間は愚かだ…

f:id:hashrock:20171204214737g:plain