6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

アスキーアート総合Advent Calendar 2018

Day 8

AAをSVGで表示するWebComponentsを作成する、Stencilで。

Last updated at Posted at 2018-12-21

この記事は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を作ります。具体的に考える手順は以下の通りです。

  1. StencilのWebComponentのスケルトンプロジェクトを立ち上げる
  2. 新しいWebComponentを追加する
  3. 文字列をSVGで表示するを作る

興味がある場合は引き続きこの記事を読んでみてください。

1. StencilのWebComponentのスケルトンプロジェクトを立ち上げる

Stencilで新しくWebComponentを作るなら以下のコマンドを入力します。

npx create-stencil component

プロジェクト名称とConfirmが聞かれますので、答えましょう。今回は「aa-svg」というプロジェクト名にしました。
Screen Shot 2018-12-21 at 15.19.56.png

プロジェクト生成完了後、作業ディレクトリを移動してnpm installnpm startを行います。

cd aa-svg
npm install
npm start

プロジェクトが立ち上がり以下の画面が表示されれば成功です。
Screen Shot 2018-12-21 at 15.23.03.png

2. 新しいWebComponentを追加する

Stencilで新しいWebComponeとを生成する時はsrc/componentsをファイルを追加すればOKです。今回は、以下の2ファイルをsrc/components/aa-svgディレクトリに追加します。

src/components/aa-svg/aa-svg.ts
import { Component } from "@stencil/core";

@Component({
  tag: "aa-svg",
  styleUrl: "aa-svg.css",
  shadow: true
})
export class AASvgComponent {
  render() {
    return <div>Hello, World!</div>;
  }
}
src/components/aa-svg/aa-svg.css
// 空のCSSファイル

たったこれだけで新規WebComponentsが作成されました。試しに呼び出してみます。src/index.htmlを書き換えてaa-svgコンポネーントを呼び出してみましょう。

src/index.html
<!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で以下の画面が表示されていれば成功です。
Screen Shot 2018-12-21 at 15.31.34.png

3. 文字列をSVGで表示するを作る

いよいよ実際に文字列をSVGで表示する処理を作ります。必要な処理は以下になります。

  1. AA用フォントを読み込む
  2. aa-svgコンポーネントでAA(文字列)を受け取れるようにする
  3. 文字列を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>
    );
  }

最終的にで上がったソースコードは以下のようになります。

src/components/aa-svg/aa-svg.tsx
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>
    );
  }
}
src/index.html
<!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で表示しているため、画像のように完全にレスポンシブです。なおかつ単なるテキストでもあります。

mona.gif

ソースコード

GitHub

まとめ

本記事では、AAをSVGで表示するWebComponentを作成しました。SVGになることで、レスポンシブな表示が可能ですし、WebComponentなので、取り回しが非常に簡単です。

私もAA箱というサービスで利用しています。下のサイトは完全にStencil + Ionic4のみで作成しており一般的なフレームワークは使用していません。
AA箱 - アスキーアートの保存&共有サービス

また、WebCopmonentの作成に使用したStencilは非常に簡単に使えますし、すでにIonic4という大きなフレームワークでの利用実績もあります。興味をもったなら是非とも利用してみてください。
それでは。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?