在画布元素上创建与鼠标事件交互的碰撞区域

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在画布元素上创建与鼠标事件交互的碰撞区域相关的知识,希望对你有一定的参考价值。

我想在canvas元素周围创建一个碰撞区域,使我能够使用鼠标事件width vanilla javascript与该元素进行交互。

详细说明我的问题如下:

首先,我创建一个带有x,y,radius,beginAngle,endAngle和颜色参数的弧段构造函数

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

其次,我添加一些值来创建这些弧段

/* 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"
];

然后,我在画布上绘制它们。

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

这是结果:

我现在想要的是一种在创建的每个弧段上创建碰撞检测的方法,因此当单击鼠标或在特定弧段的顶部移动鼠标时

可以发生一系列事件(例如旋转动画,例如......)。

我所做的所有研究都建议获取矩形的x和y值并计算鼠标位置的距离(mouse.x,mouse.y)和矩形的长度,但该方法不适用于具有lineWidth属性的弧段。

任何有关该主题的帮助将非常感激。

答案

下面是一个纯数学方法,这里的关键是代码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>

以上是关于在画布元素上创建与鼠标事件交互的碰撞区域的主要内容,如果未能解决你的问题,请参考以下文章

canvas 鼠标绘图

画布绘制对象的鼠标事件

IE9 通过屏蔽画布元素转发鼠标事件

无法在画布中设置背景图像

如何根据鼠标点击位置参数触发Javascript中的事件?

画布粒子、碰撞和性能