1

How do I get the exact point that was clicked on a canvas?

JS-Fiddle: http://jsfiddle.net/IQAndreas/yRMZ6/

None of the "built in" event properties (clientX, pageX, screenX) get the value I am searching for.

Searching Google or previous question reveals several answers, but most of them assume things like "the canvas must be fullscreen", or for some other reason, they fail to work in the test above.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
IQAndreas
  • 8,060
  • 8
  • 39
  • 74

2 Answers2

1

The main problem in your example is padding and real position of canvas start...

You can obtain padding using getComputedStyle function, and find real canvas start on page with this function:

function getPos(el) {
    var pl = parseInt(window.getComputedStyle(el, null).getPropertyValue('padding-left'));
    var pt = parseInt(window.getComputedStyle(el, null).getPropertyValue('padding-top'));
    for (var lx=0, ly=0;
         el != null;
         lx += el.offsetLeft, ly += el.offsetTop, el = el.offsetParent);
    return {x: lx + pl,y: ly + pt};
}

Workin' example on JSFiddle

Please note that border affects that aswell, so you may want to add window.getComputedStyle(el, null).getPropertyValue('border-top') and window.getComputedStyle(el, null).getPropertyValue('border-left') aswell.

Flash Thunder
  • 11,672
  • 8
  • 47
  • 91
0

You can use .getBoundingClientRect to get the canvas element position--including scrolling:

var BB=canvas.getBoundingClientRect();

Then you can get the mouse position relative to the canvas using event.clientX and event.clientY adjusted by the bounding box of the canvas element.

Note about borders and padding: You can click on the border of a canvas element and it will fire a canvas click event. This means [0,0] on the canvas is on the top-left corner of the canvas border. However the drawing area of a canvas is inside the border. Therefore, if you want to get the x,y of a click relative to the canvas drawing area you must also subtract the border size. The same is true about padding. Therefore if you want to get the x,y of a click relative to the canvas drawing area you must also subtract the padding size.

function handleMousedown(e){

    var BB=canvas.getBoundingClientRect();
    var x=e.clientX-BB.left-borderLeftWidth-paddingLeft;
    var y=e.clientY-BB.top-borderTopWidth-paddingTop;
    console.log(x,y);

}

To get the border and padding you can use .getComputedStyle:

// a function using getComputedStyle to fetch resolved CSS values

  function getStyle(elem,stylename){
      return(
          window
          .getComputedStyle(elem,null)
          .getPropertyValue(stylename)
      );
  }

// Fetch border and padding

var borderLeftWidth=getStyle(canvas,"border-left-width");
var borderTopWidth=getStyle(canvas,"border-top-width");
var paddingLeft=getStyle(canvas,"padding-left");
var paddingTop=getStyle(canvas,"padding-top");
markE
  • 102,905
  • 11
  • 164
  • 176
  • to be 100% complete, one must handle css scaling of the canvas. ( See http://stackoverflow.com/questions/20060691/most-modern-method-of-getting-mouse-position-within-a-canvas-in-native-javascrip ) – GameAlchemist Apr 12 '14 at 21:23
  • @GameAlchemist: Kind of true, but scaling Canvas with CSS can cause evil distortions. Browser window scaling is handled by getBoundingClientRect, but if you scale the canvas element with CSS you must adjust for this also. Again, I don't recommend scaling the canvas element with CSS as this might cause distortion of drawn elements. Instead, change the element width/height with canvas.width=newWidth and canvas.height-newHeight. Then redo getBoundingClientRect(). – markE Apr 12 '14 at 22:25
  • I forgot to reply : nope nope nope !! when margin and padding are not that usefull with a canvas, css scaling is very useful in the case of context2d (to get higher framerate with lower resolution) and css scaling is *required* for webgl (hidPi handling). – GameAlchemist Apr 18 '14 at 08:42
  • @GameAlchemist, the question and its link do include margins & padding but don't include scaling or webGL. If you want to post an answer to a hypothetically extended question...go ahead! :=) – markE Apr 18 '14 at 16:32
  • 1) O.P. said in comment 'The solution must work for padding, margins, floating elements, iframes, or whatever else the browser may throw at you.', so it's not an hypothetical question. and 2) css scaling is a perfectly valid solution for a few issues. – GameAlchemist Apr 18 '14 at 22:01
  • @GameAlchemist. So provide an answer ;=) – markE Apr 18 '14 at 22:11