mintsu's blog

favicon 変換・作成サービスを作りました

2019-04-26 08:00:00
フロントエンド

favicon 変換・作成サービスを作りました

favicon (ファビコン)を簡単に作ることができる Favicon ジェネレーター というウェブサービスを作りました。
作った際の利用技術やライブラリ、実装などを紹介していきます。

目次

Favicon ジェネレーター

favicon(ファビコン)を簡単に作ることができる Favicon ジェネレーター というウェブサービスを作りました。

favicon 変換デモ

Favicon ジェネレーター

Favicon ジェネレーター の特徴

  • WebAssemblyを使うことで、アップロードすることなく、全てローカルで完結
  • アップロードの待ち時間がなく、高速な変換が可能
  • マルチアイコンに対応
  • 様々なファイルサイズに変換可能
  • 透過に対応

作成した動機

以前、こちらの記事で、 ImageMagick を使って favicon を作成する方法について記載しました。 実際に自分はいつもこの方法でやっていたのですが、やはりコマンドを打つのが面倒だったり、コマンドを覚えきっておらず、そらで書けるわけではないので、多少面倒くささを感じていました。

ImageMagickでfaviconを作成する | mintsu's blog

そこで、ブラウザ上で変換できるツールがあったらいいなと思い、ブラウザで favicon を自動生成できるツールが作成できたらいいなと思いました。

利用技術

  • React.js
  • WebAssembly
  • WASM ImageMagick

方向性

こういった画像を扱うウェブサービスを使うとき、ほとんどのサービスでは画像のアップロードが必要になりますが、できれば画像をインターネットに上げたくないと思う人なので、できればローカルで完結できるようにできないかと思いました。
普通にネットワークを介するものであれば、AWS Lambda を使って変換する、というのも考えましたが、上記の理由から今回はAWS Lambda 等のFaaSの利用は見送りました。

今回は WASM ImageMagick という Webassembly でImageMagickを使えるようにしたライブラリを利用することで、アップロードせずに、ローカルで完結するようにしました。

JavaScript で ImageMagick を使う

ImageMagick を Webassembly で使えるようにしたライブラリがあるのでこちらを利用します。
KnicKnic/WASM-ImageMagick: Webassembly compilation of https://github.com/ImageMagick/ImageMagick & samples

WASM ImageMagick のインストール

npm もしくは yarn で追加します。

yarn add wasm-imagemagick

実装

変換処理を行うReact Componentの処理イメージです

import React from 'react'
import { call } from 'wasm-imagemagick'

class Converter extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      outputImage: '',
    }
  }

  readFileAsync(file) {
    return new Promise((resolve, reject) => {
      let reader = new FileReader();
      reader.onload = () => {
        resolve(reader.result);
      };
      reader.onerror = reject;
      reader.readAsArrayBuffer(file);
    })
  }

  handleChangeFile = async (e) => {
    var files = e.target.files;

    const buffer = await this.readFileAsync(files[0])
    const uint8ArrayBuffer = new Uint8Array(buffer)

    const command = ["convert", files[0].name, "-define", "icon:auto-resize", "favicon.ico"]
    const result = await call([{content:uint8ArrayBuffer, name: files[0].name}], command);
    if(result.exitCode !== 0) {
      alert('変換に失敗しました')
      return
    }
    this.setState({outputImage: URL.createObjectURL(result.outputFiles[0].blob)});
  }

  render() {
    return(
      <div>
        <input type="file" rel="file" onChange={this.handleChangeFile} />
        <p>変換後画像</p>
        <img src={this.state.outputImage} />
      </div>
    )
  }
}
export default Converter

ファイルを選択すると onChange で指定している handleChangeFile が呼ばれます。

handleChangeFile の中で選択したファイルのデータを読み込み、ImageMagick の関数を呼び出し変換をかけています。
実際に変換をかける処理は下記の call関数を呼び出す部分です。
commandはImageMagickのコマンドと一緒ですね。

const command = ["convert", files[0].name, "-define", "icon:auto-resize", "favicon.ico"]
const result = await call([{content:uint8ArrayBuffer, name: files[0].name}], command);

最後に

今回ImageMagickをJavaScriptで使う方法を書きました。 WebAssembly のおかげでJavascriptからImageMagickのコマンドが呼び出せるのは非常に便利ですね。 今回はFaviconを作る用途で使いましたが、他にもいろいろ応用ができそうです。

また、WebAssembly 自体についても興味が湧いてきたので、今後触ってみたいと思います。

今回は ImageMagick の話が主でしたが、実は React.js を使ったアプリ開発は今回が初めてでした。
あまりReactの作法を知らず、動けばいいや程度なコードになってしまいましたが、React Component の基本的なところについては学ぶことができ、Reactの最初の学び・導入としては良かったなと思います。