この記事はWebComponent作成ライブラリStencilを使ってAA(アスキーアート)をSVGで表示するWebComponentを作成する記事です。
AAをSVGに変換することでテキストのまま、ズレなくレスポンシブに表示することができるようになります。
Stencilについて
Ionicチームが作成したWebCopmonentを作成するためのライブラリです。
ざっくりいうと
- tsx(jsx + TypeScript)でWebComponentをかける
- Ionic4はStencilを使ってできている
- LazyLoadingやSSR、状態管理(Redux、Mobx)も使える
というライブラリです。今、React使ってる人なら瞬間で、使ったことない人も数分でWebComponentを記述できるようになると思います。
そして、ルーティングや状態管理、UIライブラリ(Ionic4)も使えるので簡単なWebアプリならStencilだけで作ることも可能です。
SVGのWebComponentを作る
改めて今回はStencilを使っAAをSVGで表示するWebComponentを作ります。具体的に考える手順は以下の通りです。
- StencilのWebComponentのスケルトンプロジェクトを立ち上げる
- 新しいWebComponentを追加する
- 文字列をSVGで表示するを作る
興味がある場合は引き続きこの記事を読んでみてください。
1. StencilのWebComponentのスケルトンプロジェクトを立ち上げる
Stencilで新しくWebComponentを作るなら以下のコマンドを入力します。
npx create-stencil component
プロジェクト名称とConfirmが聞かれますので、答えましょう。今回は「aa-svg」というプロジェクト名にしました。
プロジェクト生成完了後、作業ディレクトリを移動してnpm install
とnpm start
を行います。
cd aa-svg
npm install
npm start
2. 新しいWebComponentを追加する
Stencilで新しいWebComponeとを生成する時はsrc/componentsをファイルを追加すればOKです。今回は、以下の2ファイルをsrc/components/aa-svgディレクトリに追加します。
import { Component } from "@stencil/core";
@Component({
tag: "aa-svg",
styleUrl: "aa-svg.css",
shadow: true
})
export class AASvgComponent {
render() {
return <div>Hello, World!</div>;
}
}
// 空のCSSファイル
たったこれだけで新規WebComponentsが作成されました。試しに呼び出してみます。src/index.html
を書き換えてaa-svg
コンポネーントを呼び出してみましょう。
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
<title>Stencil Component Starter</title>
<script src="/build/mycomponent.js"></script>
</head>
<body>
<aa-svg></aa-svg>
</body>
</html>
すでにnpm start
でプロジェクトを立ち上げている場合は、LiveReloadされます。localhost:3333
で以下の画面が表示されていれば成功です。
3. 文字列をSVGで表示するを作る
いよいよ実際に文字列をSVGで表示する処理を作ります。必要な処理は以下になります。
- AA用フォントを読み込む
- aa-svgコンポーネントでAA(文字列)を受け取れるようにする
- 文字列をSVGで描画する
1. AA用フォントを読み込む
index.htmlでAAの表示が可能な日本語をフォントを読み込みましょう。今回は「Saitamaar」フォントを利用しました。
<style>
@font-face {
font-family: "Saitamaar";
src: url("/assets/fonts/Saitamaar/Saitamaar.woff2") format("woff2"),
url("/assets/fonts/Saitamaar/Saitamaar.woff") format("woff"),
url("/assets/fonts/Saitamaar/Saitamaar.ttf") format("ttf");
font-display: swap;
}
.Saitamaar {
font-family: "Saitamaar";
}
</style>
2. aa-svgコンポーネントでAA(文字列)を受け取れるようにする
aa-svgコンポネーントに対して表示したいAAを入力できるようにしましょう。こちらは、StencilではPropというデコレータで宣言することができます。以下のソースコードはaa
という変数を宣言しています。
import { Component, Prop } from "@stencil/core";
@Component({
tag: "aa-svg",
styleUrl: "aa-svg.css",
shadow: true
})
export class AASvgComponent {
@Prop() aa: string;
// 〜〜〜以下略〜〜〜
親コンポーネントはaaという属性を利用して情報の受け渡しができます。
<body>
<aa-svg aa=" ∧_∧\n ( ´∀`)\n ( )\n | | |\n (__)_)\n"></aa-svg>
</body>
</html>
3. 文字列をSVGで描画する
tsxのrenderメソッドの中で受け取った文字列を描画します。まず、SVG描画時の横幅を決めるため、文字列の最大幅を計測します。以下は、受け取った文字列を計測する関数です。
get_tex_width(txt, font) {
let element = document.createElement("canvas");
let context = element.getContext("2d");
context.font = font;
return context.measureText(txt).width;
}
また、SVGは文字列の改行ができないため、受け取った文字列を改行コードで分割し、line-Height分の縦幅をループでずらしながら描画します。
render() {
const data = this.aa.split(/\\n|\r\n|\r|\n/);
let text = [];
let maxWidth = 0;
let maxHeight = 16 * data.length;
for (let i = 0; i < data.length; i++) {
let width = this.get_tex_width(data[i], "16px 'Saitamaar'");
if (maxWidth < width) {
maxWidth = width;
}
text.push(
<text x="0" y={16 * (i + 1) + 2}>
{data[i]}
</text>
);
}
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox={"0 0 " + maxWidth + " " + maxHeight}
>
<g style={{ fontFamily: "Saitamaar", fontSize: 16 }}>{text}</g>
</svg>
);
}
最終的にで上がったソースコードは以下のようになります。
import { Component, Prop } from "@stencil/core";
@Component({
tag: "aa-svg",
styleUrl: "aa-svg.css",
shadow: true
})
export class AASvgComponent {
@Prop() aa: string;
get_tex_width(txt, font) {
let element = document.createElement("canvas");
let context = element.getContext("2d");
context.font = font;
return context.measureText(txt).width;
}
render() {
const data = this.aa.split(/\\n|\r\n|\r|\n/);
let text = [];
let maxWidth = 0;
let maxHeight = 16 * data.length;
for (let i = 0; i < data.length; i++) {
let width = this.get_tex_width(data[i], "16px 'Saitamaar'");
if (maxWidth < width) {
maxWidth = width;
}
text.push(
<text x="0" y={16 * (i + 1) + 2}>
{data[i]}
</text>
);
}
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox={"0 0 " + maxWidth + " " + maxHeight}
>
<g style={{ fontFamily: "Saitamaar", fontSize: 16 }}>{text}</g>
</svg>
);
}
}
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
<title>Stencil Component Starter</title>
<script src="/build/mycomponent.js"></script>
<style>
@font-face {
font-family: "Saitamaar";
src: url("/assets/fonts/Saitamaar/Saitamaar.woff2") format("woff2"),
url("/assets/fonts/Saitamaar/Saitamaar.woff") format("woff"),
url("/assets/fonts/Saitamaar/Saitamaar.ttf") format("ttf");
font-display: swap;
}
.Saitamaar {
font-family: "Saitamaar";
}
</style>
</head>
<body>
<aa-svg aa=" ∧_∧\n ( ´∀`)\n ( )\n | | |\n (__)_)\n"></aa-svg>
</body>
</html>
以上で完成です。完成したAAはSVGで表示しているため、画像のように完全にレスポンシブです。なおかつ単なるテキストでもあります。
ソースコード
まとめ
本記事では、AAをSVGで表示するWebComponentを作成しました。SVGになることで、レスポンシブな表示が可能ですし、WebComponentなので、取り回しが非常に簡単です。
私もAA箱というサービスで利用しています。下のサイトは完全にStencil + Ionic4のみで作成しており一般的なフレームワークは使用していません。
AA箱 - アスキーアートの保存&共有サービス
また、WebCopmonentの作成に使用したStencilは非常に簡単に使えますし、すでにIonic4という大きなフレームワークでの利用実績もあります。興味をもったなら是非とも利用してみてください。
それでは。