コムセント 技術情報

  1. TOP
  2. コムセント 技術情報
  3. ブラウザだけで動くテスト用複数画像ジェネレーターを作ってみた

ブラウザだけで動くテスト用複数画像ジェネレーターを作ってみた

弊社では Web システムを採用する際にいわゆる SPA ではなく MPA を採用することが多いです。

バックエンドではほとんどの場合 CodeIgniter や Laravel を使用していますが、そのままこれらフレームワークが View と呼んでいるレイヤで HTML も作成してしまいます。

そんなわけで弊社のエンジニアが PHP と JavaScript をだいたい 7:3 くらいの割合で書いている……といった感じですが、もちろん画面の一部分が切り替わっていくような機能をを実装することもあります。

そんな時に弊社が採用しているのが Vue.js です。

僕も様々なプロジェクトで Vue.js を書いてきましたが、最近はバックエンド側のタスクが多かったので Vue.js からは若干遠ざかっていました。

そこで、リフレッシュ運転(開発?)がてら Vue.js + Vite + TypeScript で仕事で使えるテスト画像ジェネレータを作成してみました。

目的

我々エンジニアは参画しているプロジェクトのサービスでテストを行うことがしばしばありますが、画像を表示したりアップロードしたりするのは定番のテストです。

テストに使用する画像はハイクオリティである必要はないものの、

  • ・万が一社外に漏れれても権利上問題ない
  • ・誰が見てもテストが画像だと分かる
  • ・テスト目的に適した幅、高さ

というような条件を満たした画像が望ましいです。

ただ、このような画像は意外に手元にないことが多く、いちいち Photoshop を開いて画像を作成するのも面倒です。

そこで、サーバー無しのブラウザのみで動作する画像ジェネレータが欲しいと考えたので、これを開発することにしました。

ただし、単発のテスト画像を生成するサービス既に存在するため、大量の微妙に異なる画像を大量に生成し、 ZIP ファイルとして一括ダウンロードする機能も付け加えました。

作ったサービス

以下が実際に作成したサービスです。だいたい半日くらいかかりました。

https://go-noji.github.io/test-image-generator/

GitHub のリポジトリはこちら

作成する画像の設定項目として

  • 生成数
  • width(px)
  • height(px)
  • テキスト
  • テキストサイズ(px)
  • 文字色
  • 文字色の色相自動変更
  • 背景色
  • 文字色の色相自動変更
  • 背景の形

を設定できます。

テキストは複数行設定すると、生成される画像ごとに一行ずつテキストが描画されます。
文字色と背景色は色相の自動変更を on にすると、設定した色からスタートして生成される画像ごとに色相が変わって行きます。
色相は指定した生成数で色相環を等分し、最初から最後までで色相がちょうど一周する割合で変化します。

生成した画像は Zip ファイルとしてダウンロードすることも可能です。

開発 Tips

使用技術

冒頭でも説明しましたが、

  • フレームワークとして Vue.js
  • ビルドツールとして Vite
  • 開発言語として TypeScript

で開発を行いました。

弊社では普段ビルドツールには webpack, 開発言語としては TypeScript ではなく通常の JavaScript (場合によっては Babel などで変換)を使用することが多いのですが、今回はより最近のフロントエンド開発に近い(?)環境で開発してみました。

また、 Vue は Vue でも馴染み深い Options API ではなく Composition API で開発します。

個人的に以前の Options API に比べ、 Composition API では素の JavaScript に近くなったことにより TypeScript の旨味が増え、快適に開発が進められるように感じられました。

コンポーザブルの利用

Vue においてコンポーザブルとは機能単位でカプセル化した関数のことを指します。

Vue は本来コンポーネントごとに、それに付随するふるまいとして JavaScript を書いていくようなアプローチで、それは現在でも変わっていません。

しかし、機能が複雑になっていくと、どこのコンポーネントにも属していない・または複数のコンポーネントに関わりのあるようなロジック部分が発生します。

このようなロジックをコンポーネント・もしくはテンプレートから独立させたのがコンポーザブル関数ということになります。

今回のケースでは

・ユーザーの入力した各設定情報を一元管理し、更新・整形情報の取得などの API を通して、各コンポーネントに提供するコンポーザブル(useControls.ts)
・それら情報のバリデーションを実行し、エラーメッセージの自動生成を行うコンポーザブル(useValidator.ts)
・プレビュー表示用の Canvas or 画像生成用の Canvas に対する描画ロジックを担当するコンポーザブル(useCanvas.ts)

を用意しました。

これらにより各 .vue ファイルの肥大化が抑えられ、テンプレートに近い部分のロジックに集中させることができました。

Provide / Injection によるグローバルステートの管理

先ほど紹介した内容と若干被るのですが、かつて Vuex が担ってきたグローバルステートも Vue の標準機能である Provide / Injection とコンポーザブル関数でまかなえるようになりました。

同じ物事を表す情報をコンポーネントそれぞれが別に持ってしまうとデータの同期が大変なので、できればデータは一元管理したいものです。

