#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;; };
.Он начал работать должным образом. Если бы кто-нибудь мог объяснить причину, я бы дал правильный ответ.