[Javascript] canvasでlineを引いた時のボヤけを回避する方法

2022年3月18日

Javascript テクノロジー

t f B! P L
最近canvasにハマりすぎている、ユゲタです。 canvasでパズルゲームを作ろうと思ったんですが、画面内のcanvas枠に、格子線を引きたいな〜と思ってやってみたところ、線画ボヤけてしまったんですよ〜。 canvas内に画像を表示するときに、ボヤけない方法はググってたくさん出てきたんですが、なかなかcanvas自体の描画をボヤけさせない方法がなかったので、ようやく方法が分かったので、ブログに残しておきます。

canvasでグリッド線

まずは、普通にcanvas内にグリッド線を引いてみます。 <canvas id="canvas_1" width="300" height="300"></canvas> <script> const cvs1 = document.getElementById('canvas_1') const ctx = cvs1.getContext('2d') const cnt = 10 const w = cvs1.offsetWidth const h = cvs1.offsetHeight const cols = w / cnt const rows = h / cnt ctx.beginPath() ctx.strokeStyle = 'black' // 横線 for(let i=1; i<cnt; i++){ ctx.moveTo(0, rows * i) ctx.lineTo(w, rows * i) } // 縦線 for(let i=1; i<cnt; i++){ ctx.moveTo(cols * i , 0) ctx.lineTo(cols * i , h) } ctx.stroke() </script> <style> #canvas_1{ border:1px solid black; } </style> どうですか? 格子状のラインがボヤけてるのがわかりますか? まずcanvasタグ自体にstyleでborderラインを1px引いて、その中の格子をcanvas-lineで書いているんですが、canvas-lineがボヤけてしまっています。 原因は、 ・ラインが書かれている座標が整数値になっていないことからこの現象が起きてしまいます。

ライン座標を整数値にする(未解決)

<canvas id="canvas_2" width="300" height="300"></canvas> <script> const cvs2 = document.getElementById('canvas_2') const ctx2 = cvs2.getContext('2d') const cnt2 = 10 const w2 = cvs2.offsetWidth const h2 = cvs2.offsetHeight const cols2 = ~~(w2 / cnt2) const rows2 = ~~(h2 / cnt2) ctx2.beginPath() ctx2.strokeStyle = 'black' ctx2.lineWidth = 1 // 横線 for(let i=1; i<cnt2; i++){ ctx2.moveTo(0, rows2 * i) ctx2.lineTo(w2, rows2 * i) } // 縦線 for(let i=1; i<cnt2; i++){ ctx2.moveTo(cols2 * i , 0) ctx2.lineTo(cols2 * i , h2) } ctx2.stroke() </script> <style> #canvas_2{ border:1px solid black; } </style> あれ?まだボヤけてる・・・どして?

ラインの座標を0.5ずらす(半分解決)

<canvas id="canvas_3" width="300" height="300"></canvas> <script> const cvs3 = document.getElementById('canvas_3') const ctx3 = cvs3.getContext('2d') const cnt3 = 10 const w3 = cvs3.offsetWidth const h3 = cvs3.offsetHeight const cols3 = ~~(w3 / cnt3) const rows3 = ~~(h3 / cnt3) ctx3.beginPath() ctx3.strokeStyle = 'black' ctx3.lineWidth = 1 // 横線 for(let i=1; i<cnt3; i++){ ctx3.moveTo(0, rows3 * i + 0.5) ctx3.lineTo(w3, rows3 * i + 0.5) } // 縦線 for(let i=1; i<cnt3; i++){ ctx3.moveTo(cols3 * i + 0.5 , 0) ctx3.lineTo(cols3 * i + 0.5 , h3) } ctx3.stroke() </script> <style> #canvas_3{ border:1px solid black; } </style> どうやら、canvasでは、座標した位置の中心の1px書かれるので、2pxで書くとボケないのですが、1pxだと、0.5pxが中心点になるので、そのピクセル値が整数に置かれない事でボケが発生していたようです。 でも、少しボケ幅が改善されたように見えますが、まだ完璧ではなさそうです。

ボケ解消完璧版

<canvas id="canvas_4" width="300" height="300"></canvas> <script> const cvs4 = document.getElementById('canvas_4') const ctx4 = cvs4.getContext('2d') const cnt4 = 10 const w4 = cvs4.offsetWidth const h4 = cvs4.offsetHeight const cols4 = ~~(w4 / cnt4) const rows4 = ~~(h4 / cnt4) ctx4.beginPath() ctx4.strokeStyle = 'black' ctx4.lineWidth = 1 ctx4.imageSmoothingEnabled = false; // 横線 for(let i=1; i<cnt4; i++){ ctx4.moveTo(0, rows4 * i + 0.5) ctx4.lineTo(w4, rows4 * i + 0.5) } // 縦線 for(let i=1; i<cnt4; i++){ ctx4.moveTo(cols4 * i + 0.5 , 0) ctx4.lineTo(cols4 * i + 0.5 , h4) } ctx4.stroke() </script> <style> #canvas_4{ border:1px solid black; image-rendering: -moz-crisp-edges; /* Firefox用 */ image-rendering: pixelated; /* GoogleChrome用 */ } </style> cssにimage-renderingをセットしてみました。 まだ正式対応していないブラウザもあり、プレフィックスというよりも、ブラウザごとの記述が必要になるようですね。 でも、完璧にボケが防止されました。 お疲れ様でした。

おまけ

<canvas id="canvas_5" width="300" height="300"></canvas> <script> const cvs5 = document.getElementById('canvas_5') const ctx5 = cvs5.getContext('2d') const cnt5 = 10 const w5 = cvs5.offsetWidth const h5 = cvs5.offsetHeight const cols5 = ~~(w5 / cnt5) const rows5 = ~~(h5 / cnt5) ctx5.beginPath() ctx5.strokeStyle = 'black' ctx5.lineWidth = 1 ctx5.imageSmoothingEnabled = false; ctx5.setLineDash([2, 2]) // 横線 for(let i=1; i<cnt5; i++){ ctx5.moveTo(0, rows5 * i + 0.5) ctx5.lineTo(w5, rows5 * i + 0.5) } // 縦線 for(let i=1; i<cnt5; i++){ ctx5.moveTo(cols5 * i + 0.5 , 0) ctx5.lineTo(cols5 * i + 0.5 , h5) } ctx5.stroke() </script> <style> #canvas_5{ border:1px solid black; image-rendering: -moz-crisp-edges; /* Firefox用 */ image-rendering: pixelated; /* GoogleChrome用 */ } </style> 罫線を波線にすることで、よりグリッド感の見栄えが良くなります。 波線は、ctx5.setLineDash([2, 2])とすることで、2ドット記述して、2ドット空白にするという繰り返しができる設定で、いろいろな直線パターンを作ることも可能です。

まとめ

canvasで綺麗なラインを出したい場合は、次の工程を忘れないようにしましょう。
  1. 座用を整数値にする。
  2. 奇数の幅で配置する場合には、0.5ずらす。(できれば偶数幅にする)
  3. cssでimage-renderingをセットする。
styleはcanvasを使う場合には必ずセットしておいた方がよさそうですね。 あと、画像を表示する際にボヤけさせないためには、 ctx.imageSmoothingEnabled = false; このおまじないをつけるといいそうですよ。 Have a good canvas life !

このブログを検索

ブログ アーカイブ

QooQ