react/addEventListener mouseenter и mouseleave срабатывают дважды каждый раз

#javascript #reactjs #canvas

#язык JavaScript #реагирует на #холст

Вопрос:

Я создаю пчелу, которая летает по холсту, я даю ей случайное место назначения, как только она достигает его, создается новое место назначения, поэтому она продолжает блуждать. Теперь я пытаюсь сделать так, чтобы, если моя мышь подойдет достаточно близко, она начала бы следовать за моей мышью вместо точки назначения. Все было бы хорошо, и я думаю, что знаю, как это сделать, но проблема в том, что мои слушатели стреляют дважды при использовании react и только один раз только в Js. Правка: Похоже, все дублируется, просто попробовал рисовать без очистки холста, и я получаю две пчелы двух направлений.

Апп

 import Canvas from "./canvas/Canvas"; import "./App.css"; import { useRef, useEffect, useState } from "react";  function App() {  const appRef = useRef();  const [loaded, setLoaded] = useState(false);   useEffect(() =gt; {  if (appRef) {  setLoaded(true);  }  return () =gt; setLoaded(false);  }, []);   return (  lt;div className="App" ref={appRef}gt;  {loaded ? (  lt;Canvas  width={appRef.current.clientWidth}  height={appRef.current.clientHeight}  /gt;  ) : (  lt;gt;lt;/gt;  )}  lt;/divgt;  ); }  export default App;  

