feature image

2024年3月17日 | ブログ記事

[SolidStart] アイコンメーカーを自作した

この記事はtraP新歓ブログリレー2024 10日目の記事です。

こんにちは@d_etteiu8383です。@eyemono.moeでもあります。
本記事では自作のアイコンメーカー (https://icon.eyemono.moe) の開発について紹介します。

eyemono.moeメーカー
君だけのeyemono.moeを作り出せ!

アイコン

弊サークルで使用されているメッセージングアプリtraQでは、ユーザーアイコンを設定することが可能です。このアイコンは本ブログ上でも表示されていますね。
対面イベント以上にtraQでのコミュニケーションが活発なtraQでは、アイコン画像が部員を象徴する重要な要素となっています。

私が現在使用しているアイコンは2020年に(グラフィック班によるクリスタ講習会中に)作成したものです。
その後「エンジニアたるもの自分のアイコンも管理しやすい形式にすべきだ」との思いからSVG形式で作成し直し、オリジナルデータをFigmaで管理するようになり、Figmaのcomponent機能を使って色々なバリエーションを作成できるようになりました。

Figma上でのアイコン管理

せっかくならWebアプリ化してしまおうということで、アイコンメーカーを作成することにしました。

実はアイコンメーカー自体は2022年に一度作っていたのですが、メンテナンス性の悪さから放置してしまっていました。今回はアイコンメーカーを再構築し、より使いやすく、メンテナンスしやすいものにしました。

今回作成したアイコンメーカー

開発

使用技術

実装方針

基本的な仕組みとしては、アイコンの目, 口, 髪等の各パーツのsvgをそれぞれをcomponentとし、これらを組み合わせてアイコンを生成しています。
パーツの種類(口の形など)や色はcontextで管理し、各パーツcomponent内でこれを参照して色の変更等をしています。

↓目のパーツ例

<Portal>コンポーネントについては後述します

各パーツの切り替えには<Switch>コンポーネントを使用しています。contextから参照したパーツ種類を<Parts>コンポーネント内で出し分けている形です。

以下に示す<Icon>コンポーネントがアイコンの本体となっているコンポーネントで、この中で<Parts>コンポーネントを呼び出して各パーツを表示しています。

<Portal>コンポーネントの利用

Solidには、ReactにおけるcreatePortalと同様の機能を持つ<Portal>コンポーネントが用意されています:Portal - SolidDocs

例えば先述した目のパーツは、ただ肌部分の上に乗っているわけではなく、「白目部分」 < 「髪部分」 < 「まつ毛部分」のレイヤー順序になっており、間に別パーツである髪部分があるため2箇所に分けて表示する必要があります。

アイコン内の各パーツのレイヤー順序

このほかにも、髪パーツでは「後ろ髪」「顔にかかる影」「前髪」の3パーツをそれぞれ異なるレイヤーで表示する必要があります。

このようなパーツも<Portal>コンポーネントを使用することで、単一コンポーネント内の記述で複数のレイヤーにまたがるパーツを表示することができました。

この<Portal>コンポーネントの表示先を、先述の<Icon>コンポーネント内に記述しています(<g id="eye-upper-target" />等の部分)。

サーバーサイドでの画像生成

アイコンメーカーの本体はクライアントサイドで動作し、画像の保存処理等もクライアントサイドで可能になっています。
しかし、利便性を考えると、URLから直接アイコンをimage/pngimage/svg+xmlとして取得できると便利そうです。

本アイコンメーカーでは、API Routesとして/imageへのGETリクエストを受け付け、サーバーサイドで動的に画像を生成して返すようにしています。

このAPI Routes内では、先述の<Icon>コンポーネントをsolidのrenderToString()関数で文字列に変換して返しています。

アイコンメーカーページでは、パーツ種類や色情報をlz-stringを利用してクエリパラメータ内に保存しているのですが、このパラメータを/imageエンドポイントへのGETリクエストにそのまま渡すことで、サーバーサイドでもそのパーツ・色のアイコンを生成することができます。

Node.jsで動作する画像処理ライブラリのsharpを使用したpngへの変換等も行っています。

サーバーサイドでの<Portal>コンポーネントの描画

<Portal>コンポーネントを使用した別要素への描画は、クライアントサイドでelement.appendChild()を使用することで実現されています。そのため、サーバーサイドで実行されるrenderToString()内では、通常の<Portal>コンポーネントをそのまま使用しただけでは描画されません。

↓SSR時は何もしない<Portal>コンポーネント君

そのため本アイコンメーカーでは、<Portal>コンポーネントをラップした独自の(闇の)コンポーネント<ServerPortal>を作成し、サーバーサイドでの描画を可能にしています。

絶対もっとスマートに書けそうですが、とりあえず動いているので...

とにかくこれで何とかサーバーサイドでの画像生成に成功しました。これによりアイコンメーカー自体のOGPでも動的に生成したアイコン画像を使用できるようになりました。
アイコンメーカーページのURLをSNS等に貼ると、その時の編集内容がOGPに表示されます。

eyemono.moeメーカー
君だけのeyemono.moeを作り出せ!
eyemono.moeメーカー
君だけのeyemono.moeを作り出せ!

まとめ

Webアプリとして動作するアイコンメーカーを作成しました。SolidStartを使用したことで、クライアントサイドとサーバーサイドの両方での画像生成を実現することができました。
皆さんもぜひ自分だけのアイコンメーカーを作ってみてはいかがでしょうか。


最後までお読みいただきありがとうございます。明日の新歓ブログリレー2024担当者は@comaviusです。楽しみ~

d_etteiu8383 icon
この記事を書いた人
d_etteiu8383

グラフィック班とゲーム班とSysAd班所属 いろいろ活動しています

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2023年12月11日
DIGI-CON HACKATHON 2023『Mikage』
toshi00 icon toshi00
2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2024年6月21日
ハッカソン参加記 4班"Slide Center"
Alt--er icon Alt--er
2024年3月22日
traPグラフィック班の活動紹介2024
haru10 icon haru10
2022年9月26日
競プロしかシラン人間が web アプリ QK Judge を作った話
tqk icon tqk
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記