Tarek Chentouf
Tarek Chentouf

Reputation: 15

Create a collision region on canvas elements That interacts with mouse Events

I want to create a collision region around a canvas element that enables me to interact with that element using mouse events width vanilla javascript.

To elaborate more on my problem here is the following:

at first I make an arc segment constructor with x, y, radius, beginAngle, endAngle, and a color arguments

var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');

/* arc class constructor */
function ArcSegment(x, y, radius, beginAngle, endAngle, segColor) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.beginAngle = beginAngle;
    this.endAngle = endAngle;
    this.segColor = segColor;

    this.update = function() {
        this.draw();
    }

    this.draw = function(){
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, this.beginAngle, this.endAngle, false);
        ctx.lineWidth = 20;
        ctx.strokeStyle = this.segColor;
        ctx.stroke();
    }
}

Secondly, i add some value to create those arc segments

/* x, y, radius, startAngle, endAngle and color */

var centerX = canvas.width/2;
var centerY = canvas.height/2;

var radiuses = [
    100,
    120
];

var pi = Math.PI;
var segmentStart = [
    pi/2,
    0
];

var segmentRotation = [
    1.4*pi,
    0.2*pi
];
var segmentColors = [
    "#133046",
    "#15959F"
];

Then, i draw Them on the canvas.

var segment1 = new ArcSegment(centerX, centerY, radiuses[0], segmentStart[0], segmentStart[0]+segmentRotation[0], segmentColors[0]);
segment1.update();

var segment2 = new ArcSegment(centerX, centerY, radiuses[1], segmentStart[1], segmentStart[1]+segmentRotation[1], segmentColors[1]);
segment2.update();

and here is the result:

Result

What i want now is a way to create a collision detection on top of each arc segment created, so when a mouse is clicked or moved on top of that specific arc segment

Region

a sequence of events can occur (like a rotation animation for example or so...).

all the research i've done suggest to get the x and y value of a rectangle and calculate the distance of mouse position (mouse.x, mouse.y) and the length of the rectangle, but that method doesn't work with an arc segment with a lineWidth property.

Any help on the subject would be very appreciated.

Upvotes: 1

Views: 500

Answers (1)

Mr. Reddy
Mr. Reddy

Reputation: 1114

Below is a pure mathematical approach, the key here is the code isPointInside

// Classes
function Arc(x, y, angle, arc, radius, colour, highlightColour) {
  this.x = x;
  this.y = y;
  this.angle = angle;
  this.arc = arc;
  this.radius = radius;
  this.colour = colour;
  this.highlightColour = highlightColour;
  this.highlighted = false;
  this.lineWidth = 20;
}

Arc.prototype = {
  isPointInside: function(x, y) {
    var _x = x - this.x;
    var _y = y - this.y;
    var distance = Math.sqrt(_x * _x + _y * _y);
    var invDistance = 1.0 / distance;
    var angle = Math.acos(
        _x * Math.cos(this.angle) * invDistance + 
        _y * Math.sin(this.angle) * invDistance
    );

    return distance > (this.radius - this.lineWidth/2)  && 
        distance < (this.radius + this.lineWidth/2) && 
        angle < this.arc/2;
  },

  render: function(ctx) {
    ctx.lineWidth = this.lineWidth;
    ctx.strokeStyle = this.highlighted ? this.highlightColour : this.colour;
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, this.angle - this.arc/2, this.angle + this.arc/2, false );
    ctx.stroke();
  }
};

// Variables
var canvas = null;
var ctx = null;
var arcs = [];

// Functions
function draw() {
  ctx.fillStyle = "gray";
  ctx.fillRect(0, 0, 999, 999);

  for (var i = 0; i < arcs.length; ++i) {
    arcs[i].render(ctx);
  }
}

// Event Listeners
function onMouseMove(e) {
  var bounds = canvas.getBoundingClientRect();
  var x = e.clientX - bounds.left;
  var y = e.clientY - bounds.top;

  for (var i = 0; i < arcs.length; ++i) {
    arcs[i].highlighted = arcs[i].isPointInside(x, y);
  }
  draw();
}

// Entry Point
onload = function() {
  canvas = document.getElementById("canvas");
  canvas.onmousemove = onMouseMove;

  ctx = canvas.getContext("2d");

  arcs.push(new Arc(190, 75, 0.2, 1.8, 60, "blue", "lime"));
  arcs.push(new Arc(90, 75, 3.5, 4.2, 60, "red", "lime"));
  draw();
}
<canvas id="canvas"></canvas>

Upvotes: 1

Related Questions