10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LAPRASAdvent Calendar 2019

Day 19

[約束のネバーランド] Vue.jsでミネルヴァさんのモールススタンプを描いてみる

Last updated at Posted at 2019-12-18

私が今年最もハマった漫画「約束のネバーランド」で登場する、ミネルヴァさんのモールススタンプをVue.jsで作ってみました。

まずは完成したものをどうぞ…

スクリーンショット 2019-12-18 0.13.09.png

こちらで実際に遊べます
いらすとやさんのおかげもあって、なんだか本家よりもファンシーになっていると思います。

コードの解説

まず、スタンプのSVGはVueのコンポーネントになっていて、propでメッセージを渡すとスタンプのSVGが作られます。

こんな感じです。

<MorseStamp message="sos" />

それでは、MorseStampコンポーネントの中を解説していきます。
※ 解説のために一部コードを変更したりしています。

メッセージのモールス化

morse というnpmパッケージを使っています。
アルファベットの文字列を簡単にモールス化してくれます。

const morse = require('morse')
console.log(morse.encode('SOS')) // '... --- ...'

こんな感じで使えます。ありがたいですね。

モールスの描画

Group.png

コミックスの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年は、約束のネバーランドのアニメの二期もあるとのことなので楽しみですね!
それではみなさん、

スクリーンショット 2019-12-19 2.24.31.pngスクリーンショット 2019-12-19 2.24.42.png

(merry christmas)

10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?