ฉันจะได้รับพิกัดของการคลิกเมาส์บนองค์ประกอบผ้าใบได้อย่างไร


276

วิธีที่ง่ายที่สุดในการเพิ่มตัวจัดการเหตุการณ์การคลิกไปยังองค์ประกอบ Canvas ที่จะส่งคืนพิกัด x และ y ของการคลิก (สัมพันธ์กับองค์ประกอบ Canvas) คืออะไร

ไม่จำเป็นต้องใช้เบราว์เซอร์รุ่นเก่า Safari, Opera และ Firefox จะทำเช่นนั้น


สิ่งนี้ไม่ควรแตกต่างจากการรับเหตุการณ์เมาส์จากองค์ประกอบ dom ปกติ quirksmodeมีการอ้างอิงที่ดีในเรื่องนั้น
airportyh

4
รหัสที่คุณแสดงรายการข้างต้นจะใช้ได้เฉพาะเมื่อผืนผ้าใบไม่ลึกลงไปในคอนเทนเนอร์อื่น โดยทั่วไปคุณต้องใช้ฟังก์ชัน jquery offset [var testDiv = $ ('# testDiv'); var offset = testDiv.offset ();] เพื่อรับออฟเซ็ตที่ถูกต้องด้วยวิธีข้ามเบราว์เซอร์ นี่คือความเจ็บปวดที่แท้จริงใน ***
Aaron Watters

รหัสที่โพสต์ไว้ด้านบนด้วยการอัปเดตไม่สามารถทำงานได้หากหน้าเว็บที่มีแถบเลื่อน
Timothy Kim

ฉันลบคำตอบเก่าที่รวมอยู่ในการอัปเดตคำถามออกแล้ว ตามที่กล่าวไว้มันล้าสมัยและไม่สมบูรณ์
Tom

3
เนื่องจากมี 50 คำตอบที่นี่ฉันขอแนะนำให้เลื่อนไปที่คนนี้ตอบ: patriques - ซับ 5 ที่ดีและเรียบง่าย
Igor L.

คำตอบ:


77

แก้ไข 2018:คำตอบนี้ค่อนข้างเก่าและใช้การตรวจสอบเบราว์เซอร์เก่าที่ไม่จำเป็นอีกต่อไปเนื่องจากคุณสมบัติclientXและใช้clientYงานได้กับเบราว์เซอร์ปัจจุบันทั้งหมด คุณอาจต้องการตรวจสอบคำตอบของ Patriquesเพื่อหาทางออกที่ง่ายกว่าและล่าสุดกว่า

คำตอบเดิม:
ตามที่อธิบายไว้ในบทความที่ฉันพบ แต่แล้วไม่มีอยู่อีกต่อไป:

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;

ทำงานได้ดีอย่างสมบูรณ์แบบสำหรับฉัน


30
วิธีนี้ไม่ได้ผลเสมอไป - คำตอบของ Ryan Artecona ทำงานได้ในกรณีทั่วไปมากขึ้นเมื่อผืนผ้าใบไม่จำเป็นต้องอยู่ในตำแหน่งที่สัมพันธ์กับทั้งหน้า
Chris Johnson

3
โซลูชันนี้ไม่ได้ติดตั้งหากประสิทธิภาพมีความสำคัญเนื่องจากการเข้าถึง Dom จะกระทำในการคลิก / ย้ายแต่ละครั้ง และ getBoundingClientRect มีอยู่ในตอนนี้และดูสง่างามยิ่งขึ้น
GameAlchemist

192

หากคุณชอบความเรียบง่าย แต่ยังต้องการฟังก์ชั่นข้ามเบราว์เซอร์ฉันพบว่าโซลูชันนี้ทำงานได้ดีที่สุดสำหรับฉัน นี่คือความเรียบง่ายของการแก้ปัญหา @ Aldekein's แต่โดยไม่ต้อง 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)
})

หากหน้าเอกสารถูกเลื่อนลงฉันคิดว่าหน้าเว็บหนึ่งควรนำการเลื่อนของเอกสารมาพิจารณาด้วย
Peppe LG

8
@ PeppeL-G สี่เหลี่ยมผืนผ้าขอบเขตไคลเอนต์คำนวณสำหรับคุณ คุณสามารถทดสอบในคอนโซลได้อย่างง่ายดายก่อนโพสต์ความคิดเห็น (นั่นคือสิ่งที่ฉันทำ)
Tomáš Zato - Reinstate Monica

