サーバーサイドを主戦場とする俺だが、自発的にグリーティングカードを作る

リブセンスでエンジニアをしている大波です。こんにちは。
日頃はレガシーコードの供養をしたり、お金の計算をしたりしている僕です。

そんな僕ですが、12月ともなればいささかロマンティックなことをしたくなりました。
クリスマスなんかもやってくるんでhtmlでグリーティングカード的なものでも作ってみましょう。

完成したもの

とりあえずできたのはこちら。星をクリックしてみてください。
https://z-ohnami.github.io/svg_sample/

これは全てSVGを配置して表現しています。
作るときの方針としては、jpgとかの画像は使わないこと、SVG自体もIllustratorみたいなツールは使わないでjavascriptの中で書けるレベルで用意することにしました。

どうやって作ったか

タグを打ち込んで行けば完成はするのですが、それは大変な行為です。
素晴らしきライブラリを使わせていただきました。
SVGの描画にはSVG.jsを利用しました。
手で書いていく風な演出にはvivusを利用しました。

両方とも初見だったのですがわかりやすいドキュメントもあるし、すんなりと書くことができました。ここでこの機能ほしい、と思うものは大体揃っていて、直感的に書けるのでとても使いやすいですね。

ソースはこちら
https://github.com/z-ohnami/svg_sample/blob/master/index.html

初期化

最初はこんな風にしてSVGをやりますよ宣言をします。描画する大きさを指定してdrawするためのインスタンスを作る感じですね。それを使うと、図形とか線とかを書いていけるようになります。いろんな関数をチェインでつなげていくことができます。

3行目のattr('id')は描画した要素に自分でidを付与することができます。後ほど、演出を入れたりする際に、このidを指定して対象をつかむことができるので、大変便利です。

const draw = SVG('drawing').size(window.innerWidth, window.innerHeight)
const rect = draw.rect(window.innerWidth, window.innerHeight).fill('#191970')
rect.attr('id', 'background')

最初の星ボタンを追加する

index.htmlを表示させたときに、最初に表示している星の描画がここです。buttonはgroupというものを定義していてそこに、星のSVGとテキストを追加しています。そうすると、groupに対して一括で色ぬりや位置の調整をかけることができるのですこぶる捗りました。FlashのActionScriptとかでいうとaddChildしているみたいな感覚ですね。

animateのeasingを不等号で設定することができます。この辺はSVG.js独特の発想な気もしますが、端的に表現できるし、結構面白くて気に入りました。

on clickイベントでdrawScene関数を作動させています。

const button = draw.group()
button.add(hoshi)
button.add(hoshiText)
button.on('click', drawScene, this).center(centerX, centerY).fill('#fff')
button.animate(2000, '>').fill('#191970').reverse(true).loop()

vivus発動

drawScene関数では描画した家のパーツをgroupに溜め込んで、vivusの実行処理に登録をしています。こうすると、あとはvivusが勝手にやってくれます。アニメーションの演出とか、色々凝ったこともたくさんできそうですが、家でシチューを作らないといけなかったので、深堀りはしませんでした。

group.attr('id', 'group1')
group.add(kabe)
group.add(mado)
group.add(yane)
group.add(entotsu)
group.center((window.innerWidth / 2) + 80, window.innerHeight - jimen.height()).fill('none')
    .stroke({ color: '#fff', width: 6, linecap: 'round', linejoin: 'round' })

new Vivus(group.attr('id'), {duration: 200}, animateAfter)

animateAfter関数でやっている処理ですが、こんな風にSVG.get(xxx)でidを指定して各要素にアクセスができます。これをつかって、vivusで書き込みが終わったタイミングで家に色を入れています。

const background = SVG.get('background')

const block = SVG.get('house-block')
block.fill("#191970")

星の大量生産

vivusの描画終わった後で、星をバラ撒く処理を入れました。
力技です。いいんです。ワンショットの挨拶状なんですから。

background.after(hoshi)とすることで、配置する際の重なりを調整しています。
これをやらないと、星が家の前に出現したりして、ちょっと残念な感じになってしまいます。
進めていて、あ、やべ、これおかしい(汗)ってなったのですが、SVG.jsに助けられました。SVG.jsすごい。
他にもbeforeとかbackとか大体欲しいのが揃っている。SVG.jsすごい。

const hoshiRandomDelays = [0, 100, 300, 600, 900, 1200]
for (let i = 0; i <= 200; i++) {
    const hoshiX = Math.floor( Math.random() * window.innerWidth);
    const hoshiY = Math.floor( Math.random() * (window.innerHeight));
    const hoshi = draw.polygon('6 2 2 12 12 6 0 6 10 12').fill('#191970').x(hoshiX).y(hoshiY)
    background.after(hoshi)
    const duration = Math.floor( Math.random() * window.innerWidth);
    hoshi.animate(400, '-', hoshiRandomDelays[Math.floor( Math.random() * hoshiRandomDelays.length )]).fill('#f0e68c')
}

NEXT?

なんか調子に乗って来たので画像をブンブン振り回すような面白いやつをもっと作っていきたいですね。
そうした興味を持つことができたのが、この記事を投稿しての最大の成果でありました。

ちょっとお硬めのビジネスアプリでも、こうした演出強化系の仕組みをさりげなく取り入れる工夫をするとユーザーへのフィードバックが向上して快適に使ってくれるかもしれないなぁと思いました。入れすぎは禁物ですが、最後のひと手間的に入れてみることも来年の活動では是非狙っていきたいですね。

できたものを奥さんに見せたらちょっとだけ喜んでくれました。
今年もありがとう、愛してるよ。