ポケモン画像でアスキーアート風に変換するwebアプリを作った

こんにちは、[Yui](https://twitter.com/yui_active)です。

引き続き週イチ発信をしていきます。
今週は先週作った[ポケモンAA化API](https://qiita.com/yuikoito/items/7dcf1deeed46f46b2f2e)を使ってwebアプリを作ったのでその件について書きます。


<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">先週作ったポケモン画像でアスキーアート風に変換するAPIを利用してwebアプリを作りました！<a href="https://t.co/LcZPeFVdyO">https://t.co/LcZPeFVdyO</a> <a href="https://t.co/iof8e1tSW0">pic.twitter.com/iof8e1tSW0</a></p>&mdash; yui ⛅ Yuiko Ito (@yui_active) <a href="https://twitter.com/yui_active/status/1414458679110234116?ref_src=twsrc%5Etfw">July 12, 2021</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


過去の週イチ発信は以下

- [【React + Typescriptで顔認識】tensorflowを使って画像にマスクをかけるアプリを作った](https://qiita.com/Yui_active/items/b8062b9f89f72c6865ed)
- [【React + Typescript】ボタン一つでコンポーネントのscssをコピーできるサイトを作った](https://qiita.com/Yui_active/items/1288fed0dcc24c71e159)
- [【アップデート】ui-componentsに18個のコンポーネントを追加した](https://qiita.com/yuiko_ito/items/c1d0599e1e7da7fa42e0)
- [【Nuxt.js × Tailwind CSS】ボタン一つで有名絵画風の画像にできるサービスをリリースした！](https://qiita.com/yuikoito/items/126b0e2d36a707bfd36e)
- [【GASでLINE Bot作成】現在地の近くのおすすめのごはん屋さんを教えてくれるLINE Botを作った](https://qiita.com/yuikoito/items/c1aef147cef4458f9d2e)
- [【動的OGP】Next.js + TypeScript + Vercelデプロイで動的OGPを実現する](https://qiita.com/yuikoito/items/619120c592d99f9d3053)
- [【LambdaでOpenCVを利用】AWSとOpenCVを利用してポケモン画像でアスキーアート風に変換するAPIを作った](https://qiita.com/yuikoito/items/7dcf1deeed46f46b2f2e)

## 構成

構成はいつもどおりのNuxt.js + tailwind CSSです。
一度テンプレを作ったらずっと使ってしまいますね。

デプロイはvercelで行ってます。

動的OGP部分は[こちらの記事](https://zenn.dev/yui/articles/0507acb6cdc100)に書いてあるとおりで、アスキーアート化するAPI部分に関しては先週[こちら](https://qiita.com/yuikoito/items/7dcf1deeed46f46b2f2e)に書いてます。

## 使い方

https://poke.art-creator.net/

使い方は上記にアクセスして、

- 画像を選択
- 変換ボタンをクリック
- 後は待つだけ

のお手軽構成です。
ログインも不要なので、ぜひ遊んで見て下さい！

一応多言語化していて、ユーザーのブラウザ言語が日本語のときは日本語で英語のときは英語で表示できるようにしています。

デザインは[塗り絵ツクール](https://nurie-maker.com/)や[絵画ツクール](https://art-creator.net/)の使いまわしですが、微妙にこだわったこととして、ローディングアニメーションをモンスターボールにしてます。笑

[![Image from Gyazo](https://i.gyazo.com/4f7aa2d105fb2dbc4e341bc32fca3e4f.gif)](https://gyazo.com/4f7aa2d105fb2dbc4e341bc32fca3e4f)

このモンスターボールに関しては、[Temani Afifさんのこの記事](https://dev.to/afif/another-100-css-loaders-for-your-next-project-352l)から、自由に使って良いと書かれてあるのでありがたく拝借しました。

## 画像の圧縮処理

今回APIの処理部分が非常に重くなってしまう関係で、フロント側で画像を圧縮して送ってます。
[絵画ツクール](https://art-creator.net/)でも同じ処理を加えています。
やはり画像処理系は送る前にできる限り軽量化して送らないと時間がかかってしまいますね。

処理内容としては以下です。

```react
/**
 * Canvasの描画して画像をリサイズする
 * @param {object} image - Imageオブジェクト
 * @param {number} limitWidth - 最大横幅
 * @param {number} limitHeight - 最大縦幅
 * @returns {string} - base64
 */
export default async function resizeImage(image, limitWidth, limitHeight) {
  const aspect = image.width / image.height;
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  let canvasWidth;
  let canvasHeight;
  if (image.width > limitWidth || image.height > limitHeight) {
    // 規定サイズよりも画像が大きい場合
    if (aspect > 1) {
      // 横長画像の場合
      canvasWidth = limitWidth;
      canvasHeight = limitHeight * (image.height / image.width);
    } else {
      // 縦長画像の場合
      canvasWidth = limitWidth * (image.width / image.height);
      canvasHeight = limitHeight;
    }
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;
  } else {
    // 規定サイズ内の場合
    canvas.width = image.width;
    canvas.height = image.height;
    canvasWidth = image.width;
    canvasHeight = image.height;
  }
  ctx.drawImage(
    image,
    0,
    0,
    image.width,
    image.height,
    0,
    0,
    canvasWidth,
    canvasHeight
  );
  return canvas.toDataURL("image/jpeg", 0.85);
}

```

## あとがき

今回はネタアプリということで、先週遊びで作ったAPIをwebアプリ化してみました。
うまく変換できなかったり、変換できても使い所がわからない画像にはなるとは思いますが、楽しんでもらえると嬉しいです！