1
@ TomášZatoโอ้getBoundingClientRectส่งคืนตำแหน่งที่สัมพันธ์กับพอร์ตมุมมองหรือไม่ จากนั้นฉันเดาว่าผิด ฉันไม่เคยทดสอบมันมาก่อนเพราะนี่ไม่ใช่ปัญหาสำหรับฉันและฉันอยากเตือนผู้อ่านคนอื่นถึงปัญหาที่อาจเกิดขึ้นที่ฉันเห็น แต่ขอบคุณสำหรับคำชี้แจงของคุณ
Peppe LG

1
ไม่สามารถใช้งานได้หากปรับขนาดผ้าใบ ไม่แน่ใจว่าเป็นข้อผิดพลาดของเบราว์เซอร์หรือไม่
Jeroen

2
เพิ่มการใช้งานสำหรับคนอย่างฉัน:var canvas = document.getElementById('canvasID'); canvas.addEventListener("mousedown", function (e) { getCursorPosition(canvas, e);});
SnowOnion

179

อัปเดต (5/5/16): ควรใช้คำตอบของผู้รักชาติแทนเพราะทั้งง่ายและเชื่อถือได้มากกว่า


เนื่องจากผืนผ้าใบไม่ได้มีรูปแบบที่สัมพันธ์กับทั้งหน้าตลอดเวลาcanvas.offsetLeft/Topจึงไม่ส่งคืนสิ่งที่คุณต้องการเสมอไป มันจะส่งคืนจำนวนพิกเซลที่จะชดเชยเมื่อเทียบกับองค์ประกอบ offsetParent ของมันซึ่งอาจเป็นสิ่งที่เหมือนdivองค์ประกอบที่มีผ้าใบที่มีposition: relativeสไตล์ที่ใช้ ในการพิจารณาเรื่องนี้คุณต้องวนผ่านสายโซ่ของoffsetParents โดยเริ่มจากองค์ประกอบผ้าใบ รหัสนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับฉันทดสอบใน 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;

8
ไม่มันใช้วัตถุต้นแบบจาวาสคริปต์ในตัว --- developer.mozilla.org/en/…
garg

14
ฉัน Chrome มีevent.offsetXและแอตทริบิวต์ดังนั้นผมจึงมีการปรับเปลี่ยนวิธีการแก้ปัญหาของคุณโดยการเพิ่มevent.offsetY if (event.offsetX !== undefined && event.offsetY !== undefined) { return {x:event.offsetX, y:event.offsetY}; }ดูเหมือนว่ามันใช้งานได้
Baczek

3
Baczek นั้นถูกต้องเกี่ยวกับ Chrome event.offsetXและevent.offsetYยังใช้งานได้ใน IE9 สำหรับ Firefox (ทดสอบ w / v13) คุณสามารถใช้และevent.layerX event.layerY
mafafu

3
ฉันยังเพิ่มนี้: canvasX = event.pageX - totalOffsetX - document.body.scrollLeft; canvasY = event.pageY - totalOffsetY - document.body.scrollTop;
amirpc

4
คำตอบสุดท้ายนี้ใช้ไม่ได้สำหรับฉัน บน Firefox หากเลื่อนหน้าจอทั้งหมดฉันได้ค่าที่แทนที่เป็นเอาต์พุต สิ่งที่ได้ผลสำหรับฉันคือวิธีการแก้ปัญหาที่คล้ายกันมากในstackoverflow.com/a/10816667/578749ซึ่งแทนที่จะเป็น event.pageX / Y มันจะลบการคำนวณออฟเซ็ตจาก event.clientX / Y มีคนให้ตรวจสอบและอธิบายได้ไหม
lvella

33

เบราว์เซอร์สมัยใหม่ที่จัดการกับสิ่งนี้เพื่อคุณ Chrome, IE9 และ Firefox รองรับ offsetX / Y เช่นนี้ส่งผ่านเหตุการณ์จากตัวจัดการคลิก

function getRelativeCoords(event) {
    return { x: event.offsetX, y: event.offsetY };
}

เบราว์เซอร์ที่ทันสมัยส่วนใหญ่รองรับเลเยอร์ X / Y อย่างไรก็ตาม Chrome และ IE ใช้เลเยอร์ X / Y สำหรับออฟเซ็ตที่แน่นอนของการคลิกบนหน้าเว็บรวมถึงระยะขอบ, การเว้นระยะห่าง ฯลฯ ใน Firefox, layerX / Y และ offsetX / Y นั้นเทียบเท่ากัน ไม่มีอยู่ก่อนหน้านี้ ดังนั้นเพื่อความเข้ากันได้กับเบราว์เซอร์รุ่นเก่าคุณสามารถใช้:

