2023年7月19日水曜日

日本地図パズルを作った

Web 上で遊べる都道府県パズルを作ってみました。 普通の都道府県パズルと違って、なんと 12 レベルもあります。 レベル 9 くらいまでは子供向きで簡単ですが、レベル 10 になると大人でも難しいです。



最終的には割と良いものができたと思うのですが、意外と事前準備に苦労しました。

都道府県の SVG が難しい

この手のアプリを作る時、みんなどうやって作っているんでしょう。 都道府県アプリをいくつか確認しましたが、どれもデータの出典が書いてないです。 どうやって地図を作っているんでしょう。 まあ作っている人がほぼいないので沼…が真実の気はします。

ちなみに 47都道府県のポリゴンデータは国土数値情報にあってGeojson もあって軽量化もできる ことはわかっています。 OpenStreetMap Region なども使えるのではないかと思います。 真面目に作るならそれらを SVG 化したほうがライセンス的にもデータ的にも良いのですが、とにかく面倒です。

svg-maps はそれなりに使いやすいですが、 北方領土が載ってないため歴史や地理の説明では使いにくい問題があったり、 韓国のウルルン郡がなぜか日本地図に入っていたり、与那国島がなかったり、 離島がたくさん消失していたり、鹿児島県と沖縄県の県境が間違っていたりします。 このように細かな領域ではたくさんの問題があって、教育用途での利用は少々厳しい印象があります。

結局のところ一番使いやすいデータは Wikipedia 由来の geolonia/japanese-prefectures だと思います。 ただこれも細かい部分は調整しないと使えないので、調整した marmooo/japanese-prefectures を作って公開しました。 しかしこれも改めて見てみると、湖がほとんど書かれていないのはわずかな不満点です。 Wikipedia の Japan Map Lincun.svg などを使って今後作り直すかもしれません。 困ったら Wikipedia から拝借するのが、結局のところ一番良さそうです。

Resizable, Rotatable, Draggable をどう作る?

パズルを作るには、JavaScript で最低限 Rotatable, Draggable に対応しないといけません。 今回は少し難しいパズルを作ろうと思ったので、Resizable にも対応する必要があります。 しかしこれらの操作に対応したライブラリはほとんどありません。 DOM 系だと MoveableSubjx しかないと思います。 実質的に Moveable 一択なんじゃないかな。

Moveable は使ってもみたのですが、処理がちょっと重いです。 理由は 8つの点と 4つの線で移動用の矩形オブジェクトを作っているのですが、その描画に position: absolute; を使っているからです。 普通に考えれば動かしたいオブジェクトに wrapper として矩形オブジェクトを付ければ position:relative; で表現でき、座標情報の更新は不要です。 処理の負荷は 1/10 くらいにできそうなので、今後の更新に期待です。 改良が続けばとても良いライブラリになりそうだけど、今のところは これ とかを改良したり、 他の解決方法に頼ったほうが良さそう。

個人的には DOM 操作でも十分な速度が出るとは思うのですが、現状では微妙だったので Canvas 系を採用することにしました。 あと SVG は DOM 数が増えすぎるとすぐ怒られそうだしな…。 もともと fabric.js は使えるとわかっていました。 他にもベンチマーク結果などを見ながら 軽く探したのですが、 どれも複雑な操作には対応していなそうです。 Konva.js は使えるかも知れませんが、ちょっとコードが長いかな。 という訳で今回は fabric.js です。


SVG の扱いが難しすぎる

コードを書き始めてからは割とすぐモックは完成しましたが、object タグから SVG を読み込むときの挙動が複雑で、最後の調整に苦労しました。 object タグはタイミング次第で getBoundingClientRect() が効かなかったり、アスペクト比を保持してくれない問題があります。 また横方向にサイズを変更すると中の SVG をリサイズしてくれるのですが、縦方向だとリサイズしてくれなかったりします。 そして object タグは object-fit: contain; がきちんと発動しなかったりします。 癖が強くて難しかったです。

さらにコンテンツの中身が viewBox からはみ出している部分のある SVG は、アスペクト比が崩れると、 はみ出している部分が表示されてしまうこともわかりました。 これは position-top テクニックも使えないし、たぶん解決策がないです。 SVG は viewBox からはみ出ないように事前にきちんと調整しておくしかないのかな…。 SVG さん本当に細かなところで難し過ぎるので、涙が出ます。

ただまあ、データがないとか、ライブラリをどうしようといった問題のほうが、手戻りが発生しやすくて面倒でしょうかね。 Canvas をゴリゴリ使ったアプリは作ったことがなかったですが、思っていたよりは簡単でした。

0 件のコメント: