見出し画像

【React】サクサク動く画像圧縮アプリを作る

Webサイトの運営やアルバム整理などで度々問題になるのが、写真のサイズが重すぎることです。近年、スマートフォンのカメラの高画質化が進んでいることは確かに喜ばしいことですが、依然として軽い写真が求められているのも事実です。

例えば、Webサイトでは、必要以上に重い写真を載せると、ページが表示されるまで時間がかかってしまいます。すると、多くの訪問者が離脱してしまい、訪問者数に影響します。

このような問題の中、簡単に画像を圧縮してサイズを小さくするアプリが必要になってきています。
という訳で、Reactの勉強を兼ねて、写真を簡単に圧縮できるWebサイトを作ってみました。

以下のような画面が表示されます(レイアウトとかデザインはこれから調整します)。

ここで画像を選びます。すると、1秒も経たないうちに画像が圧縮され、結果が表示されます。

左が元画像、右が圧縮後の画像です。サイズは元画像の約25%になりました。

画質や最大幅を変えることもできます。画質や最大幅を変更すると、自動的に圧縮がやり直されます。

これは極端な例ですが、ここまで圧縮を頑張れば、元画像の1%ほどのサイズにすることもできるのですね

仕組み

まず、画像を圧縮するライブラリとしてCompressor.jsを使用しました。

これはブラウザのCanvas APIの機能を使って動いているものです。まずCanvasを生成し、圧縮対象にある画像をそこに貼り付け、Canvasの内容をBlob形式で読み出すというものです。こうするだけで、圧縮された画像を取り出せるのです。
何にしろ動作がとても速くて助かります。

UIフレームワークとしては、Reactを使いました。画像を1つ圧縮する上で、様々なHTML要素を作ったり、処理を行ったりする必要があるため、JavaScriptべた書きで書くのは少し無理があると思ったためです。

以下はコードの抜粋です。画像1つに対して生成される、元画像と圧縮後の画像のペアの要素を"Entry"とします。

// Entry.jsx

/**
 * @param {object} props
 * @param {File} props.ofile
 * @param {number} props.initialQuality
 * @param {string} props.initialMwText
 */
const Entry = (props) => {
    const [quality, setQuality] = useState(props.initialQuality)
    const [mwText, setMwText] = useState(props.initialMwText)
    ...

Entryは元ファイル(ofile)と画質の初期値(initialQuality)、テキスト形式の最大幅の初期値(initialMwText)を受け取ります。画質や最大幅はEntryの生成後にも個別的に変更できるようにしています。

initialQualityとinitialMwTextというのは、Entryの親要素から与えられているもので、下の赤枠で囲った値を指します。これらの値は、これから生成されるEntryに対して反映されるものですが、既に存在しているEntryには反映されません。

この書き方、あまりReactとしてはよくないかもしれませんね。Reactは、コンポーネントに受け渡しされる値は全て画面に反映されるべき、といった考えがあります。
"initial○○"などといった値は、画面に反映されるのは最初だけで、いずれ反映されなくなってしまうかもしれない値です。そのような値を受け渡すのは如何なものか、ということになります。

本当なら、各Entryが今どのような画質や最大幅の設定になっているのかの情報も、Entryの親コンポーネントが状態として持っておくべきなのでしょう。ただ、この話題を深く掘ってしまうと、コンテクストを使う話になったり、Reduxを使う話になったりして一気に大規模化するので触れないでおきます。

また、似たような実装例は以下の中の「propsをstateにコピーしない」というコラムに書いてあります。一応公式公認なのかな…??

今後やること

  • ファイル選択ボタンをもっとかっこよくする。

  • 圧縮後の画像のダウンロードボタンを設置する。

  • 圧縮後の画像を一括でダウンロードできるようにもする。

  • Reduxの導入???



この記事が気に入ったらサポートをしてみませんか?