function getRelativeCoords(event) {
    return { x: event.offsetX || event.layerX, y: event.offsetY || event.layerY };
}

1
ที่น่าสนใจว่า layerX, layerY ถูกกำหนดบนทั้ง Chrome และ Firefox แต่บน Chrome มันไม่ถูกต้อง (หรือหมายถึงอย่างอื่น)
Julian Mann

@JulianMann ขอบคุณสำหรับข้อมูล ฉันได้อัปเดตคำตอบนี้จากการสนับสนุนที่เป็นปัจจุบันมากขึ้น ดูเหมือนว่าคุณสามารถออกไปได้ด้วย offsetX / Y เกือบจะเป็นสากลแล้ว
mafafu

18

ตามที่สดquirksmodeclientXและ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()สำหรับ


เทียบเท่ากับคำตอบ @ N4ppeL
Paweł Szczur

13

ฉันทำสาธิตเต็มรูปแบบที่ใช้งานได้ในทุกเบราว์เซอร์ที่มีรหัสที่มาเต็มของการแก้ปัญหาของปัญหานี้: พิกัดของการคลิกเมาส์บนผ้าใบใน Javascript หากต้องการลองสาธิตให้คัดลอกรหัสและวางลงในโปรแกรมแก้ไขข้อความ จากนั้นให้บันทึกเป็น example.html และสุดท้ายให้เปิดไฟล์ด้วยเบราว์เซอร์


11

นี่คือการดัดแปลงเล็กน้อยสำหรับคำตอบของ 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}
}

7

ระวังในขณะที่ทำการแปลงพิกัด มีค่าที่ไม่ใช่ข้ามเบราว์เซอร์หลายค่าที่ส่งคืนในเหตุการณ์คลิก การใช้ clientX และ clientY เพียงอย่างเดียวไม่เพียงพอหากหน้าต่างเบราว์เซอร์ถูกเลื่อน (ตรวจสอบใน Firefox 3.5 และ Chrome 3.0)

โหมดนิสัยใจคอนี้บทความนี้ให้ฟังก์ชันที่ถูกต้องมากขึ้นที่สามารถใช้ pageX หรือ pageY หรือการรวมกันของ clientX กับ document.body.scrollLeft และ clientY ด้วย document.body.scrollTop เพื่อคำนวณพิกัดคลิกที่สัมพันธ์กับที่มาของเอกสาร

UPDATE: นอกจากนี้ offsetLeft และ offsetTop สัมพันธ์กับขนาดเบาะขององค์ประกอบไม่ใช่ขนาดภายใน แคนวาสที่มีการเติมเต็ม: ใช้สไตล์จะไม่รายงานด้านบนซ้ายของพื้นที่เนื้อหาเป็น offsetLeft มีวิธีแก้ไขปัญหาต่าง ๆ นี้ สิ่งที่ง่ายที่สุดอาจจะเป็นการลบเส้นขอบการเติมและอื่น ๆ ทั้งหมดของรูปแบบบนผืนผ้าใบเองและนำไปใช้กับกล่องที่บรรจุผ้าใบแทน


7

ผมไม่แน่ใจว่าสิ่งที่จุดของทุกคำตอบเหล่านี้ที่ห่วงผ่านองค์ประกอบที่ผู้ปกครองและทำทุกชนิดของสิ่งที่แปลก

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)

การจัดการพรมแดน CSS

หากผ้าใบมีชายแดนหนาสิ่งที่ได้รับความซับซ้อนน้อย คุณจะต้องลบเส้นขอบออกจากสี่เหลี่ยมผืนผ้าที่มีขอบเขต ซึ่งสามารถทำได้โดยใช้. 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)


6

นี่คือการสอนที่ดีมาก

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);

หวังว่านี่จะช่วยได้!


โซลูชันของ Ryan Artecona ไม่สามารถใช้งานได้กับเบราว์เซอร์แท็บเล็ตที่มีการซูม อย่างไรก็ตามอันนี้ก็ทำ
Andy Meyers

ไม่ทำงานกับรูปภาพที่มี CSS width/ heightoverriden แต่ยังคงเป็นหนึ่งในโซลูชันที่ดีที่สุด
Tomáš Zato - Reinstate Monica

3

ใช้ jQuery ในปี 2559 เพื่อรับพิกัดการคลิกเมื่อเทียบกับผืนผ้าใบฉัน:

