モニターによって、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)
となる。ゲームサイズとスタイルサイズの比をかけるだけだ。ああややこしい。