[React] Canvas で画像を拡大するとき、画像がぼけやるのを防ぐには

2022-10-19

今回は React に限った話ではありませんが…、サンプルを React で書いています。
HTMLやCSSを準備するよりReactで用意するほうがエコシステムがいろいろ揃えてくれて楽ちん (。´・ω・)

Canvas で小さい画像を拡大して表示すると、自動でいい感じにふんわりと整えてくれます。
自然画像だと嬉しいことも多いのかな?

ですが、ドット絵を拡大して表示したいときなど、いいかんじの機能がアダとなることがあります。
ドット絵なのにぼんやりしてしまう…(/ω\)

今回は、それを解決するお話。

image-rendering プロパティ

結論はこれです。

https://developer.mozilla.org/ja/docs/Web/CSS/image-rendering

上記ページからの引用

今時点だと、大体のブラウザが対応しているので、安心。

使ってみた。

イメージ

元の画像は 16×16 の小さな画像です。

それを オリジナルの画像特別なことをせずにCanvasに拡大表示ドット絵がドット絵らしく拡大される方法で表示 という順で並べてみました。

サンプルコード

いつものように App.js を編集しています。

import React, { useRef } from 'react';
import './App.css';

import original from './16x16.png';

function App() {
  const imgRef = useRef(null);
  const canvas1Ref = useRef(null);
  const canvas2Ref = useRef(null);

  const onLoad = () => {
    const img = imgRef?.current;
    canvas1Ref?.current?.getContext('2d')?.drawImage(img, 0, 0);
    canvas2Ref?.current?.getContext('2d')?.drawImage(img, 0, 0);
  };

  return (
    <>
      <div className="bg-dark p-5" style={{ minHeight: '100vh', height: '100%' }}>
        <div className="d-flex flex-column justify-content-center align-items-center p-5 bg-light text-center">
          <div className="fw-bold my-2">
            オリジナル
          </div>
          <img ref={imgRef} src={original} alt="original" onLoad={onLoad} />
          <div className="m-2">
            <div className="fw-bold my-2">
              デフォルト
            </div>
            <canvas
              ref={canvas1Ref}
              width="16"
              height="16"
              className="border border-dark bg-light"
              style={{ width: 240, height: 240 }}
            />
          </div>
          <div className="m-2">
            <div className="fw-bold my-2">
              ピクセレーション
            </div>
            <canvas
              ref={canvas2Ref}
              width="16"
              height="16"
              className="border border-dark bg-light"
              style={{ width: 240, height: 240, imageRendering: 'pixelated' }}
            />
          </div>
        </div>
      </div>
    </>
  );
}

export default App;

はい、できました。