悬停时更新HTML5画布矩形

Update HTML5 canvas rectangle on hover?

本文关键字:布矩形 HTML5 更新 悬停      更新时间:2023-09-26

我有一些代码可以在画布上绘制一个矩形,但我希望当我将鼠标悬停在矩形上时,它会改变颜色。

问题是,在我画完矩形后,我不确定如何再次选择它来进行调整。

我想做的事:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
$('c.[rectangle]').hover(function(this){
    this.fillStyle = 'red';
    this.fill();
});

您不能使用画布开箱即用。Canvas只是一个位图,因此悬停逻辑必须手动实现。

以下是如何:

  • 将所有想要的矩形存储为简单对象
  • 对于画布元素上的每次鼠标移动:
    • 获取鼠标位置
    • 遍历对象列表
    • 使用isPointInPath()检测"悬停"
    • 重新绘制两个状态

示例

var canvas = document.querySelector("canvas"),
    ctx = canvas.getContext("2d"),
    rects = [
        {x: 10, y: 10, w: 200, h: 50},
        {x: 50, y: 70, w: 150, h: 30}    // etc.
    ], i = 0, r;
// render initial rects.
while(r = rects[i++]) ctx.rect(r.x, r.y, r.w, r.h);
ctx.fillStyle = "blue"; ctx.fill();
canvas.onmousemove = function(e) {
  // important: correct mouse position:
  var rect = this.getBoundingClientRect(),
      x = e.clientX - rect.left,
      y = e.clientY - rect.top,
      i = 0, r;
  
  ctx.clearRect(0, 0, canvas.width, canvas.height); // for demo
   
  while(r = rects[i++]) {
    // add a single rect to path:
    ctx.beginPath();
    ctx.rect(r.x, r.y, r.w, r.h);    
    
    // check if we hover it, fill red, if not fill it blue
    ctx.fillStyle = ctx.isPointInPath(x, y) ? "red" : "blue";
    ctx.fill();
  }
};
<canvas/>

这是一个基于@K3N答案的稳定代码。他的代码的基本问题是,当一个框在另一个框上时,两个框可能会同时悬停鼠标。我的答案完美地解决了在"ASC"循环中添加"DESC"的问题。

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d");
var map = [
    {x: 20, y: 20, w: 60, h: 60},
    {x: 30, y: 50, w: 76, h: 60}
];
var hover = false, id;
var _i, _b;
function renderMap() {
    for(_i = 0; _b = map[_i]; _i ++) {
        ctx.fillStyle = (hover && id === _i) ? "red" : "blue";
        ctx.fillRect(_b.x, _b.y, _b.w, _b.h);
    }
}
// Render everything
renderMap();
canvas.onmousemove = function(e) {
    // Get the current mouse position
    var r = canvas.getBoundingClientRect(),
        x = e.clientX - r.left, y = e.clientY - r.top;
    hover = false;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for(var i = map.length - 1, b; b = map[i]; i--) {
        if(x >= b.x && x <= b.x + b.w &&
           y >= b.y && y <= b.y + b.h) {
            // The mouse honestly hits the rect
            hover = true;
            id = i;
            break;
        }
    }
    // Draw the rectangles by Z (ASC)
    renderMap();
}
<canvas id="canvas"></canvas>

下面的代码在悬停时为画布圆圈添加阴影。

<html>
<body>
  <canvas id="myCanvas" width="1000" height="500" style="border:1px solid #d3d3d3;">
    Your browser does not support the HTML5 canvas tag.</canvas>
</body>
<script>
  var canvas = document.getElementById("myCanvas"),
    ctx = canvas.getContext("2d"),
    circle = [{
        x: 60,
        y: 50,
        r: 40,
      },
      {
        x: 100,
        y: 150,
        r: 50,
      } // etc.
    ];
  // render initial rects.
  for (var i = 0; i < circle.length; i++) {
    ctx.beginPath();
    ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
    ctx.fillStyle = "blue";
    ctx.fill();
  }
  canvas.onmousemove = function(e) {
    var x = e.pageX,
      y = e.pageY,
      i = 0,
      r;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (let i = 0; i < circle.length; i++) {
      if ((x > circle[i].x - circle[i].r) && (y > circle[i].y - circle[i].r) && (x < circle[i].x + circle[i].r) && (y < circle[i].y + circle[i].r)) {

        ctx.beginPath();
        ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
        ctx.fillStyle = "blue";
        ctx.fill();
        ctx.shadowBlur = 10;
        ctx.lineWidth = 3;
        ctx.strokeStyle = 'rgb(255,255,255)';
        ctx.shadowColor = 'grey';
        ctx.stroke();
        ctx.shadowColor = 'white';
        ctx.shadowBlur = 0;
      } else {
        ctx.beginPath();
        ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
        ctx.fillStyle = "blue";
        ctx.fill();
        ctx.shadowColor = 'white';
        ctx.shadowBlur = 0;
      }
    }
  };
</script>
</html>

您可能需要使用JavaScript跟踪画布上的鼠标,看看它何时在矩形上方,然后更改颜色。请参阅我的博客文章中的以下代码

<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="700" height="500" style="border:1px solid #c3c3c3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var myRect={x:150, y:75, w:50, h:50, color:"red"};
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = myRect.color;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
c.addEventListener("mousemove", function(e){
if ((e.clientX>=myRect.x)&(e.clientX<=myRect.x+myRect.w)&(e.clientY>=myRect.y)&(e.clientY<=myRect.y+myRect.h)){
myRect.color = "green";}
else{
myRect.color = "red";}
updateCanvas();
}, false);