$(canvas).click(function(jqEvent) {
    var coords = {
        x: jqEvent.pageX - $(canvas).offset().left,
        y: jqEvent.pageY - $(canvas).offset().top
    };
});

สามารถใช้งานได้เนื่องจากทั้งอ็อฟเซ็ต Canvas () และ jqEvent.pageX / Y นั้นสัมพันธ์กับเอกสารโดยไม่คำนึงถึงตำแหน่งการเลื่อน

โปรดทราบว่าถ้าผ้าใบของคุณจะถูกปรับขนาดแล้วพิกัดเหล่านี้จะไม่เหมือนกันเป็นผ้าใบพิกัดทางตรรกะ ที่จะได้รับเหล่านั้นคุณจะยังทำ:

var logicalCoords = {
    x: coords.x * (canvas.width / $(canvas).width()),
    y: coords.y * (canvas.height / $(canvas).height())
}

ว้าว. 'jqEvent' มีการกำหนดอย่างไร? หรือ 'ผ้าใบ' หรือในตัวอย่างที่สอง 'ประสาน'? คุณจำเป็นต้องเรียกใช้ตัวอย่างแรกก่อนหน้าสองหรือไม่ ในกรณีนั้นทำไมคุณไม่เขียน "รับสิ่งนั้นคุณจะต้องทำ" ทั้งหมดนี้ไปในฟังก์ชั่น onclick หรืออะไร ให้บริบทหน่อยเพื่อน และเมื่อพิจารณาคำถามเดิมที่ถูกถามในปี 2008 ฉันคิดว่าคุณต้องตอบในบริบทของเทคโนโลยีที่มีให้ในปี 2008 ปรับแต่งคำตอบของคุณโดยใช้ jQuery ที่ถูกต้องจากรุ่นที่มีให้ในเวลา (v1.2) ;)
Ayelis

1
โอเคขอโทษสำหรับความคิดของฉัน ฉันจะแก้ไขเพื่อลบสิ่งนั้น ฉันตั้งใจจะให้คำตอบโดยใช้กรอบงานล่าสุด และฉันเชื่อว่าโปรแกรมเมอร์ไม่จำเป็นต้องอธิบายว่า jqEvent, canvas และ coords คืออะไร
Sarsaparilla

ดูดี. ขอบคุณสำหรับข้อมูลของคุณ! ขอโทษฉันให้เวลาคุณยากลำบาก! ;)
Ayelis

3

ดังนั้นนี่คือทั้งง่าย ๆ แต่เป็นหัวข้อที่ซับซ้อนกว่าเล็กน้อย

ก่อนอื่นมักจะมีคำถามที่ conflated ที่นี่

  1. วิธีรับองค์ประกอบเมาส์สัมพันธ์

  2. วิธีรับพิกัด Canvas pixel Mouse สำหรับ 2D Canvas API หรือ WebGL

ดังนั้นคำตอบ

วิธีรับองค์ประกอบเมาส์สัมพันธ์

ไม่ว่าองค์ประกอบนั้นจะเป็น Canvas หรือไม่รับองค์ประกอบของเมาส์ที่สัมพันธ์กันกับองค์ประกอบทั้งหมด

มีคำตอบง่ายๆ 2 ข้อสำหรับคำถาม "วิธีรับพิกัดของเมาส์เทียบกับผืนผ้าใบ"

คำตอบง่ายๆ # 1 ใช้offsetXและoffsetY

canvas.addEventListner('mousemove', (e) => {
  const x = e.offsetX;
  const y = e.offsetY;
});

คำตอบนี้ใช้ได้ใน Chrome, Firefox และ Safari ไม่เหมือนกับค่าเหตุการณ์อื่น ๆ ทั้งหมดoffsetXและoffsetYนำการแปลง CSS มาพิจารณา

ปัญหาที่ใหญ่ที่สุดของoffsetXและoffsetYเป็นของ 2019/05 พวกเขาไม่ได้อยู่ในเหตุการณ์ที่สัมผัสและดังนั้นจึงไม่สามารถใช้กับ iOS Safari มีอยู่ในกิจกรรมตัวชี้ซึ่งมีอยู่ใน Chrome และ Firefox แต่ไม่ใช่ Safari แม้ว่าSafari จะทำงานอยู่จะทำงานกับมัน

