Saya ingin membuat aplikasi melukis kecil menggunakan kanvas. Jadi saya perlu menemukan posisi mouse di atas kanvas.
Saya ingin membuat aplikasi melukis kecil menggunakan kanvas. Jadi saya perlu menemukan posisi mouse di atas kanvas.
Jawaban:
Untuk orang yang menggunakan JQuery:
Terkadang, ketika Anda memiliki elemen bersarang, salah satunya dengan peristiwa yang menyertainya, bisa membingungkan untuk memahami apa yang dilihat browser Anda sebagai induknya. Di sini, Anda dapat menentukan induk yang mana.
Anda mengambil posisi mouse, dan kemudian kurangi dari posisi offset elemen induk.
var x = evt.pageX - $('#element').offset().left;
var y = evt.pageY - $('#element').offset().top;
Jika Anda mencoba untuk mendapatkan posisi mouse pada halaman di dalam panel gulir:
var x = (evt.pageX - $('#element').offset().left) + self.frame.scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + self.frame.scrollTop();
Atau posisi relatif ke halaman:
var x = (evt.pageX - $('#element').offset().left) + $(window).scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();
Perhatikan pengoptimalan kinerja berikut:
var offset = $('#element').offset();
// Then refer to
var x = evt.pageX - offset.left;
Dengan cara ini, JQuery tidak harus mencari #elementsetiap baris.
Ada versi yang lebih baru, khusus JavaScript di bawah ini oleh @anytimecoder di bawah ini untuk peramban yang mendukung getBoundingClientRect () .
var y = (evt.pageY - $('#element').offset().left) + $(window).scrollTop();kevar y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();
Karena saya tidak menemukan jawaban bebas jQuery yang dapat saya salin / tempel, inilah solusi yang saya gunakan:
function clickEvent(e) {
// e = Mouse click event.
var rect = e.target.getBoundingClientRect();
var x = e.clientX - rect.left; //x position within the element.
var y = e.clientY - rect.top; //y position within the element.
}
e.currentTarget, bukan e.target. Jika tidak, elemen anak-anak akan mempengaruhinya
Yang berikut ini menghitung relasi posisi mouse ke elemen kanvas:
var example = document.getElementById('example');
example.onmousemove = function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
}
Dalam contoh ini, thismengacu pada exampleelemen, dan eadalah onmousemoveacara.
var example = document.getElementById('example'); example.onmousemove = function(e) { var X = e.pageX - this.offsetLeft; var Y = e.pageY - this.offsetTop; }
thiske e.currentTarget, yang akan membuat Anda posisi relatif terhadap elemen yang Anda tambahkan pendengar acara.
Tidak ada jawaban di javascript murni yang mengembalikan koordinat relatif ketika elemen referensi bersarang di dalam orang lain yang dapat dengan posisi absolut. Berikut ini solusi untuk skenario ini:
function getRelativeCoordinates (event, referenceElement) {
const position = {
x: event.pageX,
y: event.pageY
};
const offset = {
left: referenceElement.offsetLeft,
top: referenceElement.offsetTop
};
let reference = referenceElement.offsetParent;
while(reference){
offset.left += reference.offsetLeft;
offset.top += reference.offsetTop;
reference = reference.offsetParent;
}
return {
x: position.x - offset.left,
y: position.y - offset.top,
};
}
htmlelemen akun diimbangi
while(reference !== undefined)dengan while(reference). Setidaknya di Chrome, offsetParent tidak berakhir undefined, tetapi null. Hanya memeriksa apakah referencekebenaran akan memastikan itu berfungsi dengan baik undefineddan null.
offsetParentproperti, yang ternyata sangat berguna dalam situasi lain!
scrollLeftdan scrollTopmengkompensasi keturunan yang digulir.
Tulisan yang bagus tentang kesulitan masalah ini dapat ditemukan di sini: http://www.quirksmode.org/js/events_properties.html#position
Dengan menggunakan teknik yang dijelaskan di sana Anda dapat menemukan posisi mouse dalam dokumen. Kemudian Anda hanya memeriksa untuk melihat apakah di dalam kotak berlari dari elemen Anda, yang dapat Anda temukan dengan panggilan element.getBoundingClientRect()yang akan mengembalikan objek dengan properti berikut: { bottom, height, left, right, top, width }. Dari sana sepele untuk mencari tahu apakah peristiwa itu terjadi di dalam elemen Anda atau tidak.
Saya mencoba semua solusi ini dan karena pengaturan khusus saya dengan wadah yang diubah matriks (perpustakaan panzoom) tidak ada yang berhasil. Ini mengembalikan nilai yang benar, bahkan jika diperbesar dan digeser:
mouseevent(e) {
const x = e.offsetX,
y = e.offsetY
}
Tetapi hanya jika tidak ada elemen anak di jalan. Ini dapat dielakkan dengan membuatnya 'tidak terlihat' pada acara tersebut, menggunakan CSS:
.child {
pointer-events: none;
}
Saya menemukan pertanyaan ini, tetapi untuk membuatnya berfungsi untuk kasus saya (menggunakan dragover pada elemen DOM (tidak menjadi kanvas dalam kasus saya)), saya menemukan bahwa Anda hanya perlu menggunakan offsetXdan offsetYpada acara dragover-mouse .
onDragOver(event){
var x = event.offsetX;
var y = event.offsetY;
}
Saya memberi +1 jawaban Mark van Wyk karena membuat saya berada di arah yang benar, tetapi tidak cukup menyelesaikannya untuk saya. Saya masih memiliki offset pada lukisan dalam elemen-elemen yang terdapat dalam elemen lain.
Bagaimana memecahkannya untuk saya:
x = e.pageX - this.offsetLeft - $(elem).offset().left;
y = e.pageY - this.offsetTop - $(elem).offset().top;
Dengan kata lain - saya hanya menumpuk semua offset dari semua elemen yang bersarang
Tidak ada jawaban di atas yang IMO memuaskan, jadi inilah yang saya gunakan:
// Cross-browser AddEventListener
function ael(e, n, h){
if( e.addEventListener ){
e.addEventListener(n, h, true);
}else{
e.attachEvent('on'+n, h);
}
}
var touch = 'ontouchstart' in document.documentElement; // true if touch device
var mx, my; // always has current mouse position IN WINDOW
if(touch){
ael(document, 'touchmove', function(e){var ori=e;mx=ori.changedTouches[0].pageX;my=ori.changedTouches[0].pageY} );
}else{
ael(document, 'mousemove', function(e){mx=e.clientX;my=e.clientY} );
}
// local mouse X,Y position in element
function showLocalPos(e){
document.title = (mx - e.getBoundingClientRect().left)
+ 'x'
+ Math.round(my - e.getBoundingClientRect().top);
}
Dan jika Anda perlu mengetahui posisi gulir Y saat ini dari halaman:
var yscroll = window.pageYOffset
|| (document.documentElement && document.documentElement.scrollTop)
|| document.body.scrollTop; // scroll Y position in page
Bagi Anda yang mengembangkan situs web biasa atau PWA (Progressive Web Apps) untuk perangkat seluler dan / atau laptop / monitor dengan layar sentuh, maka Anda telah mendarat di sini karena Anda mungkin terbiasa dengan acara tetikus dan baru dalam pengalaman Touch yang terkadang menyakitkan. acara ... yay!
Hanya ada 3 aturan:
mousemove atau touchmoveacara.mousedown atau touchstartacara.Tak perlu dikatakan, hal-hal lebih rumit dengan touchperistiwa karena bisa ada lebih dari satu dan mereka lebih fleksibel (rumit) daripada peristiwa mouse. Saya hanya akan membahas satu sentuhan di sini. Ya, saya malas, tapi itu jenis sentuhan yang paling umum, jadi itulah.
var posTop;
var posLeft;
function handleMouseDown(evt) {
var e = evt || window.event; // Because Firefox, etc.
posTop = e.target.offsetTop;
posLeft = e.target.offsetLeft;
e.target.style.background = "red";
// The statement above would be better handled by CSS
// but it's just an example of a generic visible indicator.
}
function handleMouseMove(evt) {
var e = evt || window.event;
var x = e.offsetX; // Wonderfully
var y = e.offsetY; // Simple!
e.target.innerHTML = "Mouse: " + x + ", " + y;
if (posTop)
e.target.innerHTML += "<br>" + (x + posLeft) + ", " + (y + posTop);
}
function handleMouseOut(evt) {
var e = evt || window.event;
e.target.innerHTML = "";
}
function handleMouseUp(evt) {
var e = evt || window.event;
e.target.style.background = "yellow";
}
function handleTouchStart(evt) {
var e = evt || window.event;
var rect = e.target.getBoundingClientRect();
posTop = rect.top;
posLeft = rect.left;
e.target.style.background = "green";
e.preventDefault(); // Unnecessary if using Vue.js
e.stopPropagation(); // Same deal here
}
function handleTouchMove(evt) {
var e = evt || window.event;
var pageX = e.touches[0].clientX; // Touches are page-relative
var pageY = e.touches[0].clientY; // not target-relative
var x = pageX - posLeft;
var y = pageY - posTop;
e.target.innerHTML = "Touch: " + x + ", " + y;
e.target.innerHTML += "<br>" + pageX + ", " + pageY;
e.preventDefault();
e.stopPropagation();
}
function handleTouchEnd(evt) {
var e = evt || window.event;
e.target.style.background = "yellow";
// Yes, I'm being lazy and doing the same as mouseout here
// but obviously you could do something different if needed.
e.preventDefault();
e.stopPropagation();
}
div {
background: yellow;
height: 100px;
left: 50px;
position: absolute;
top: 80px;
user-select: none; /* Disable text selection */
-ms-user-select: none;
width: 100px;
}
<div
onmousedown="handleMouseDown()"
onmousemove="handleMouseMove()"
onmouseout="handleMouseOut()"
onmouseup="handleMouseUp()"
ontouchstart="handleTouchStart()"
ontouchmove="handleTouchMove()"
ontouchend="handleTouchEnd()">
</div>
Move over box for coordinates relative to top left of box.<br>
Hold mouse down or touch to change color.<br>
Drag to turn on coordinates relative to top left of page.
Lebih suka menggunakan Vue.js ? Saya lakukan! Maka HTML Anda akan terlihat seperti ini:
<div @mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@touchstart.stop.prevent="handleTouchStart"
@touchmove.stop.prevent="handleTouchMove"
@touchend.stop.prevent="handleTouchEnd">
Anda bisa mendapatkannya dengan
var element = document.getElementById(canvasId);
element.onmousemove = function(e) {
var xCoor = e.clientX;
var yCoor = e.clientY;
}
Diambil dari tutorial ini , dengan koreksi dilakukan berkat komentar teratas:
function getMousePos( canvas, evt ) {
var rect = canvas.getBoundingClientRect();
return {
x: Math.floor( ( evt.clientX - rect.left ) / ( rect.right - rect.left ) * canvas.width ),
y: Math.floor( ( evt.clientY - rect.top ) / ( rect.bottom - rect.top ) * canvas.height )
};
}
Gunakan pada kanvas sebagai berikut:
var canvas = document.getElementById( 'myCanvas' );
canvas.addEventListener( 'mousemove', function( evt ) {
var mousePos = getMousePos( canvas, evt );
} );
Saya sadar saya sedikit terlambat, tetapi ini bekerja dengan javascript PURE, dan bahkan memberi Anda koordinat pointer dalam elemen jika elemen lebih besar dari viewport dan pengguna telah menggulir.
var element_offset_x ; // The distance from the left side of the element to the left of the content area
....// some code here (function declaration or element lookup )
element_offset_x = element.getBoundingClientRect().left - document.getElementsByTagName("html")[0].getBoundingClientRect().left ;
....// code here
function mouseMoveEvent(event)
{
var pointer_location = (event.clientX + window.pageXOffset) - element_offset_x ;
}
Bagaimana itu bekerja.
Hal pertama yang kami lakukan adalah mendapatkan lokasi elemen HTML (area konten) relatif terhadap viewport saat ini. Jika halaman tersebut memiliki scrollbar dan digulir, maka nomor tersebut dikembalikan olehgetBoundingClientRect().left untuk tag html akan negatif. Kami kemudian menggunakan nomor ini untuk menghitung jarak antara elemen dan kiri area konten. Denganelement_offset_x = element.getBoundingClientRect().left......;
Mengetahui jarak elemen dari area konten. event.clientXmemberi kita jarak pointer dari viewport. Penting untuk dipahami bahwa viewport dan area konten adalah dua entitas yang berbeda, viewport dapat bergerak jika halaman tersebut digulir. Oleh karena itu, clientX akan mengembalikan nomor SAMA meskipun halaman tersebut digulir.
Untuk mengimbangi ini, kita perlu menambahkan posisi x dari pointer (relatif ke viewport), ke posisi x viewport (relatif terhadap area konten). Posisi X viewport ditemukan dengan window.pageXOffset.
Berdasarkan solusi @ Spider, versi non JQuery saya adalah ini:
// Get the container element's bounding box
var sides = document.getElementById("container").getBoundingClientRect();
// Apply the mouse event listener
document.getElementById("canvas").onmousemove = (e) => {
// Here 'self' is simply the current window's context
var x = (e.clientX - sides.left) + self.pageXOffset;
var y = (e.clientY - sides.top) + self.pageYOffset;
}
Ini berfungsi baik dengan menggulir dan memperbesar (dalam hal ini kadang-kadang mengembalikan mengapung).
Koordinat mouse di dalam kanvas dapat diperoleh berkat event.offsetX dan event.offsetY. Berikut cuplikan kecil untuk membuktikan pendapat saya:
c=document.getElementById("c");
ctx=c.getContext("2d");
ctx.fillStyle="black";
ctx.fillRect(0,0,100,100);
c.addEventListener("mousemove",function(mouseEvt){
// the mouse's coordinates on the canvas are just below
x=mouseEvt.offsetX;
y=mouseEvt.offsetY;
// the following lines draw a red square around the mouse to prove it
ctx.fillStyle="black";
ctx.fillRect(0,0,100,100);
ctx.fillStyle="red";
ctx.fillRect(x-5,y-5,10,10);
});
body {
background-color: blue;
}
canvas {
position: absolute;
top: 50px;
left: 100px;
}
<canvas id="c" width="100" height="100"></canvas>
canvas.onmousedown = function(e) {
pos_left = e.pageX - e.currentTarget.offsetLeft;
pos_top = e.pageY - e.currentTarget.offsetTop;
console.log(pos_left, pos_top)
}
HTMLElement.offsetLeft
Properti HTMLElement.offsetLeftbaca-saja mengembalikan jumlah piksel yang sudut kiri atas elemen saat ini diimbangi ke kiri dalamHTMLElement.offsetParent node.
Untuk unsur blok-tingkat, offsetTop, offsetLeft, offsetWidth, dan offsetHeightmenggambarkan kotak perbatasan elemen relatif terhadap offsetParent.
Namun, untuk elemen level-inline (seperti span) yang dapat membungkus dari satu baris ke baris berikutnya, offsetTopdan offsetLeftmenggambarkan posisi kotak batas pertama (gunakan Element.getClientRects()untuk mendapatkan lebar dan tinggi), sementara offsetWidthdan offsetHeightmenggambarkan dimensi kotak batas pembatas (gunakan Element.getBoundingClientRect()untuk mendapatkan posisinya). Oleh karena itu, sebuah kotak dengan kiri, atas, lebar dan tinggi offsetLeft, offsetTop, offsetWidthdan offsetHeighttidak akan menjadi kotak berlari untuk rentang dengan teks dibungkus.
HTMLElement.offsetTop
Properti HTMLElement.offsetTopread-only mengembalikan jarak elemen saat ini relatif ke atas offsetParentnode.
MouseEvent.pageX
Properti pageXbaca-saja mengembalikan koordinat X (horizontal) dalam piksel acara relatif ke seluruh dokumen. Properti ini memperhitungkan setiap pengguliran horizontal halaman.
MouseEvent.pageY
Properti MouseEvent.pageYbaca-saja mengembalikan koordinat Y (vertikal) dalam piksel acara relatif ke seluruh dokumen. Properti ini memperhitungkan setiap pengguliran vertikal halaman.
Untuk penjelasan lebih lanjut, silakan lihat Jaringan Pengembang Mozilla:
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY https: // developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetLeft https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop
pagedan offsetakan sangat membantu saya. Terima kasih telah mempertimbangkan permintaan ini!
Saya menerapkan solusi lain yang saya pikir sangat sederhana, jadi saya pikir saya akan berbagi dengan kalian.
Jadi, masalah bagi saya adalah bahwa div yang diseret akan melompat ke 0,0 untuk kursor mouse. Jadi saya perlu menangkap posisi mouse pada div untuk menyesuaikan posisi divs baru.
Saya membaca div PageX dan PageY dan mengatur bagian atas dan kiri sesuai dengan itu dan kemudian untuk mendapatkan nilai-nilai untuk menyesuaikan koordinat untuk menjaga kursor pada posisi awal di div saya menggunakan pendengar onDragStart dan menyimpan e.nativeEvent .layerX dan e.nativeEvent.layerY yang hanya di trigger awal memberi Anda posisi mouse di dalam div yang dapat ditarik.
Kode contoh:
onDrag={(e) => {
let newCoords;
newCoords = { x: e.pageX - this.state.correctionX, y: e.pageY - this.state.correctionY };
this.props.onDrag(newCoords, e, item.id);
}}
onDragStart={
(e) => {
this.setState({
correctionX: e.nativeEvent.layerX,
correctionY: e.nativeEvent.layerY,
});
}
Saya harap ini akan membantu seseorang yang mengalami masalah yang sama seperti yang saya alami :)
Anda harus mengetahui struktur halaman Anda, karena jika kanvas Anda adalah anak dari div yang pada gilirannya adalah anak dari div lain ... maka ceritanya menjadi lebih rumit. Inilah kode saya untuk kanvas yang ada di dalam 2 level div s:
canvas.addEventListener("click", function(event) {
var x = event.pageX - (this.offsetLeft + this.parentElement.offsetLeft);
var y = event.pageY - (this.offsetTop + this.parentElement.offsetTop);
console.log("relative x=" + x, "relative y" + y);
});
Jawaban orisinal mengatakan untuk meletakkannya di iframe. Solusi yang lebih baik adalah dengan menggunakan peristiwa offsetX dan offsetY pada kanvas yang memiliki padding set ke 0px.
<html>
<body>
<script>
var main=document.createElement('canvas');
main.width="200";
main.height="300";
main.style="padding:0px;margin:30px;border:thick dashed red";
document.body.appendChild(main);
// adding event listener
main.addEventListener('mousemove',function(e){
var ctx=e.target.getContext('2d');
var c=Math.floor(Math.random()*0xFFFFFF);
c=c.toString(16); for(;c.length<6;) c='0'+c;
ctx.strokeStyle='#'+c;
ctx.beginPath();
ctx.arc(e.offsetX,e.offsetY,3,0,2*Math.PI);
ctx.stroke();
e.target.title=e.offsetX+' '+e.offsetY;
});
// it worked! move mouse over window
</script>
</body>
</html>
Anda dapat menggunakan getBoudingClientRect()orang relativetua.
document.addEventListener("mousemove", (e) => {
let xCoord = e.clientX - e.target.getBoundingClientRect().left + e.offsetX
let yCoord = e.clientY - e.target.getBoundingClientRect().top + e.offsetY
console.log("xCoord", xCoord, "yCoord", yCoord)
})