0

I am trying to show if a Mouse of over any of the two lines that form the cross shape Right now it works if you hover on bottom right part of 'X'. BUt not on all

const x1 = 50;
const y1 = 50;

const x2 = 100;
const y2 = 100;

const xdiff = x2 - x1;
const ydiff = y2 - y1;

function drawX(x, y) {
ctx.beginPath();

ctx.moveTo(x - xdiff, y - ydiff);
ctx.lineTo(x + xdiff, y + ydiff);
ctx.stroke();

ctx.moveTo(x + xdiff, y - ydiff);
ctx.lineTo(x - xdiff, y + ydiff);
ctx.stroke();
}


const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");

 
drawX(x1,y1);

function myFunction(e) {
const x = e.clientX;
const y = e.clientY;
//const coor = "Coordinates: (" + x + "," + y + ")";
const overShape = () => ( (x > x1 && x < (x1 +xdiff)) && (y > y1 && y < (y1 +ydiff)) ) ;

console.log('I am over X, ', overShape() )

}
<canvas id="myCanvas" width="300" height="150" onmousemove="myFunction(event)" style="border:1px solid #d3d3d3;">
  Your browser does not support the HTML5 canvas tag.</canvas>
dota2pro
  • 7,220
  • 7
  • 44
  • 79
  • This article covers the general technique of collision detection, which is what you are attempting: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection – Garrett Motzner Jun 18 '19 at 21:50
  • Possible duplicate of [Get dimensions of a HTML5 canvas graphic (Arc), when an event occurs](https://stackoverflow.com/questions/52731097/get-dimensions-of-a-html5-canvas-graphic-arc-when-an-event-occurs/52731475#52731475) – Kaiido Jun 19 '19 at 00:44
  • 1
    Also note that sonce you are not beginning a new path, you are actually stroking twice the first line. – Kaiido Jun 19 '19 at 00:45
  • @Kaiido cool, didn't knew that function existed – dota2pro Jun 19 '19 at 01:34
  • Would you accept it as a dupe target though? I wasn't sure so only typed this comment but didn't actually cast a lose vote yet. – Kaiido Jun 19 '19 at 01:36
  • @Kaiido its not really a dupe because isPointInStroke only works if you hover on the stroke, but the accepted answer will work because no one has that precision in real life to hover exactly on the stroke, basically isPointInStroke is not the best way for line size less than 4px in my opinion acceptable answer has a customizable threshold – dota2pro Jun 19 '19 at 01:39
  • @ dota2pro, you can also very well control this threshold with isPointInStroke :-) Just set the lineWidth of your context before calling it. – Kaiido Jun 19 '19 at 01:41
  • @Kaiido this was an example the actual code I am working on reads x1,y1,x2,y2 color and line width from and Rest Api and draws different shapes on canvas, there might be multiple shapes with different lineWidths – dota2pro Jun 19 '19 at 01:46
  • 1
    That doesn't change anything, just like you would set the threshold in the accepted answer's function, you could alos set it with isPointInStroke: https://jsfiddle.net/b5gt1q4u/ – Kaiido Jun 19 '19 at 01:50
  • I agree this does make it more simple – dota2pro Jun 19 '19 at 01:55

3 Answers3

1

Instead of

const overShape = () => ( (x > x1 && x < (x1 +xdiff)) && (y > y1 && y < (y1 +ydiff)) ) ;

Your bounds should be checked like

const overShape = () => ( (x  > (x1 - xdiff) && x < (x1 +xdiff)) && (y > (y1 - ydiff) && y < (y1 +ydiff)) ) ;

const x1 = 50;
const y1 = 50;

const x2 = 100;
const y2 = 100;

const xdiff = x2 - x1;
const ydiff = y2 - y1;

function drawX(x, y) {
  ctx.beginPath();

  ctx.moveTo(x - xdiff, y - ydiff);
  ctx.lineTo(x + xdiff, y + ydiff);
  ctx.stroke();

  ctx.moveTo(x + xdiff, y - ydiff);
  ctx.lineTo(x - xdiff, y + ydiff);
  ctx.stroke();
}


const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");


drawX(x1, y1);

function myFunction(e) {
  const x = e.clientX;
  const y = e.clientY;
  //const coor = "Coordinates: (" + x + "," + y + ")";
  const overShape = () => ((x > (x1 - xdiff) && x < (x1 + xdiff)) && (y > (y1 - ydiff) && y < (y1 + ydiff)));

  console.log('I am over X, ', overShape())

}
<canvas id="myCanvas" width="300" height="150" onmousemove="myFunction(event)" style="border:1px solid #d3d3d3;">
  Your browser does not support the HTML5 canvas tag.</canvas>
