1. ホーム
  2. javascript

[解決済み] キャンバス上のマウスの実位置

2022-06-28 15:14:15

質問

HTML5 のキャンバスの上にマウスで描画しようとしているのですが、キャンバスの位置が 0,0 (左上隅) のときだけうまく動作するようです。以下は私のコードです。

 function createImageOnCanvas(imageId){
    document.getElementById("imgCanvas").style.display = "block";
    document.getElementById("images").style.overflowY= "hidden";
    var canvas = document.getElementById("imgCanvas");
    var context = canvas.getContext("2d");
    var img = new Image(300,300);
    img.src = document.getElementById(imageId).src;
    context.drawImage(img, (0),(0));
}

function draw(e){
    var canvas = document.getElementById("imgCanvas");
    var context = canvas.getContext("2d");
    posx = e.clientX;
    posy = e.clientY;
    context.fillStyle = "#000000";
    context.fillRect (posx, posy, 4, 4);
}

HTML部分

 <body>
 <div id="images">
 </div>
 <canvas onmousemove="draw(event)" style="margin:0;padding:0;" id="imgCanvas"
          class="canvasView" width="250" height="250"></canvas> 

JavaScriptで簡単な関数を作って正しい位置を取得する方法があると読んだことがありますが、その方法について全くわかりません。

どのように解決するのでしょうか?

シンプルな1:1シナリオ

canvas 要素とビットマップのサイズが 1:1 の場合、次のスニペットを使用してマウスの位置を取得することができます。

function getMousePos(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
    };
}

イベントとcanvasを引数として、イベントから呼び出すだけです。マウスの位置を表すxとyを含むオブジェクトを返します。

取得するマウスの位置はクライアントウィンドウからの相対位置なので、canvas 要素の位置を差し引いて要素自体からの相対位置に変換する必要があります。

コードでの統合の例です。

//put this outside the event loop..
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");

function draw(evt) {
    var pos = getMousePos(canvas, evt);

    context.fillStyle = "#000000";
    context.fillRect (pos.x, pos.y, 4, 4);
}

注意:canvas 要素に直接適用すると、ボーダーとパディングが位置に影響するため、これらは getComputedStyle() - を使用するか、これらのスタイルを親 div に適用する必要があります。

要素とビットマップの大きさが異なる場合

要素がCSSで拡大縮小されている、ピクセルアスペクト比があるなど、要素とビットマップの大きさが異なる場合は、対応する必要があります。

function  getMousePos(canvas, evt) {
  var rect = canvas.getBoundingClientRect(), // abs. size of element
      scaleX = canvas.width / rect.width,    // relationship bitmap vs. element for X
      scaleY = canvas.height / rect.height;  // relationship bitmap vs. element for Y

  return {
    x: (evt.clientX - rect.left) * scaleX,   // scale mouse coordinates after they have
    y: (evt.clientY - rect.top) * scaleY     // been adjusted to be relative to element
  }
}

コンテキストに適用される変換(拡大縮小、回転など)を持つ

次に、回転、傾き/せん断、スケール、平行移動などのコンテキストに変換を適用した、より複雑なケースがあります。これに対処するために、現在の行列の逆行列を計算することができます。

新しいブラウザでは、現在の行列を currentTransform . しかし、Firefox では mozCurrentTransformInverted を使うと、配列が返され mozCurrentTransform を返します。Chrome では、実験的なフラグで有効にした場合、どちらも DOMMatrix を返しますが DOMMatrix .

しかし、ほとんどの場合、独自のカスタム行列ソリューションを実装する必要があります(例えば、私のソリューションである ここで - free/MITプロジェクト) を実装する必要があります。

どのような経路で行列を取得したかにかかわらず、最終的に行列を取得したら、それを反転してマウス座標に適用する必要があります。座標は次にキャンバスに渡され、キャンバスはその行列を使用して、その時点のどこにいても元に戻すように変換されます。

このようにして、点はマウスに対して正しい位置になります。また、ここで座標を(逆行列を適用する前に)要素からの相対位置に調整する必要があります。

行列のステップを示すだけの例

SVGMatrix

の使用例 function draw(evt) { var pos = getMousePos(canvas, evt); // get adjusted coordinates as above var imatrix = matrix.inverse(); // get inverted matrix somehow pos = imatrix.applyToPoint(pos.x, pos.y); // apply to adjusted coordinate context.fillStyle = "#000000"; context.fillRect(pos.x-1, pos.y-1, 2, 2); } を実装した場合、次のようになります。

currentTransform

更新 これらのステップを一つの使いやすいオブジェクトに埋め込むフリーソリューション(MIT)を作りました。 ここで で見ることができ、また、ほとんどの人が無視するような他のいくつかの細かな点にも気を配っています。