ナンモワカランアザラシ

技術的なアレコレを自分の言葉で書いて保管・公開しておくための静かなインターネット

モニターの解像度に合わせてCanvas要素のサイズを変える

モニターによって、1単位長さに表示できるピクセル数は異なる。 Canvas要素のstyle上の大きさと描画の大きさを気にしてあげないと、モニターによってはぼやけてしまう。

それを回避するための基本的な考え方を記載する。

window.devicePixelRatio

https://developer.mozilla.org/ja/docs/Web/API/Window/devicePixelRatio

devicePixelRatio は Window インターフェイスのプロパティで、現在のディスプレイ機器における CSS 解像度と物理解像度の比を返します。

この値を使う。

Canvasのresize

Canvasのstyle上の大きさが所与であるとする。styleWidth, styleHeightとする。

// canvas要素の描画サイズを適応させる
const resizeCanvas = (canvas: HTMLCanvasElement) => {
  const dpr = window.devicePixelRatio || 1
  canvas.width = styleWidth * dpr
  canvas.height = styleHeight * dpr
}

以上が基本的なコードである。

CSS上のサイズがレスポンシブだったり、縦横比が変化したり、CSS上の縦横比とcanvasの縦横比が異なったり、クリック地点によって処理を変えたかったり、CSS上のサイズ・Canvasの描画サイズ以外にアプリケーションで管理するスクリーン概念のサイズがあったりすると考えることが多くなって疲れる。

レスポンシブ

ResizeObserverを使う。スタイル上の大きさが変わるたびに描画サイズも変えるのだ。 https://developer.mozilla.org/ja/docs/Web/API/ResizeObserver

クリック位置

直接的に「Canvasのスタイルサイズにおけるクリック位置」を取得できる方法はないので、画面上でのクリック位置とCanvasの位置で差分をとる。

const canvas = document.createElement('canvas')
const dpr = window.devicePixelRatio || 1
const handleClick = (e: MouseEvent) => {
  const canvasBoundingClientRect = canvas.getBoundingClientRect()
  
  const canvasStyle = window.getComputedStyle(canvas)
  const borderWidthLeft = parseFloat(canvasStyle.borderLeftWidth)
  const paddingLeft = parseFloat(canvasStyle.borderTopWidth)
  const canvasPositionLeft = canvasBoundingClientRect.left + borderWidthLeft + paddingLeft
  const clickX = (event.clientX - canvasPositionLeft) * dpr
  doSomethingWithClickPositionInCanvas(clickX)
}

ややこしいのでCanvasにborderとかつけないほうがいい。

縦横比

変化するたびにgetComputedStyleなどでstyleサイズを取得してresizeをし直す。先のコード例の通り、取得方法によってborderやpaddingの値を含んだり含まなかったりするから注意。 CSSサイズとCanvasサイズで縦横比が異なる場合はそんなに考えることは多くない。ただ描画が歪むだけだ。分かりやすい。

CSSサイズでもCanvasサイズでもない「サイズ」

例えばゲームのサイズとして横幅を"gameWidth"と定める。キャラのサイズとの比の兼ね合いなどあるので、大抵の場合この値は固定値だ。 Canvas要素のスタイルサイズを画面いっぱいにしたかったら、縦横どちらを余らせてもいいかを決めてから、ゲームサイズの比と合うようにする。 解像度に合わせたいのでCanvasの描画サイズはdpr倍される。 さて、Canvasの描画サイズに対してゲームサイズの1単位はいくつだろう?クリック位置をゲームのサイズで測りたい場合は?

Canvasのスタイル上でのクリック位置をclickXとする。 Canvasのスタイル上での横幅をStyleWidthとする。 Canvasの描画サイズはStyleWidth * dprだ。 クリック位置のゲーム中での位置は、clickX * dpr * gameWidth / (StyleWidth*dpr) = clickX * (gameWidth/StyleWidth)となる。ゲームサイズとスタイルサイズの比をかけるだけだ。ああややこしい。