อีกประเด็นคือเหตุการณ์ต้องอยู่บนผืนผ้าใบเอง หากคุณวางองค์ประกอบเหล่านี้ลงในองค์ประกอบอื่นหรือหน้าต่างคุณจะไม่สามารถเลือกผืนผ้าใบภายหลังเพื่อเป็นจุดอ้างอิงของคุณ

คำตอบง่าย # 2 การใช้งาน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 Canvas สำหรับ 2D Canvas API

สำหรับสิ่งนี้เราต้องนำค่าที่เราได้รับไปใช้และแปลงจากขนาดที่ผืนผ้าใบแสดงเป็นจำนวนพิกเซลในผืนผ้าใบ

ด้วย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และevent.clientXและevent.clientY


2

ฉันแนะนำลิงค์นี้ - 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ซึ่งหมายความว่าจำนวนที่จัดสรรจะถูกยกเลิกในทันที
4704


1

คุณสามารถทำได้:

var canvas = yourCanvasElement;
var mouseX = (event.clientX - (canvas.offsetLeft - canvas.scrollLeft)) - 2;
var mouseY = (event.clientY - (canvas.offsetTop - canvas.scrollTop)) - 2;

สิ่งนี้จะให้ตำแหน่งที่แน่นอนของตัวชี้เมาส์


1

ดูการสาธิตได้ที่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 };
  }

0

นี่คือการแก้ไขบางส่วนของวิธีการแก้ปัญหาของ 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};
}

0

ครั้งแรกกับคนอื่น ๆ ได้กล่าวว่าฟังก์ชั่นที่คุณต้องการที่จะได้รับตำแหน่งขององค์ประกอบผ้าใบ นี่คือวิธีการที่ดูดีกว่าคนอื่น ๆ ในหน้านี้เล็กน้อย (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ฟังก์ชั่นทั่วไปออกจากรหัสการจัดการเหตุการณ์ ( ตามที่ควรจะเป็นเราควรพยายามรักษาหน้าที่ของเราให้เป็นหนึ่งในภารกิจแต่ละงาน)

ค่าของoffsetLeftและoffsetTopจะสัมพันธ์กับoffsetParentซึ่งอาจเป็นdivโหนดwrapper (หรือสิ่งอื่นใดสำหรับเรื่องนั้น) เมื่อไม่มีองค์ประกอบที่ห่อcanvasพวกเขาจะสัมพันธ์กับbodyดังนั้นจึงไม่มีการชดเชยการลบ นี่คือเหตุผลที่เราต้องกำหนดตำแหน่งของผืนผ้าใบก่อนที่เราจะทำสิ่งอื่นได้

Similary e.pageXและe.pageYให้ตำแหน่งของเคอร์เซอร์สัมพันธ์กับเอกสาร นั่นเป็นเหตุผลที่เราลบผ้าใบตรงข้ามออกจากค่าเหล่านั้นเพื่อมาถึงตำแหน่งที่แท้จริง

ทางเลือกสำหรับตำแหน่งองค์ประกอบคือการใช้โดยตรงค่าของและe.layerX e.layerYวิธีนี้เชื่อถือได้น้อยกว่าวิธีการข้างต้นด้วยเหตุผลสองประการ:

  1. ค่าเหล่านี้จะสัมพันธ์กับเอกสารทั้งหมดเมื่อเหตุการณ์ไม่เกิดขึ้นภายในองค์ประกอบตำแหน่ง
  2. พวกเขาไม่ได้เป็นส่วนหนึ่งของมาตรฐานใด ๆ

0

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;

หลังจากลองวิธีแก้ปัญหามากมาย สิ่งนี้ใช้ได้สำหรับฉัน อาจช่วยคนอื่นโพสต์ดังนั้น รับได้จากที่นี่


0

นี่เป็นวิธีที่ง่ายกว่า (ไม่สามารถใช้กับเส้นขอบ / การเลื่อน):

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),
    };
}

0

ฉันกำลังสร้างแอปพลิเคชันที่มีผ้าใบทับ 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 )
};

-1

เฮ้นี่คือโดโจเพียงเพราะมันเป็นสิ่งที่ฉันมีรหัสในโครงการแล้ว

มันควรจะค่อนข้างชัดเจนว่าจะแปลงกลับไปเป็น non dojo vanilla JavaScript

  function onMouseClick(e) {
      var x = e.clientX;
      var y = e.clientY;
  }
  var canvas = dojo.byId(canvasId);
  dojo.connect(canvas,"click",onMouseClick);

หวังว่าจะช่วย


6
clientX / clientY ไม่ทำงานเหมือนกันในเบราว์เซอร์
tfwright
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.