web-dev-qa-db-fra.com

Mettre à jour le rectangle de toile HTML5 en survol?

J'ai du code qui dessine un rectangle sur une toile, mais je veux que ce rectangle change de couleur quand je passe la souris dessus.

Le problème est qu'après avoir dessiné le rectangle, je ne sais pas comment le sélectionner à nouveau pour effectuer le réglage.

Ce que je veux faire:

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

Vous ne pouvez pas faire cela avec la toile. Canvas n'est qu'un bitmap, la logique de survol doit donc être implémentée manuellement.

Voici comment:

  • Stockez tous les rectangles que vous voulez comme objet simple
  • Pour chaque déplacement de la souris sur l'élément canvas:
    • Obtenez la position de la souris
    • Parcourez la liste des objets
    • utiliser isPointInPath () pour détecter un "survol"
    • Redessiner les deux états

Exemple

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/>
28
user1693593

Il s'agit d'un code stable à la base de la réponse @ K3N. Le problème de base de son code est que lorsqu'une case est au-dessus de l'autre, les deux peuvent avoir le survol de la souris en même temps. Ma réponse résout parfaitement l'ajout d'une boucle "DESC" à "ASC".

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>
8
Hydroper

Le code ci-dessous ajoute une ombre au cercle de toile en le survolant.

<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.clientX,
      y = e.clientY,
      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>
1
Akshay

Vous pouvez utiliser canvas.addEventListener

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

Cela a fonctionné sur Google Chrome

1
Cloud1988

Vous devrez peut-être suivre la souris sur le canevas à l'aide de JavaScript et voir quand il se trouve sur votre rectangle, puis changer la couleur. Voir le code ci-dessous de mon article de blog

<!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>
1
dwightreid
    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"/>
0
N K

Je pense que c'est une réponse un peu plus approfondie qui fonctionnerait mieux pour vous, surtout si vous êtes intéressé par la conception de jeux avec l'élément canvas.

La principale raison pour laquelle cela fonctionnerait mieux pour vous est qu'elle se concentre davantage sur une approche OOP (programmation orientée objet). Cela permet aux objets d'être définis, suivis et modifiés ultérieurement via certains événement ou circonstance. Il permet également une mise à l'échelle facile de votre code et à mon avis est juste plus lisible et organisé.

Essentiellement, ce que vous avez ici est en collision de deux formes. Le curseur et le point/objet individuel qu'il survole. Avec des carrés, des rectangles ou des cercles de base, ce n'est pas trop mal. Mais, si vous comparez deux autres formes uniques, vous devrez en savoir plus sur Théorème d'axe de séparation (SAT) et d'autres techniques de collision. À ce stade, l'optimisation et les performances deviendront une préoccupation, mais pour l'instant, je pense que c'est l'approche optimale.

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;

Il y a encore une chose que je voudrais suggérer. Je n'ai pas encore fait de tests à ce sujet, mais je soupçonne que l'utilisation d'une simple trigonométrie pour détecter si nos objets circulaires entrent en collision se produirait mieux sur la méthode ctx.IsPointInPath ().

Cependant, si vous utilisez des chemins et des formes plus complexes, la méthode ctx.IsPointInPath () serait probablement la voie à suivre. sinon une autre forme plus étendue de détection de collision comme je l'ai mentionné plus tôt.

Le changement résultant ressemblerait à ceci ...

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

voici un lien contenant des exemples et d'autres liens liés à détection de collision

J'espère que vous pouvez voir avec quelle facilité quelque chose comme ça peut être modifié et utilisé dans les jeux et quoi que ce soit d'autre. J'espère que cela t'aides.

0
James

Considérez ce code suivant:

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

[~ # ~] démo [~ # ~]

0
Manwal