dota2pro
  • 7,220
  • 7
  • 44
  • 79
  • Is there any way to make it more precise ? – dota2pro Jun 18 '19 at 21:32
  • More precise in what way? Only true when it is over a painted pixel? (So a smaller hitbox?) – Garrett Motzner Jun 18 '19 at 21:39
  • @GarrettMotzner yes even if its a little off its ok but right now it true between some spaces inside the cross – dota2pro Jun 18 '19 at 21:41
  • This might work : https://stackoverflow.com/questions/6735470/get-pixel-color-from-canvas-on-mouseover. The trick for this though is that if you have a lot of items rendered, then it might not be accurate. You could, however, _potentially_ use a hidden canvas layer that only has (copies of) the x's. – Garrett Motzner Jun 18 '19 at 21:48
  • 1
    This is probably almost exactly what you want: https://stackoverflow.com/questions/24043967/detect-if-mouse-is-over-an-object-inside-canvas – Garrett Motzner Jun 18 '19 at 22:00
1

You could solve this by checking the color at the current mouse position. If it's black your most likely touching the X. Of course this will just work if there aren't any other black colors in your image so the better way is done using some math.

So first define the start and end points of your lines as individual points - this way we can use those for the calculation.

const pointA={x:0,y:0};
const pointB={x:100,y:100};
const pointC={x:100,y:0};
const pointD={x:0,y:100};

Now to check if you're on one of the lines those four points form you can use this formula:

var treshold=0.01;
var onFirstLine=distance(pointA, mousePosition) + distance(pointB, mousePosition)-distance(pointA, pointB)<treshold;

Here we're adding up the distances from pointA and pointB to the mouse position and if the result would be equal to the distance from pointA to pointB we've detected a hit. The problem is we would need to hit it extremely precise that's why we're subtracting the distance and compare it to a treshold instead.

Here's the complete example:

const pointA={x:0,y:0};
const pointB={x:100,y:100};
const pointC={x:100,y:0};
const pointD={x:0,y:100};

function drawX(x, y) {
  ctx.beginPath();

  ctx.moveTo(pointA.x, pointA.y);
  ctx.lineTo(pointB.x, pointB.y);
  ctx.stroke();

  ctx.moveTo(pointC.x, pointC.y);
  ctx.lineTo(pointD.x, pointD.y)
  ctx.stroke();
}

const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");

drawX();

function myFunction(e) {
  var tempPoint = {
    x: e.offsetX,
    y: e.offsetY
  };
  var treshold = 0.01;
  if (distance(pointA, tempPoint) + distance(pointB, tempPoint) - distance(pointA, pointB) < treshold || distance(pointC, tempPoint) + distance(pointD, tempPoint) - distance(pointC, pointD) < treshold) {
    console.log("you're touching X")
  } else {
    console.log("you're NOT touching X")
  }
}

function distance(locPointA, locPointB) {
  return Math.sqrt(Math.pow(locPointB.x - locPointA.x, 2) + Math.pow(locPointB.y - locPointA.y, 2));
}
<canvas id="myCanvas" width="300" height="150" onmousemove="myFunction(event)" style="border:1px solid #d3d3d3;">
  Your browser does not support the HTML5 canvas tag.</canvas>
obscure
  • 11,916
  • 2
  • 17
  • 36
0

Using @Kaiido 's solution This is the best way to solve this

const x1 = 50;
const y1 = 50;

const x2 = 100;
const y2 = 100;

const xdiff = x2 - x1;
const ydiff = y2 - y1;

const threshold = 20;

let path;

function drawX(x, y) {
  path = new Path2D();
  path.moveTo(x - xdiff, y - ydiff);
  path.lineTo(x + xdiff, y + ydiff);
  path.moveTo(x + xdiff, y - ydiff);
  path.lineTo(x - xdiff, y + ydiff);
  ctx.stroke(path);
}


const c = document.getElementById("canvas");
const ctx = c.getContext("2d");


drawX(x1, y1);

canvas.onmousemove = ({
  offsetX,
  offsetY
}) => {
  ctx.lineWidth = threshold;
  console.log('I am over X, ', ctx.isPointInStroke(path, offsetX, offsetY))
}
canvas {
  border: 1px solid #d3d3d3;
  width: 300px;
  height: 150;
}
<canvas id="canvas">
      Your browser does not support the HTML5 canvas tag.</canvas>
dota2pro
  • 7,220
  • 7
  • 44
  • 79