function updateCanvas(){
ctx.fillStyle = myRect.color;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
}
</script>
</body>
</html>

我相信这是一个稍微深入一点的答案,对你来说会更好,尤其是如果你对带有画布元素的游戏设计感兴趣的话。

这对你来说效果更好的主要原因是它更关注OOP(面向对象编程)方法。这允许在以后通过某些事件或环境来定义、跟踪和更改对象。它还允许轻松地扩展代码,在我看来,它只是更具可读性和组织性。

本质上,这里有两个形状碰撞。光标及其悬停的单个点/对象。对于基本的正方形、矩形或圆形,这还不错。但是,如果你正在比较两种更独特的形状,你需要更多地了解分离轴定理(SAT)和其他碰撞技术。到那时,优化和性能将成为一个问题,但目前我认为这是最佳方法。

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;
const cx = width / 2;
const cy = height / 2;
const twoPie = Math.PI * 2;
const points = []; // This will be the array we store our hover points in later
class Point {
  constructor(x, y, r) {
    this.x = x;
    this.y = y;
    this.r = r || 0;
  }
}
class HoverPoint extends Point {
  constructor(x, y, r, color, hoverColor) {
    super(x, y, r);
    this.color = color;
    this.hoverColor = hoverColor;
    this.hovered = false;
    this.path = new Path2D();
  }
  draw() {
    this.hovered ? ctx.fillStyle = this.hoverColor : ctx.fillStyle = this.color;
    this.path.arc(this.x, this.y, this.r, 0, twoPie);
    ctx.fill(this.path);
  }
}
class Cursor extends Point {
  constructor(x, y, r) {
    super(x, y, r);
  }
  collisionCheck(points) {
  // This is the method that will be called during the animate function that 
  // will check the cursors position against each of our objects in the points array.
    document.body.style.cursor = "default";
    points.forEach(point => {
      point.hovered = false;
      if (ctx.isPointInPath(point.path, this.x, this.y)) {
        document.body.style.cursor = "pointer";
        point.hovered = true;
      }
    });
  }
}
function createPoints() {
  // Create your points and add them to the points array.
  points.push(new HoverPoint(cx, cy, 100, 'red', 'coral'));
  points.push(new HoverPoint(cx + 250, cy - 100, 50, 'teal', 'skyBlue'));
  // ....
}
function update() {
  ctx.clearRect(0, 0, width, height);
  points.forEach(point => point.draw());
}
function animate(e) {
  const cursor = new Cursor(e.offsetX, e.offsetY);
  update();
  cursor.collisionCheck(points);
}
createPoints();
update();
canvas.onmousemove = animate;

还有一件事我想提出。我还没有对此进行测试,但我怀疑使用一些简单的三角法来检测我们的圆形物体是否碰撞会比ctx更好。IsPointInPath()方法。

然而,如果您使用的是更复杂的路径和形状,那么ctx。IsPointInPath()方法很可能是最好的方法。如果不是我前面提到的其他更广泛形式的碰撞检测的话。

由此产生的变化看起来是这样的。。。

class Cursor extends Point {
  constructor(x, y, r) {
    super(x, y, r);
  }
  collisionCheck(points) {
    document.body.style.cursor = "default";
    points.forEach(point => {
      let dx = point.x - this.x;
      let dy = point.y - this.y;
      let distance = Math.hypot(dx, dy);
      let dr = point.r + this.r;
      point.hovered = false;
      // If the distance between the two objects is less then their combined radius
      // then they must be touching.
      if (distance < dr) {
        document.body.style.cursor = "pointer";
        point.hovered = true;
      }
    });
  }
}

这是一个链接,包含了与碰撞检测相关的其他链接的示例

我希望你能看到像这样的东西是多么容易被修改并在游戏和其他任何东西中使用。希望这能有所帮助。

我知道这是旧的,但我很惊讶没有人提到JCanvas。它增加了在事件上设置画布动画的简单性。此处提供更多文档https://projects.calebevans.me/jcanvas/docs/mouseEvents/

<html lang="en">
<head>
<!-- css and other -->
</head>
<body onload="draw();">
<canvas id = "canvas" width="500" height="500" style= border:1px solid #000000;"> </canvas>
<script>
function draw() {
    $('canvas').drawRect({
        layer: true,
        fillStyle:'#333',
        x:100, y: 200,
        width: 600,
        height: 400,
        mouseover: function(layer) {
                $(this).animateLayer(layer, {
                    fillStyle: 'green'
                 }, 1000, 'swing');
                }
    }); 
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"  crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jcanvas/21.0.1/jcanvas.js" crossorigin="anonymous"></script>
</body>
</html>

考虑以下代码:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
c.addEventListener("mouseover", doMouseOver, false);//added event to canvas
function doMouseOver(e){
    ctx.fillStyle = 'red';
    ctx.fill();
}

DEMO

您可以使用canvas.addEventListener

var canvas = document.getElementById('canvas0');
canvas.addEventListener('mouseover', function() { /*your code*/ }, false);

它在谷歌chrome 上运行

    var c=document.getElementById("myCanvas");
    var ctx=c.getContext("2d");
    ctx.rect(20,20,150,100);
    ctx.stroke();
    
    $(c).hover(function(e){
        ctx.fillStyle = 'red';
        ctx.fill();
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="myCanvas"/>