Холст

 import Bee from "./bee"; import { useState, useCallback, useEffect } from "react"; import "./canvas.css";  const Canvas = ({ width, height }) =gt; {  const [canvas, setCanvas] = useState();  const [context, setContext] = useState();   const canvasCallback = useCallback((node) =gt; {  if (node) {  setCanvas(node);  }  }, []);   useEffect(() =gt; {  if (canvas) {  setContext(canvas.getContext("2d"));   canvas.width = width;  canvas.height = height;  }  }, [canvas]);   if (context) {  // Game loop  // let fps = 60;  // let now;  // let then = Date.now();  // let delta;   const bee = new Bee(context, canvas.width, canvas.height, canvas);  const gameLoop = () =gt; {  // let interval = 1000 / fps;  window.requestAnimationFrame(gameLoop);   // now = Date.now();  // delta = now - then;   // if (delta gt; interval) {  // then = now - (delta % interval);   bee.move();  // }  };  window.requestAnimationFrame(gameLoop);  //Game loop end  }   return lt;canvas id="canvas" ref={canvasCallback} /gt;; };  export default Canvas;  

Bee

 import beeImg from "./bee.png"; import beeLeftImg from "./bee-left.png";  function Bee(ctx, width, height, parent) {  this.currentPos = { x: width / 5, y: height / 3 };  this.beeWidth = 32;  this.beeHeight = 32;  this.velocity = { x: 0.1, y: 0 };  this.acc = { x: 0, y: 0 };  this.direction = { x: Math.random() * width, y: Math.random() * height };  this.mouse = { x: 0, y: 0 };  this.mouseInside = false;   let loadedR = false;  let loadedL = false;  let img = new Image();  img.src = beeImg;  img.onload = () =gt; {  loadedR = true;  };  let imgLeft = new Image();  imgLeft.src = beeLeftImg;  imgLeft.onload = () =gt; {  loadedL = true;  };   this.createBee = (x, y) =gt; {  if (this.velocity.x gt;= 0 amp;amp; loadedR) {  ctx.clearRect(0, 0, width, height, this.bee);  ctx.drawImage(img, x, y, this.beeHeight, this.beeHeight);  }  if (this.velocity.x lt; 0 amp;amp; loadedL) {  ctx.clearRect(0, 0, width, height, this.bee);  ctx.drawImage(imgLeft, x, y, this.beeHeight, this.beeHeight);  }  };   window.addEventListener("mousemove", (e) =gt; {  this.mouse = { x: e.clientX, y: e.clientY };   });  parent.addEventListener("mouseenter", (e) =gt; {  console.log(e, "enter");  e.stopPropagation()  });  parent.addEventListener("mouseleave", (e) =gt; {  console.log(e, "left");  e.stopPropagation()  });   let goTo = (x, y) =gt; {  let v = {  x: this.currentPos.x - x,  y: this.currentPos.y - y,  };  let mag = Math.sqrt(Math.pow(v.x, 2)   Math.pow(v.y, 2));  let normalize = { x: v.x / mag, y: v.y / mag };  this.acc.x -= normalize.x;  this.acc.y -= normalize.y;  this.velocity.x  = this.acc.x / 50;  this.velocity.y  = this.acc.y / 50;  this.currentPos.x  = this.velocity.x;  this.currentPos.y  = this.velocity.y;   this.acc.x = 0;  this.acc.y = 0;   if (this.currentPos.x gt;= width - this.beeWidth) {  this.currentPos.x = width - this.beeWidth;  this.velocity.x = 0;  }  if (this.currentPos.x lt;= 0) {  this.currentPos.x = 0;  this.velocity.x = 0;  }  if (this.currentPos.y gt;= height - this.beeHeight) {  this.currentPos.y = height - this.beeHeight;  this.velocity.y = 0;  }  if (this.currentPos.y lt;= 0) {  this.currentPos.y = 0;  this.velocity.y = 0;  }  this.createBee(this.currentPos.x, this.currentPos.y);  ctx.beginPath();  ctx.arc(this.direction.x, this.direction.y, 10, 0, 2 * Math.PI);  ctx.stroke();  };   this.move = () =gt; {  let mouseV = {  x: this.currentPos.x - this.mouse.x,  y: this.currentPos.y - this.mouse.y,  };  let mouseMag = Math.sqrt(Math.pow(mouseV.x, 2)   Math.pow(mouseV.y, 2));   if (mouseMag lt; 200) {  goTo(this.mouse.x, this.mouse.y);  } else {  let dirV = {  x: this.currentPos.x - this.direction.x,  y: this.currentPos.y - this.direction.y,  };  let dirMag = Math.sqrt(Math.pow(dirV.x, 2)   Math.pow(dirV.y, 2));  if (dirMag lt;= 100) {  this.direction = {  x: Math.random() * width,  y: Math.random() * height,  };  }  goTo(this.direction.x, this.direction.y);  }  }; }  export default Bee;  

И это в одном файле javascript, если кто-нибудь захочет его протестировать, просто добавьте какое-нибудь изображение.src по вашему выбору

 if (canvas.getContext) {  const ctx = canvas.getContext("2d");  canvas.width = window.innerWidth;  canvas.height = window.innerHeight;  function Bee(ctx, width, height) {  this.currentPos = { x: width / 5, y: height / 3 };  this.beeWidth = 32;  this.beeHeight = 32;  this.velocity = { x: 0.1, y: 0 };  this.acc = { x: 0, y: 0 };  this.direction = { x: width / 2, y: height / 2 };  this.mouse = { x: 0, y: 0 };   this.createBee = (x, y) =gt; {  // ctx.clearRect(0, 0, width, height, this.bee);  // ctx.beginPath();  // ctx.arc(x, y, 10, 0, 2 * Math.PI);  // ctx.stroke();   const img = new Image();  img.src = "./bee.png";  const imgLeft = new Image();  imgLeft.src = "./bee-left.png";  if (this.velocity.x gt;= 0) {  img.onload = () =gt; {  ctx.clearRect(0, 0, width, height, this.bee);  ctx.drawImage(img, x, y, this.beeHeight, this.beeHeight);  };  }  if (this.velocity.x lt; 0) {  imgLeft.onload = () =gt; {  ctx.clearRect(0, 0, width, height, this.bee);  ctx.drawImage(imgLeft, x, y, this.beeHeight, this.beeHeight);  };  }  };   window.addEventListener("mousemove", (e) =gt; {  this.mouse = { x: e.clientX, y: e.clientY };    });  canvas.addEventListener("mouseenter", (e) =gt; {  console.log(e, "enter");  // e.stopPropagation()  });  canvas.addEventListener("mouseleave", (e) =gt; {  console.log(e, "left");  // e.stopPropagation()  });   let goTo = (x, y) =gt; {  let v = {  x: this.currentPos.x - x,  y: this.currentPos.y - y,  };  let mag = Math.sqrt(Math.pow(v.x, 2)   Math.pow(v.y, 2));  let normalize = { x: v.x / mag, y: v.y / mag };  this.acc.x -= normalize.x;  this.acc.y -= normalize.y;  this.velocity.x  = this.acc.x / 50;  this.velocity.y  = this.acc.y / 50;  this.currentPos.x  = this.velocity.x;  this.currentPos.y  = this.velocity.y;    this.acc.x = 0;  this.acc.y = 0;    if (this.currentPos.x gt;= width - this.beeWidth) {  this.currentPos.x = width - this.beeWidth;  this.velocity.x = 0;  }  if (this.currentPos.x lt;= 0) {  this.currentPos.x = 0;  this.velocity.x = 0;  }  if (this.currentPos.y gt;= height - this.beeHeight) {  this.currentPos.y = height - this.beeHeight;  this.velocity.y = 0;  }  if (this.currentPos.y lt;= 0) {  this.currentPos.y = 0;  this.velocity.y = 0;  }  this.createBee(this.currentPos.x, this.currentPos.y);  ctx.beginPath();  ctx.arc(this.direction.x, this.direction.y, 10, 0, 2 * Math.PI);  ctx.stroke();  };   this.move = () =gt; {  let mouseV = {  x: this.currentPos.x - this.mouse.x,  y: this.currentPos.y - this.mouse.y,  };  let mouseMag = Math.sqrt(Math.pow(mouseV.x, 2)   Math.pow(mouseV.y, 2));    if (mouseMag lt; 200) {  goTo(this.mouse.x, this.mouse.y);  } else {  let dirV = {  x: this.currentPos.x - this.direction.x,  y: this.currentPos.y - this.direction.y,  };  let dirMag = Math.sqrt(Math.pow(dirV.x, 2)   Math.pow(dirV.y, 2));  if (dirMag lt;= 100) {  this.direction = {  x: Math.random() * width,  y: Math.random() * height,  };  }  goTo(this.direction.x, this.direction.y);  }  };  }  const bee = new Bee(ctx, 700, 700);  // Game loop  let fps = 60;  let now;  let then = Date.now();  let delta;   const gameLoop = () =gt; {  let interval = 1000 / fps;  window.requestAnimationFrame(gameLoop);   now = Date.now();  delta = now - then;   if (delta gt; interval) {  then = now - (delta % interval);   bee.move();  }  };  window.requestAnimationFrame(gameLoop);  //Game loop end }  

Комментарии:

1. Я не знаю, должно ли это быть так или нет, но когда я консолю.log(холст) внутри App.js это утешает. бревна тоже в два раза больше.

Ответ №1:

Моя первая гипотеза, если произойдет что-то подобное: проверьте, добавили ли вы слушателей дважды или более. Также проверьте, сколько раз вы создаете Bee (добавьте console.log в начале Bee конструктора).

Я думаю, что если прослушиватель событий был вызван дважды, то он каким-то образом был добавлен дважды.

Комментарии:

1. Нет, я добавляю прослушиватели событий только один раз внутри конструктора Bee и если я консолирую. войдя в журнал, я вижу, что он создается только один раз.

Ответ №2:

Я действительно сбит с толку, почему это было проблемой, но как только я изменился Canvas.js файл на это

 const Canvas = ({ width, height }) =gt; {  const [canvas, setCanvas] = useState();  const [context, setContext] = useState();  const [bee, setBee] = useState();   const canvasCallback = useCallback((node) =gt; {  if (node) {  setCanvas(node);  }  }, []);   const runCanvas = () =gt; {  const bee = new Bee(context, canvas.width, canvas.height, canvas)  setBee(bee);    // Game loop  let fps = 60;  let now;  let then = performance.now();  let delta;    const gameLoop = () =gt; {  let interval = 1000 / fps;      now = performance.now();  delta = now - then;    if (delta gt; interval) {  then = now - (delta % interval);  // context.clearRect(0, 0, canvas.width, canvas.height);  bee.move();  }  window.requestAnimationFrame(gameLoop);  };  window.requestAnimationFrame(gameLoop);  //Game loop end }   useEffect(() =gt; {  if (canvas) {  setContext(canvas.getContext("2d"));  canvas.width = width;  canvas.height = height;  }  }, [canvas]);   useEffect(() =gt; {  if(context){  runCanvas()  }  }, [context])     return lt;canvas id="canvas" ref={canvasCallback} /gt;; };  

.Он начал работать должным образом. Если бы кто-нибудь мог объяснить причину, я бы дал правильный ответ.