私が今年最もハマった漫画「約束のネバーランド」で登場する、ミネルヴァさんのモールススタンプをVue.jsで作ってみました。
まずは完成したものをどうぞ…
こちらで実際に遊べます
いらすとやさんのおかげもあって、なんだか本家よりもファンシーになっていると思います。
コードの解説
まず、スタンプのSVGはVueのコンポーネントになっていて、propでメッセージを渡すとスタンプのSVGが作られます。
こんな感じです。
<MorseStamp message="sos" />
それでは、MorseStampコンポーネントの中を解説していきます。
※ 解説のために一部コードを変更したりしています。
メッセージのモールス化
morse
というnpmパッケージを使っています。
アルファベットの文字列を簡単にモールス化してくれます。
const morse = require('morse')
console.log(morse.encode('SOS')) // '... --- ...'
こんな感じで使えます。ありがたいですね。
モールスの描画
コミックスの3巻でいくつか出てくるモールススタンプを見てみると、どうやらこんな感じのルールで描かれているようです。
- 左上から時計回りに一周して読む
- 1つの英単語がしっかり一周(360度)に収まっている
- モールスで
.
と-
があるうちで、.
は決まった長さ、-
は余った長さを均等に割った長さ
まずは morse
から取得したモールスの文字列を配列にして、 -
の長さを計算します。
const DOT_AND_SPACE_WIDTH = 9 // `.` と ` ` の長さ
const morseString = morse.encode(this.message)
const chars = `${morseString} `.split('')
const barCount = chars.filter(char => char === '-').length
const dotAndSpaceCount = chars.length - barCount
const barWidth = (360 - DOT_AND_SPACE_WIDTH * dotAndSpaceCount) / barCount // ← `-` の長さ!
あとはそれぞれのモールス符号をSVGで描画するときのパラメータ計算していきます。
const DOT_AND_SPACE_WIDTH = 9 // `.` と ` ` の幅
const MORSE_HEAD_DEGREE = -135 // 左斜め上から時計回りに読むらしい
let previousEndDegree = MORSE_HEAD_DEGREE
return chars.map(char => {
const charWidth = char === '-' ? barWidth : DOT_AND_SPACE_WIDTH
const startDegree = previousEndDegree
const endDegree = startDegree + charWidth
previousEndDegree = endDegree
if (char === ' ') {
return ''
}
// pathタグのd属性の値を取得
return getPathTagDAttrValue(startDegree, endDegree)
}).filter(s => s !== '')
モールス符号の開始位置と終了位置の度数を受け取って、SVGの <path>
のパラメーター(d属性)を返すところ
const CHAR_OFFSET = 2 // 符号ごとの間隔
const degreeToRadian = (degree) => degree * Math.PI / 180
const getPathTagDAttrValue = (degree1, degree2) => {
const radian1 = degreeToRadian(degree1 + CHAR_OFFSET)
const radian2 = degreeToRadian(degree2 - CHAR_OFFSET)
const x1 = Math.sin(radian1) * R + O
const y1 = Math.cos(radian1) * R + O
const x2 = Math.sin(radian2) * R + O
const y2 = Math.cos(radian2) * R + O
const f1 = (degree2 - degree1) >= 180 ? 1 : 0
return `M ${y1},${x1} A ${R} ${R} 0 ${f1} 1 ${y2},${x2}`
}
ここまでできたら、あとはv-forでまわすだけ!
<template>
<svg>
<path
:d="signPath"
:key="signPath"
v-for="signPath in signPathes"
/>
</svg>
</template>
おまけに背景の色と、ふくろうの画像をつけます
<rect fill="#f6eace" />
<image xlink:href="@/assets/animal_fukurou.png" />
Vue.js と SVG
今年はなにかとVue.jsでSVGを書くお仕事が多かったのですが、この組み合わせはかなり相性が良いなと思っています。
慣れ親しんだSVGにちょっとVue.jsのコードを書き加えるだけで気軽に動的なSVGが描画できますし、他のSVG描画系のライブラリを導入するよりも学習コストが低いのも嬉しいですよね。
アニメーションなどの表現の作り込みに関してはD3.jsやTweenMax.jsの方が得意ですが、データによって瞬時に変わる簡単なグラフなどは、Vue.jsの方がシンプルに書けそうです。
最後に
2020年は、約束のネバーランドのアニメの二期もあるとのことなので楽しみですね!
それではみなさん、
(merry christmas)