しかし、全てのコンポーネントで一元情報にアクセスするとなると、 Vue のコンポーネントは木構造を取っている以上、そのデータは App.vue に置かざるを得ません。
しかも大本の App.vue から遠い位置にある情報にアクセスするためにはその間にある全てのコンポーネントを巻き込んで Props と Emits によるバケツリレーを行わなくてはなりません。

これを解決するために Provide / Injection という機能が用意されているのですが、これとコンポーザブル関数を組み合わせると強力なデータストアを扱うことができます。

コンポーザブル関数内で ref() や reactive() により作成されたリアクティブな変数を全てのコンポーネントに配信することで、バケツリレー無しに銃砲の CRUD 処理が可能になりました。

今回のサービスでは useControls.ts をシングルトン的なコンポーザブル関数として使用し、 App.vue で provide したものを各コンポーネントで inject するという形になっています。

逆にバリデーション用・ Canvas 用のコンポーザブル関数はそれぞれバリデーション対象項目・ canvas 要素ごとに設定値が違うので、一元化するのではなく別個に関数を生成しています。

こうしてみるとコンポーザブル関数は実態がただの関数ながら、オブジェクト指向を代表する Class とインスタンスのような振る舞いをしていますね。

各フォーム部品のベースコンポーネントの用意

今ままで Vue を使用する際は一つのコンポーネントに大量の HTML テンプレートを書き、サブとなるコンポーネントはあまり用意せず……という大味な開発をしたケースが多くありました。

今回はもうちょっとコンポーネントの粒度を細かく設定してみようと考えたので、繰り返し登場する input タグのような部品を基底コンポーネントとして作成してみることにしました。

なぜなら Vue のスタイルガイド( https://ja.vuejs.org/style-guide/rules-strongly-recommended.html#base-component-names )にて基底コンポーネントとしての命名規則が説明されていたので、少なくとも公式が言うのであればこの類のコンポーネントを切るメリットはあると考えたからです。

結果、今回使用する機会のあった
・一行テキスト入力欄(兼数値入力 or カラーピッカー)
・複数行テキストエリア
・セレクトボックス
・On / Off スイッチ
をそれぞれ Component として用意してみました。

実際に作成してみたところ、一度コンポーネントを作ってみてしまえば、差分は Props と Emits で設定・表現すればいいだけなので、同じようなフォーム部品の複製はスムーズに行えました。

入力情報はコンポーザブルで一元管理しているので、自信はデータを持たずに Props と Emits でデータを流すことにより、密結合を避けられたと感じます。

TypeScript の採用

型パズルとも表現される難解なコードを見ていると採用をためらってしまうような面もありますが、少なくとも今回のサービス開発において TypeScript 便利な言語でした。

特に ref や reactive, computed に

ref<string>('');

のように型を指定しておくと、後続のコードでも何が返ってくるのか分かるため、ある意味ドキュメント代わりになって開発効率が上がったのは興味深い発見でした。

他にも設定したいバリデーションルールを引数として設定する時も、

// バリデーション指定時の型
export type REQUIRED = 'required';
export type COLOR = 'color';
export type NUMBER = 'number';
export type INTEGER = 'integer';
export type MAX = {rule: 'max', value: number};
export type MIN = {rule: 'min', value: number};
export type LIST = {rule: 'list', value: string[]};

// バリデーション指定
type RULE = REQUIRED | COLOR | NUMBER | INTEGER | MAX | MIN | LIST;
type RULES = RULE[];

のような型を用意しておくと、引数に RULES 型を指定しておくだけで、使用する側で何を設定すべきかが分かりやすくなるのも似たようなメリットです。

僕は普段 PhpStorm で開発をしていますが、ロジック部分を TypeScript で書くといつの間にかテンプレート側でも型が認識されていたので、これからもプロジェクトの条件に合えば TypeScript を採用したいと感じました。

まとめ

他にも色々試したい新技術は山のようにありますが、今回はベーシックな構成で単純なサービスを作ってみたからこそ様々な発見がありました。

なお、五角形の書き方やその他ライブラリの使用方法を調べる際には ChatGPT が大いに役立ちました。

そのままのコードをコピペするだけでは動かなかったり、そうでなくともコーディング規則にそぐわなかったりすることもしばしばですが、見当のつきづらい解決方法の道しるべを簡単に答えてくれるのは便利です。

その内簡単な仕事は代替されると言われていますが、AI には低レイヤーの問題を解決して貰ったとしても、それらをサービスとして組み立てあげる部分はまだ人間が行う仕事なのかなと感じます。

今やググるだけでなく AI に聞く・仕事させることで情報やプロダクトを得る時代ですが、 AI が出してくるコードを読み解いて自分のモノにできるよう、基礎はしっかり押さえておきたいものです。

五角形を Canvas で描くコードは ChatGPT に聞いちゃいましたが、星型は自分で応用して書けましたよ! 目指せエンジニア界隈の星!(無理)

プログラマー/N.Go

CodeIgniterやLaravel、Vue.jsといったフレームワークを用い、ECシステム、リアルタイム課金制生配信、掲示板ライクなSNSシステムなどのWebシステム制作に携わる。 プロジェクトによってはフロントエンドも一貫して請け負う。

このメンバーの記事一覧へ

おすすめ記事