(canvas要素に対して)クリックのx座標とy座標を返すcanvas要素にクリックイベントハンドラーを追加する最も簡単な方法は何ですか?
レガシーブラウザの互換性は必要ありません。Safari、Opera、Firefoxがサポートします。
(canvas要素に対して)クリックのx座標とy座標を返すcanvas要素にクリックイベントハンドラーを追加する最も簡単な方法は何ですか?
レガシーブラウザの互換性は必要ありません。Safari、Opera、Firefoxがサポートします。
回答:
Edit 2018:この回答はかなり古く、現在のすべてのブラウザーでclientX
およびclientY
プロパティが機能するため、不要になった古いブラウザーのチェックを使用します。より単純で最近の解決策については、パトリクスアンサーをチェックしてください。
元の回答:
記事に記載されているように、当時は見つかりましたが、存在しません。
var x;
var y;
if (e.pageX || e.pageY) {
x = e.pageX;
y = e.pageY;
}
else {
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;
私にとっては完全にうまくいきました。
シンプルさが好きでも、ブラウザ間の機能が必要な場合は、このソリューションが最適であることがわかりました。これは、@ Aldekeinのソリューションを単純化したものですが、jQueryはありません。
function getCursorPosition(canvas, event) {
const rect = canvas.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
console.log("x: " + x + " y: " + y)
}
const canvas = document.querySelector('canvas')
canvas.addEventListener('mousedown', function(e) {
getCursorPosition(canvas, e)
})
getBoundingClientRect
ビューポートに相対的な位置を返しますか?それから私の推測は間違っていました。これは私にとって問題ではなかったので、私はそれをテストしませんでした。また、私が見た潜在的な問題について他の読者に警告したかったのですが、明確にしていただきありがとうございます。
var canvas = document.getElementById('canvasID'); canvas.addEventListener("mousedown", function (e) { getCursorPosition(canvas, e);});
更新(5/5/16):シンプルで信頼性が高いため、代わりにパトリックの回答を使用する必要があります。
キャンバスは常にページ全体に相対的にスタイル設定されるわけではないため、canvas.offsetLeft/Top
必ずしも必要なものを返すとは限りません。これは、offsetParent要素を基準にしてオフセットされるピクセル数を返します。これはdiv
、position: relative
スタイルが適用されたキャンバスを含む要素のようなものです。これを説明するにoffsetParent
は、canvas要素自体から始めて、s のチェーンをループする必要があります。このコードは、FirefoxとSafariでテストされて完全に機能しますが、すべての環境で機能するはずです。
function relMouseCoords(event){
var totalOffsetX = 0;
var totalOffsetY = 0;
var canvasX = 0;
var canvasY = 0;
var currentElement = this;
do{
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
}
while(currentElement = currentElement.offsetParent)
canvasX = event.pageX - totalOffsetX;
canvasY = event.pageY - totalOffsetY;
return {x:canvasX, y:canvasY}
}
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
最後の行は、キャンバス要素に対するマウス座標を取得するのに便利です。有用な座標を取得するために必要なのは、
coords = canvas.relMouseCoords(event);
canvasX = coords.x;
canvasY = coords.y;
event.offsetX
とevent.offsetY
属性があるため、を追加してソリューションを変更しましたif (event.offsetX !== undefined && event.offsetY !== undefined) { return {x:event.offsetX, y:event.offsetY}; }
。それは動作するように見えます。
event.offsetX
とevent.offsetY
もIE9で動作しています。Firefox(v13でテスト済み)の場合event.layerX
、およびを使用できますevent.layerY
。
canvasX = event.pageX - totalOffsetX - document.body.scrollLeft; canvasY = event.pageY - totalOffsetY - document.body.scrollTop;
最新のブラウザがこれを処理します。Chrome、IE9、Firefoxは、offsetX / Yをこのようにサポートし、クリックハンドラーからイベントを渡します。
function getRelativeCoords(event) {
return { x: event.offsetX, y: event.offsetY };
}
最近のほとんどのブラウザーはlayerX / Yもサポートしていますが、ChromeとIEは、マージン、パディングなどを含むページ上のクリックの絶対オフセットにlayerX / Yを使用しています。Firefoxでは、layerX / YとoffsetX / Yは同等ですが、オフセットは以前は存在しませんでした。そのため、わずかに古いブラウザとの互換性のために、以下を使用できます。
function getRelativeCoords(event) {
return { x: event.offsetX || event.layerX, y: event.offsetY || event.layerY };
}
新しいQuirksmodeによれば、clientX
およびclientY
メソッドはすべての主要なブラウザでサポートされています。だから、ここに行きます-スクロールバーのあるページのスクロールdivで機能する優れた機能するコード:
function getCursorPosition(canvas, event) {
var x, y;
canoffset = $(canvas).offset();
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - Math.floor(canoffset.left);
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop - Math.floor(canoffset.top) + 1;
return [x,y];
}
これには、jQueryも必要です$(canvas).offset()
。
この問題の解決策の完全なソースコードを使用して、すべてのブラウザーで機能する完全なデモを行いました。JavascriptのCanvasでのマウスクリックの座標。デモを試すには、コードをコピーしてテキストエディターに貼り付けます。次に、それをexample.htmlとして保存し、最後にブラウザでファイルを開きます。
幅(%)が可変のキャンバスに対するRyan Arteconaの回答を少し変更します。
HTMLCanvasElement.prototype.relMouseCoords = function (event) {
var totalOffsetX = 0;
var totalOffsetY = 0;
var canvasX = 0;
var canvasY = 0;
var currentElement = this;
do {
totalOffsetX += currentElement.offsetLeft;
totalOffsetY += currentElement.offsetTop;
}
while (currentElement = currentElement.offsetParent)
canvasX = event.pageX - totalOffsetX;
canvasY = event.pageY - totalOffsetY;
// Fix for variable canvas width
canvasX = Math.round( canvasX * (this.width / this.offsetWidth) );
canvasY = Math.round( canvasY * (this.height / this.offsetHeight) );
return {x:canvasX, y:canvasY}
}
座標変換を行うときは注意してください。クリックイベントで複数の非ブラウザ値が返されます。ブラウザーウィンドウがスクロールされている場合(Firefox 3.5およびChrome 3.0で確認済み)、clientXとclientYを単独で使用するだけでは不十分です。
この癖モードの記事は、pageXまたはpageYのいずれか、あるいはclientXとdocument.body.scrollLeftの組み合わせ、およびclientYとdocument.body.scrollTopの組み合わせを使用して、ドキュメントの原点を基準にしたクリック座標を計算できる、より正確な機能を提供します。
更新:さらに、offsetLeftとoffsetTopは、内部サイズではなく、要素のパディングされたサイズを基準にしています。padding:スタイルが適用されたキャンバスは、コンテンツ領域の左上をoffsetLeftとして報告しません。この問題にはさまざまな解決策があります。最も簡単な方法は、キャンバス自体のすべてのボーダー、パディングなどのスタイルをクリアし、代わりにキャンバスを含むボックスに適用することです。
親要素をループしてすべての種類の奇妙なことを行うこれらのすべての答えの意味が何であるかわかりません。
このHTMLElement.getBoundingClientRect
メソッドは、任意の要素の実際の画面位置を処理するように設計されています。これにはスクロールが含まれるため、次のようなものscrollTop
は必要ありません。
(MDNから)外接する四角形を計算するときに、ビューポート領域(またはその他のスクロール可能な要素)で行われたスクロールの量が 考慮されます。
非常に最も簡単な方法は、すでにここに掲載されました。ワイルドなCSSルールが含まれていない限り、これは正しいです。
画像のピクセル幅がCSSの幅と一致しない場合は、ピクセル値に比率を適用する必要があります。
/* Returns pixel coordinates according to the pixel that's under the mouse cursor**/
HTMLCanvasElement.prototype.relativeCoords = function(event) {
var x,y;
//This is the current screen rectangle of canvas
var rect = this.getBoundingClientRect();
var top = rect.top;
var bottom = rect.bottom;
var left = rect.left;
var right = rect.right;
//Recalculate mouse offsets to relative offsets
x = event.clientX - left;
y = event.clientY - top;
//Also recalculate offsets of canvas is stretched
var width = right - left;
//I use this to reduce number of calculations for images that have normal size
if(this.width!=width) {
var height = bottom - top;
//changes coordinates by ratio
x = x*(this.width/width);
y = y*(this.height/height);
}
//Return as an array
return [x,y];
}
キャンバスに境界線がない限り、それは引き伸ばされた画像(jsFiddle)で機能します。
キャンバスの境界線が太い場合、物事は少し複雑になります。文字通り、境界の長方形から境界線を引く必要があります。これは、.getComputedStyleを使用して行うことができます。この回答はプロセスを説明しています。
その後、関数は少し大きくなります。
/* Returns pixel coordinates according to the pixel that's under the mouse cursor**/
HTMLCanvasElement.prototype.relativeCoords = function(event) {
var x,y;
//This is the current screen rectangle of canvas
var rect = this.getBoundingClientRect();
var top = rect.top;
var bottom = rect.bottom;
var left = rect.left;
var right = rect.right;
//Subtract border size
// Get computed style
var styling=getComputedStyle(this,null);
// Turn the border widths in integers
var topBorder=parseInt(styling.getPropertyValue('border-top-width'),10);
var rightBorder=parseInt(styling.getPropertyValue('border-right-width'),10);
var bottomBorder=parseInt(styling.getPropertyValue('border-bottom-width'),10);
var leftBorder=parseInt(styling.getPropertyValue('border-left-width'),10);
//Subtract border from rectangle
left+=leftBorder;
right-=rightBorder;
top+=topBorder;
bottom-=bottomBorder;
//Proceed as usual
...
}
この最終機能を混乱させるようなことは考えられません。JsFiddleをご覧ください。
ネイティブprototype
のを変更したくない場合は、関数を変更してそれを呼び出すだけです(canvas, event)
(そしていずれかthis
をに置き換えますcanvas
)。
これはとても素敵なチュートリアルです
http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
function writeMessage(canvas, message) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.font = '18pt Calibri';
context.fillStyle = 'black';
context.fillText(message, 10, 25);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
writeMessage(canvas, message);
}, false);
お役に立てれば!
width
/ height
オーバーライドされた画像では機能しませんが、それでも最良のソリューションの1つです。
2016年にjQueryを使用して、キャンバスを基準にしたクリック座標を取得するには、次のようにします。
$(canvas).click(function(jqEvent) {
var coords = {
x: jqEvent.pageX - $(canvas).offset().left,
y: jqEvent.pageY - $(canvas).offset().top
};
});
これは、キャンバスのoffset()とjqEvent.pageX / Yの両方が、スクロール位置に関係なくドキュメントに対して相対的であるため機能します。
キャンバスがスケーリングされている場合、これらの座標はキャンバスの論理座標と同じではないことに注意してください。それらを取得するには、次のようにします。
var logicalCoords = {
x: coords.x * (canvas.width / $(canvas).width()),
y: coords.y * (canvas.height / $(canvas).height())
}
したがって、これは単純ですが、見た目よりも少し複雑なトピックです。
まず最初に、ここで混乱した質問があります
要素の相対マウス座標を取得する方法
2D Canvas APIまたはWebGLのキャンバスピクセルマウス座標を取得する方法
だから、答えます
要素がキャンバスであるかどうかに関係なく、要素の相対的なマウス座標はすべての要素で同じです。
「キャンバスの相対的なマウス座標を取得する方法」という質問に対する2つの簡単な回答があります。
offsetX
とoffsetY
canvas.addEventListner('mousemove', (e) => {
const x = e.offsetX;
const y = e.offsetY;
});
この回答はChrome、Firefox、Safariで機能します。他のすべてのイベント値offsetX
とは異なり、offsetY
CSS変換を考慮に入れます。
2019/05 現在の最大の問題はoffsetX
、offsetY
タッチイベントには存在しないため、iOS Safariでは使用できません。これらは、ChromeとFirefoxには存在するがSafariには存在しないポインターイベントには存在しますが、Safariが動作しているようです。
別の問題は、イベントがキャンバス自体にある必要があることです。それらを他の要素またはウィンドウに配置した場合、後でキャンバスを参照ポイントとして選択することはできません。
clientX
、clientY
及びcanvas.getBoundingClientRect
あなたはCSSの変換を気にしない場合は、次の最も簡単な答えは呼び出すことですcanvas. getBoundingClientRect()
し、減算から左clientX
やtop
からclientY
のように
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
});
これは、CSS変換がない限り機能します。タッチイベントでも動作するため、Safari iOSでも動作します
canvas.addEventListener('touchmove', (e) => {
const rect = canvas. getBoundingClientRect();
const x = e.touches[0].clientX - rect.left;
const y = e.touches[0].clientY - rect.top;
});
このために、上記で取得した値を取得し、キャンバスが表示されているサイズからキャンバス自体のピクセル数に変換する必要があります
とcanvas.getBoundingClientRect
とclientX
とclientY
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
const elementRelativeX = e.clientX - rect.left;
const elementRelativeY = e.clientY - rect.top;
const canvasRelativeX = elementRelativeX * canvas.width / rect.width;
const canvasRelativeY = elementRelativeY * canvas.height / rect.height;
});
またはとoffsetX
とoffsetY
canvas.addEventListener('mousemove', (e) => {
const elementRelativeX = e.offsetX;
const elementRelativeX = e.offsetY;
const canvasRelativeX = elementRelativeX * canvas.width / canvas.clientWidth;
const canvasRelativeY = elementRelativeX * canvas.height / canvas.clientHeight;
});
使用例作業event.offsetX
、event.offsetY
canvas.getBoundingClientRect
and event.clientX
とを使用した作業例event.clientY
私はこのリンクをお勧めします-http ://miloq.blogspot.in/2011/05/coordinates-mouse-click-canvas.html
<style type="text/css">
#canvas{background-color: #000;}
</style>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", init, false);
function init()
{
var canvas = document.getElementById("canvas");
canvas.addEventListener("mousedown", getPosition, false);
}
function getPosition(event)
{
var x = new Number();
var y = new Number();
var canvas = document.getElementById("canvas");
if (event.x != undefined && event.y != undefined)
{
x = event.x;
y = event.y;
}
else // Firefox method to get the position
{
x = event.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
y = event.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
x -= canvas.offsetLeft;
y -= canvas.offsetTop;
alert("x: " + x + " y: " + y);
}
</script>
x = new Number()
何ですか?再割り当てする以下のコードx
は、割り当てられた数値がただちに破棄されることを意味します
プロトタイプでは、上記のRyan Arteconaが述べたように、cumulativeOffset()を使用して再帰的な合計を行います。
あなたはただ行うことができます:
var canvas = yourCanvasElement;
var mouseX = (event.clientX - (canvas.offsetLeft - canvas.scrollLeft)) - 2;
var mouseY = (event.clientY - (canvas.offsetTop - canvas.scrollTop)) - 2;
これにより、マウスポインターの正確な位置がわかります。
http://jsbin.com/ApuJOSA/1/edit?html,outputにあるデモを参照してください。
function mousePositionOnCanvas(e) {
var el=e.target, c=el;
var scaleX = c.width/c.offsetWidth || 1;
var scaleY = c.height/c.offsetHeight || 1;
if (!isNaN(e.offsetX))
return { x:e.offsetX*scaleX, y:e.offsetY*scaleY };
var x=e.pageX, y=e.pageY;
do {
x -= el.offsetLeft;
y -= el.offsetTop;
el = el.offsetParent;
} while (el);
return { x: x*scaleX, y: y*scaleY };
}
上記のRyan Arteconaのソリューションのいくつかの変更を次に示します。
function myGetPxStyle(e,p)
{
var r=window.getComputedStyle?window.getComputedStyle(e,null)[p]:"";
return parseFloat(r);
}
function myGetClick=function(ev)
{
// {x:ev.layerX,y:ev.layerY} doesn't work when zooming with mac chrome 27
// {x:ev.clientX,y:ev.clientY} not supported by mac firefox 21
// document.body.scrollLeft and document.body.scrollTop seem required when scrolling on iPad
// html is not an offsetParent of body but can have non null offsetX or offsetY (case of wordpress 3.5.1 admin pages for instance)
// html.offsetX and html.offsetY don't work with mac firefox 21
var offsetX=0,offsetY=0,e=this,x,y;
var htmls=document.getElementsByTagName("html"),html=(htmls?htmls[0]:0);
do
{
offsetX+=e.offsetLeft-e.scrollLeft;
offsetY+=e.offsetTop-e.scrollTop;
} while (e=e.offsetParent);
if (html)
{
offsetX+=myGetPxStyle(html,"marginLeft");
offsetY+=myGetPxStyle(html,"marginTop");
}
x=ev.pageX-offsetX-document.body.scrollLeft;
y=ev.pageY-offsetY-document.body.scrollTop;
return {x:x,y:y};
}
まず、他の人が言ったように、キャンバス要素の位置を取得する関数が必要です。これは、このページの他のいくつか(IMHO)よりも少しエレガントな方法です。任意の要素を渡して、ドキュメント内での位置を取得できます。
function findPos(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
return { x: curleft, y: curtop };
}
return undefined;
}
次に、それに対するカーソルの現在の位置を計算します。
$('#canvas').mousemove(function(e) {
var pos = findPos(this);
var x = e.pageX - pos.x;
var y = e.pageY - pos.y;
var coordinateDisplay = "x=" + x + ", y=" + y;
writeCoordinateDisplay(coordinateDisplay);
});
ジェネリックfindPos
関数をイベント処理コードから分離したことに注意してください。(あるべきように。関数をそれぞれ1つのタスクに留めるようにしてください。)
値offsetLeft
とはoffsetTop
相対にあるoffsetParent
いくつかのラッパー可能性があり、div
(そのことについてまたは何か、)ノード。要素をラップする要素がない場合、canvas
それらはに相対的であるため、body
減算するオフセットはありません。これが、他の作業を行う前にキャンバスの位置を決定する必要がある理由です。
Similaryは、e.pageX
およびe.pageY
ドキュメントにカーソルの位置を与えます。そのため、これらの値からキャンバスのオフセットを差し引いて、実際の位置に到達します。
配置された要素の代替方法は、e.layerX
およびの値を直接使用することですe.layerY
。次の2つの理由により、これは上記の方法よりも信頼性が低くなります。
ThreeJS r77
var x = event.offsetX == undefined ? event.layerX : event.offsetX;
var y = event.offsetY == undefined ? event.layerY : event.offsetY;
mouse2D.x = ( x / renderer.domElement.width ) * 2 - 1;
mouse2D.y = - ( y / renderer.domElement.height ) * 2 + 1;
ここに簡略化されたソリューションがあります(これは境界線/スクロールでは機能しません):
function click(event) {
const bound = event.target.getBoundingClientRect();
const xMult = bound.width / can.width;
const yMult = bound.height / can.height;
return {
x: Math.floor(event.offsetX / xMult),
y: Math.floor(event.offsetY / yMult),
};
}
私はpdfの上にキャンバスを持つアプリケーションを作成していました。これには、pdfのズームインとズームアウトなど、キャンバスの多くのサイズ変更が含まれます。次に、PDFのズームイン/アウトごとに、キャンバスをサイズ変更して、 PDFのサイズ、私はstackOverflowで多くの答えを調べましたが、最終的に問題を解決する完璧な解決策を見つけられませんでした。
私はrxjsとangular 6 を使用していて、最新バージョンに固有の答えは見つかりませんでした。
これは、rxjsを利用してキャンバスの上に描画するのに役立つコードスニペット全体です。
private captureEvents(canvasEl: HTMLCanvasElement) {
this.drawingSubscription = fromEvent(canvasEl, 'mousedown')
.pipe(
switchMap((e: any) => {
return fromEvent(canvasEl, 'mousemove')
.pipe(
takeUntil(fromEvent(canvasEl, 'mouseup').do((event: WheelEvent) => {
const prevPos = {
x: null,
y: null
};
})),
takeUntil(fromEvent(canvasEl, 'mouseleave')),
pairwise()
)
})
)
.subscribe((res: [MouseEvent, MouseEvent]) => {
const rect = this.cx.canvas.getBoundingClientRect();
const prevPos = {
x: Math.floor( ( res[0].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
y: Math.floor( ( res[0].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};
const currentPos = {
x: Math.floor( ( res[1].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
y: Math.floor( ( res[1].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};
this.coordinatesArray[this.file.current_slide - 1].push(prevPos);
this.drawOnCanvas(prevPos, currentPos);
});
}
そして、これが修正するスニペットです。キャンバスのズームイン/ズームアウトの方法に関係なく、キャンバスのサイズを基準にしたマウス座標です。
const prevPos = {
x: Math.floor( ( res[0].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
y: Math.floor( ( res[0].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};
const currentPos = {
x: Math.floor( ( res[1].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
y: Math.floor( ( res[1].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};
ねえ、これは道場にあります、それは私がすでにプロジェクトのコードを持っているものだからです。
それを非dojoバニラJavaScriptに変換する方法はかなり明白なはずです。
function onMouseClick(e) {
var x = e.clientX;
var y = e.clientY;
}
var canvas = dojo.byId(canvasId);
dojo.connect(canvas,"click",onMouseClick);
お役に立てば幸いです。