向canvas元素添加一个单击事件处理程序,返回单击的x和y坐标(相对于canvas元素),最简单的方法是什么?
不需要传统浏览器兼容性,Safari、Opera和Firefox就可以了。
向canvas元素添加一个单击事件处理程序,返回单击的x和y坐标(相对于canvas元素),最简单的方法是什么?
不需要传统浏览器兼容性,Safari、Opera和Firefox就可以了。
当前回答
我推荐这个链接 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>
其他回答
在进行坐标转换时要小心;在单击事件中返回多个非跨浏览器的值。如果浏览器窗口是滚动的(在Firefox 3.5和Chrome 3.0中验证),仅使用clientX和clienti是不够的。
这篇怪异模式文章提供了一个更正确的函数,可以使用pageX或pageY,或者使用clientX与document.body. scrollleft和clienti与document.body. scrolltop的组合来计算相对于文档原点的单击坐标。
更新:另外,offsetLeft和offsetTop是相对于元素的填充大小,而不是内部大小。应用了padding: style的画布不会将其内容区域的左上角报告为offsetLeft。这个问题有多种解决方案;最简单的方法可能是清除画布本身的所有边框、填充等样式,而是将它们应用到包含画布的框中。
更新(5/5/16):应该使用patriques的答案,因为它既简单又可靠。
Since the canvas isn't always styled relative to the entire page, the canvas.offsetLeft/Top doesn't always return what you need. It will return the number of pixels it is offset relative to its offsetParent element, which can be something like a div element containing the canvas with a position: relative style applied. To account for this you need to loop through the chain of offsetParents, beginning with the canvas element itself. This code works perfectly for me, tested in Firefox and Safari but should work for all.
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;
最后一行可以方便地获取相对于canvas元素的鼠标坐标。要得到有用的坐标只需要
coords = canvas.relMouseCoords(event);
canvasX = coords.x;
canvasY = coords.y;
三JS 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;
在尝试了许多解决方案后。这对我很管用。可能会帮助其他人,因此发帖。从这里开始
所以这是一个简单但比看起来更复杂的话题。
首先,这里通常有一些合并的问题
如何获得元素相对鼠标坐标 如何获得画布像素鼠标坐标为2D画布API或WebGL
所以,答案
如何获得元素相对鼠标坐标
无论元素是否是画布,获取元素相对鼠标坐标对所有元素都是相同的。
“如何获得画布相对鼠标坐标”这个问题有两个简单的答案
简单答案#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, clienti和canvas.getBoundingClientRect
如果你不关心CSS转换,下一个最简单的答案是调用canvas。getBoundingClientRect()并从clientX中减去左边,从cliententy中减去顶部
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;
});
如何获得画布像素鼠标坐标为2D画布API
为此,我们需要将上面得到的值从画布显示的大小转换为画布本身的像素数
与画布。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 elementRelativeY = e.offsetY;
const canvasRelativeX = elementRelativeX * canvas.width / canvas.clientWidth;
const canvasRelativeY = elementRelativeY * canvas.height / canvas.clientHeight;
});
注意:在所有情况下都不要给画布添加填充或边框。这样做将极大地复杂化代码。而不是你想要一个边框或填充在一些其他元素的画布周围,并添加填充和或边框到外部元素。
使用事件的工作示例。offsetX, event.offsetY
[...document.querySelectorAll('canvas')].forEach((canvas) => { const ctx = canvas.getContext('2d'); ctx.canvas.width = ctx.canvas.clientWidth; ctx.canvas.height = ctx.canvas.clientHeight; let count = 0; function draw(e, radius = 1) { const pos = { x: e.offsetX * canvas.width / canvas.clientWidth, y: e.offsetY * canvas.height / canvas.clientHeight, }; document.querySelector('#debug').textContent = count; ctx.beginPath(); ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2); ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5); ctx.fill(); } function preventDefault(e) { e.preventDefault(); } if (window.PointerEvent) { canvas.addEventListener('pointermove', (e) => { draw(e, Math.max(Math.max(e.width, e.height) / 2, 1)); }); canvas.addEventListener('touchstart', preventDefault, {passive: false}); canvas.addEventListener('touchmove', preventDefault, {passive: false}); } else { canvas.addEventListener('mousemove', draw); canvas.addEventListener('mousedown', preventDefault); } }); function hsl(h, s, l) { return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`; } .scene { width: 200px; height: 200px; perspective: 600px; } .cube { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; animation-duration: 16s; animation-name: rotate; animation-iteration-count: infinite; animation-timing-function: linear; } @keyframes rotate { from { transform: translateZ(-100px) rotateX( 0deg) rotateY( 0deg); } to { transform: translateZ(-100px) rotateX(360deg) rotateY(720deg); } } .cube__face { position: absolute; width: 200px; height: 200px; display: block; } .cube__face--front { background: rgba(255, 0, 0, 0.2); transform: rotateY( 0deg) translateZ(100px); } .cube__face--right { background: rgba(0, 255, 0, 0.2); transform: rotateY( 90deg) translateZ(100px); } .cube__face--back { background: rgba(0, 0, 255, 0.2); transform: rotateY(180deg) translateZ(100px); } .cube__face--left { background: rgba(255, 255, 0, 0.2); transform: rotateY(-90deg) translateZ(100px); } .cube__face--top { background: rgba(0, 255, 255, 0.2); transform: rotateX( 90deg) translateZ(100px); } .cube__face--bottom { background: rgba(255, 0, 255, 0.2); transform: rotateX(-90deg) translateZ(100px); } <div class="scene"> <div class="cube"> <canvas class="cube__face cube__face--front"></canvas> <canvas class="cube__face cube__face--back"></canvas> <canvas class="cube__face cube__face--right"></canvas> <canvas class="cube__face cube__face--left"></canvas> <canvas class="cube__face cube__face--top"></canvas> <canvas class="cube__face cube__face--bottom"></canvas> </div> </div> <pre id="debug"></pre>
使用画布的工作示例。getBoundingClientRect和事件。clientX和event.clientY
const canvas = document.querySelector('canvas'); const ctx = canvas.getContext('2d'); ctx.canvas.width = ctx.canvas.clientWidth; ctx.canvas.height = ctx.canvas.clientHeight; let count = 0; function draw(e, radius = 1) { const rect = canvas.getBoundingClientRect(); const pos = { x: (e.clientX - rect.left) * canvas.width / canvas.clientWidth, y: (e.clientY - rect.top) * canvas.height / canvas.clientHeight, }; ctx.beginPath(); ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2); ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5); ctx.fill(); } function preventDefault(e) { e.preventDefault(); } if (window.PointerEvent) { canvas.addEventListener('pointermove', (e) => { draw(e, Math.max(Math.max(e.width, e.height) / 2, 1)); }); canvas.addEventListener('touchstart', preventDefault, {passive: false}); canvas.addEventListener('touchmove', preventDefault, {passive: false}); } else { canvas.addEventListener('mousemove', draw); canvas.addEventListener('mousedown', preventDefault); } function hsl(h, s, l) { return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`; } canvas { background: #FED; } <canvas width="400" height="100" style="width: 300px; height: 200px"></canvas> <div>canvas deliberately has differnt CSS size vs drawingbuffer size</div>
如果你喜欢简单,但仍然需要跨浏览器功能,我发现这个解决方案最适合我。这是一个简化的@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)
})