新たなプロジェクト「RECTGRAPH(レクトグラフ)」を公開しました。
アナログシンセをモチーフにした、SVG イメージを生成するシンプルなウェブアプリです。SVG は 30 個の矩形要素(<rect>
)により構成されており、古き良きドット絵風のイメージを生成できます。公開時は 10 個のプリセットを用意しています。
生成される矩形要素の配置パターンはランダムなので図柄は毎回異なります。また、この矩形要素のカラーやエフェクトを調整して見た目を変更することが可能です。
なお、本アプリにはシンセサイザーで使われている名称(「CUTOFF」や「RESONANCE」)が使われていますが、厳密にシンセの仕組みと一致するものではありません。
使用方法
見出し「使用方法」本アプリは以下の機能で構成されています。
1. PREVIEW
見出し「1. PREVIEW」イメージのプレビューが表示されます。この SVG 内の矩形要素(<rect>
)の配置はランダムなので、毎回異なる図柄になります。
2023/06/29 追記 プレビューのサイズを変更しました。
2. TITLE
見出し「2. TITLE」タイトルはダウンロード時のファイル名や、ダウンロードしたの SVG の <title>
に設定されます。このタイトルは自由に変更できます。
3. COLOR
見出し「3. COLOR」「BASE」は、ベースとなる背景カラーです。「OSC.1」は矩形要素(<rect>
)のカラーです。「OSC.2」も同様に矩形要素のカラーですが、横幅が 2 マスの場合にのみ適用されます。
ちなみに OSC は Oscillator(オシレーター、発振器)の略称で、シンセサイザーにおいては音の基本となる波形を発生させるために用いられます。
4. LEVEL
見出し「4. LEVEL」「OSC.1 LEVEL」と「OSC.2 LEVEL」の 2 つのパラメータを持ち、矩形要素それぞれの不透明度(opacity
)を調整できます。本アプリで指定できる色数は最大 3 色ですが、透過率を加えることで立体的な効果が生まれます。
5. FILTER
見出し「5. FILTER」「CUTOFF」は、矩形要素を移動・変形する量を指定します。値を大きくすると、各矩形要素が右下から切り取られ、サイズが小さくなります。
「RESONANCE」は矩形要素に対して歪み効果を与えます。値を大きくすると振動数が増えます。「CUTOFF」の値が 0
のときには変化が生じませんが、「CUTOFF」の値を大きくするほど目立った効果が得られます。
6. MIX
見出し「6. MIX」「MIX」ボタンを選択すると、プレビュー内の矩形要素の配置がシャッフルされます。
7. DOWNLOAD
見出し「7. DOWNLOAD」「DOWNLOAD」ボタンを選択すると、プレビュー内容の SVG ファイルがダウンロードできます。
ダウンロードできる SVG ファイルのライセンスは MIT License で、SVG のコードにライセンスに関するコメントを含めています。
8. VIEW MODE
見出し「8. VIEW MODE」ビューモードは、プレビューエリアでの SVG の見え方を変更する機能です。モードを有効にすると、すべてのコンポーネントに設定が反映されます。
「DISC」を選択すると、プレビューの SVG が正円に収まります。
「3D」を選択すると、プレビューの SVG が変形し遠近感が得られます。
ダウンロードファイルに「DISC」や「SVG」の効果は付きません。
既知の問題
見出し「既知の問題」現時点では以下の問題を認識しています。
グリッドの隙間
見出し「グリッドの隙間」閲覧ブラウザや表示される SVG のサイズ、矩形要素の組み合わせによっては、矩形要素が配置されるグリッドにわずかに隙間が生じます。
ダウンロードファイル
見出し「ダウンロードファイル」ダウンロードした SVG ファイルは、一部の環境では開くことができませんのでご了承ください。
また、SVG ファイルを主要なブラウザで開くことはできますが、デザインツール(Adobe Illustator や Figma)で開くとフィルタ効果が正しく反映されません。
実装内容
見出し「実装内容」ここからは、具体的な実装内容を説明していきます。
矩形要素の配置
見出し「矩形要素の配置」SVG イメージには、以下の 4 パターンの矩形要素(<rect>
)を配置しています。
矩形要素の配置のルールは以下のとおりです。
- 矩形要素の数は 30 個
- 4 パターンのいずれかがランダムに配置される
- すでに要素が配置されていてもその上に重なることがある
- 横幅が 1 マスの場合には「OSC.1」のカラー、不透明度が適用される
- 横幅が 2 マスの場合には「OSC.2」のカラー、不透明度が適用される
- 上下左右の 2 マスは余白(Clear Space)のため配置されない
「MIX」ボタンを選択したときも同様で、再び矩形要素をランダムに配置します。
カラーピッカー
見出し「カラーピッカー」ブラウザネイティブの <input type="color">
ではブラウザごとに使い勝手が大きく異なるため、「Coloris」というカラーピッカーのライブラリを導入しています。
ただ、デフォルトではカラーピッカーを開くと入力フィールドにフォーカスが当たるのですが、モバイル端末ではバーチャルキーボードが開いてしまうのがわずらわしいため、オプションで focusInput: false
を指定し、フォーカスが当たらないようにしました。
しかし、この状態だと、キーボード操作でカラーピッカーを閉じたときにフォーカス位置が元の位置に戻らないため、イベントリスナーを追加して調整しました。
color.addEventListener('close', (e) => e.target.focus());
また、スクリーンリーダーを有効にした状態では、十分にカラーピッカーのキーボード操作ができないようなので、苦肉の策ではありますが、カラーピッカーを開くボタン要素に aria-disabled="true"
を付与しました。
フィルタ
見出し「フィルタ」「CUTOFF」と「RESONANCE」の機能は SVG フィルタの <feTurbulence>
と <feDisplacementMap>
の組み合わせで実現しています。
おおまかに説明すると、「CUTOFF」の値を大きくすると <feDisplacementMap>
の scale
の値が大きくなり、各矩形要素が右下から切り取られ、サイズが小さくなる効果が得られます。
<feDisplacementMap>
は画像と組み合わせて、その画像の RGBA チャネルに応じて、X 軸と Y 軸を変位させることもできるようなのですが、このアプリではそのような使い方はしておりません。
一方の「RESONANCE」は、値を大きくすると、<feTurbulence>
の baseFrequency
の値が大きくなり、矩形要素の歪みの効果がより細かくなります。この歪みの効果は、「CUTOFF」の値が 0
だと発生しませんが、値を大きくするほど顕著になります。
正直なところ、SVG フィルタに関しては深く理解できていないので、以下の記事を参考にしつつ、試行錯誤するうちにこの形に落ち着きました。
- SVG Filter Effects: Conforming Text to Surface Texture with <feDisplacementMap> | Codrops(外部リンクを開く)
- SVG Filter Effects: Creating Texture with <feTurbulence> | Codrops(外部リンクを開く)
ビューモード
見出し「ビューモード」「DISC」と「3D」による見た目の切り替えは、いわばオマケの機能です。
「DISC」は単純に CSS の border-radius
で見た目を変更し、「3D」は transition3d()
関数を使用しています。
「3D」の実装方法については、以下の記事を参考にしました。
ダウンロード
見出し「ダウンロード」「ダウンロード」ボタン選択時には、以下の処理を実行しています。
- ファイル名の作成
- SVG コードの整理
- スタイル指定の調整
- 不要な属性の削除
- フィルタを使用していなければ
<filter>
要素を削除
- ライセンスコメントの追加
- ファイル圧縮(空白や改行の削除)
- ファイルのダウンロード
- Blob オブジェクトの作成
- ダウンロード用に
<a>
要素を生成してclick()
イベントを実行
- 後処理
SVG イメージのダウンロード方法については、以下の記事を参考にしました。
プロジェクトの経緯
見出し「プロジェクトの経緯」このプロジェクトは、「CSS コンテナクエリ考察」というブログ記事を書いた後に、コンテナクエリの使い方の可能性を調べる目的で、CSS Grid と CSS コンテナクエリの組み合わせた実験をするなかで生まれました。
当初は、CSS Grid で 10 x 10 に区切ったマスに、空の <div>
要素を grid-area
で配置し、コンテナクエリで @container (inline-size > 10cqi) { ... }
と指定し、基準コンテナのインラインサイズが 10% を超える場合に色を変更する仕組みでした。
ただ、以下の点より、最終的には SVG での実装に変更しました。
- 表示サイズによって、コンテナクエリ(
@container
)のスタイルが適用されないことがある - 画像のダウンロード機能を実装したい
前者については、特に macOS Safari が顕著で、ブラウザのウィンドウをリサイズすると解消することがあります。ブラウザ側でのコンテナ幅の計算がうまくいっていないのか、こちらの CSS 指定がよくないのか、原因はよくわかっていません。
このように、当初の目的から少しずつ変わっていきましたが、調整を重ねるうちに現在の形に落ち着きました。
まだ改善の余地はありますし、いろいろ試してみたい機能もありますが一旦ここまでとして、また新たなアイデアが浮かんだら何かつくります。