在其参数 'img' 范围之外定义函数 animate() 失败,使用 react useEffect
defining function animate() outside its parameter 'img' scope fails, using react useEffect
目标:我想在我的 React useEffect() 挂钩(加载图像以进行动画处理)之外定义我的 animation() 函数,因为我只想定义它一次,并非每次依赖项更改时。
问题:即使我在正确的 img 范围内(在 img.onload 中)调用 animation() 函数,如果我在img 范围。此外,如果我将参数 img
传递到定义动画(img)中,它也会失败,这对我来说没有任何意义。
为什么这令人困惑:我以前从未见过需要在其参数范围内声明函数定义的情况。它通常只需要在参数范围内被调用。我也从未见过通过在定义中声明参数而失败的函数。
符合我预期的代码(有点)
要重现此问题,请创建一个 create-react-app,然后用此代码替换文件“/src/App.js”。
这个组件可以工作,因为 animation() 函数是在 useEffect() 挂钩的 img 范围内定义的,并且 img 没有作为参数传递。
(即 function animation() {};不是 function animation(img) {})。
将圆圈拖到图像上以使用应用程序
import React, { useRef, useEffect, useState } from "react";
import "./App.css";
function App() {
const [mouseCoords, setMouseCoords] = useState({ x: 10, y: 10 });
const mouseRef = useRef({
down: false
});
const canvasRef = useRef();
const animRef = useRef();
useEffect(() => {
var img = new Image();
img.onload = () => {
animation(); // || animation(img) .. invoking with img param is OK
};
img.src =
"https://cdn-images-1.medium.com/max/1600/1*g6s1lvpfArJGorALkKNhvw.png";
function animation() { // != animation(img) ..defining with img param throws error why??
var canvas = canvasRef.current;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.arc(mouseCoords.x, mouseCoords.y, 10, 0, 2 * Math.PI, false);
ctx.fill();
animRef.current = requestAnimationFrame(animation);
}
return cancelAnimationFrame(animRef.current);
}, [mouseCoords]);
function getCanvasCoord(canvas, x, y) {
var rect = canvas.getBoundingClientRect();
return {
x: Math.round((x - rect.left) * (canvas.width / rect.width)),
y: Math.round((y - rect.top) * (canvas.height / rect.height))
};
}
const handleMouseDown = e => {
mouseRef.current.down = true;
};
const handleMouseMove = e => {
var { x, y } = getCanvasCoord(canvasRef.current, e.clientX, e.clientY);
if (mouseRef.current.down) {
setMouseCoords({ x, y });
}
};
const handleMouseUp = e => {
mouseRef.current.down = false;
};
return (
<div className="App">
<canvas
ref={canvasRef}
onMouseMove={handleMouseMove}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
width={500}
height={500}
></canvas>
</div>
);
}
export default App;
我不知道为什么我必须在 useEffect 钩子中定义 animation() 。此外,如果我在 img 参数中包含应用程序失败
失败代码示例 1
useEffect(() => {
var img = new Image();
img.onload = () => {
animation(img); // animation(img) .. adding img param here is OK
};
img.src =
"https://cdn-images-1.medium.com/max/1600/1*g6s1lvpfArJGorALkKNhvw.png";
function animation(img) { // animation(img) ..adding parameter to definition throws error why??
var canvas = canvasRef.current;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.arc(mouseCoords.x, mouseCoords.y, 10, 0, 2 * Math.PI, false);
ctx.fill();
animRef.current = requestAnimationFrame(animation);
}
失败代码示例 2
另一种失败的方法是尝试在 useEffect 挂钩之外声明动画函数。
useEffect(() => {
var img = new Image();
img.onload = () => {
animation(img);
};
img.src =
"https://cdn-images-1.medium.com/max/1600/1*g6s1lvpfArJGorALkKNhvw.png";
return cancelAnimationFrame(animRef.current);
}, [mouseCoords]);
function animation(img) {
var canvas = canvasRef.current;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.arc(mouseCoords.x, mouseCoords.y, 10, 0, 2 * Math.PI, false);
ctx.fill();
animRef.current = requestAnimationFrame(animation);
}
TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
animation
src/App.js:27
24 | var canvas = canvasRef.current;
25 | var ctx = canvas.getContext("2d");
26 | ctx.clearRect(0, 0, canvas.width, canvas.height);
> 27 | ctx.drawImage(img, 0, 0);
| ^ 28 | ctx.beginPath();
29 | ctx.arc(mouseCoords.x, mouseCoords.y, 10, 0, 2 * Math.PI, false);
30 | ctx.fill();
View compiled
总结:我的app动画功能增长了很多,参数和依赖也很多。我需要解决 CPU 问题,我想首先只声明一次函数。为什么我不能在 img.onload 范围之外声明函数定义? img 参数有什么特别之处,如果我将它包含在函数定义中它会失败?如果我不能移动函数本身,我不知道如何开始简化我的动画负担。
requestAnimationFrame
does pass a DOMHighResTimeStamp 参数给你的回调,所以在第二次迭代中,你试图绘制一个 number,而不是 Image
.
const img = new Image();
animation( img );
function animation( img ) {
console.log( img ); // first round: <img>, second round: a number
if( img instanceof HTMLImageElement ) {
requestAnimationFrame( animation );
}
}
目标:我想在我的 React useEffect() 挂钩(加载图像以进行动画处理)之外定义我的 animation() 函数,因为我只想定义它一次,并非每次依赖项更改时。
问题:即使我在正确的 img 范围内(在 img.onload 中)调用 animation() 函数,如果我在img 范围。此外,如果我将参数 img
传递到定义动画(img)中,它也会失败,这对我来说没有任何意义。
为什么这令人困惑:我以前从未见过需要在其参数范围内声明函数定义的情况。它通常只需要在参数范围内被调用。我也从未见过通过在定义中声明参数而失败的函数。
符合我预期的代码(有点)
要重现此问题,请创建一个 create-react-app,然后用此代码替换文件“/src/App.js”。
这个组件可以工作,因为 animation() 函数是在 useEffect() 挂钩的 img 范围内定义的,并且 img 没有作为参数传递。 (即 function animation() {};不是 function animation(img) {})。
将圆圈拖到图像上以使用应用程序
import React, { useRef, useEffect, useState } from "react";
import "./App.css";
function App() {
const [mouseCoords, setMouseCoords] = useState({ x: 10, y: 10 });
const mouseRef = useRef({
down: false
});
const canvasRef = useRef();
const animRef = useRef();
useEffect(() => {
var img = new Image();
img.onload = () => {
animation(); // || animation(img) .. invoking with img param is OK
};
img.src =
"https://cdn-images-1.medium.com/max/1600/1*g6s1lvpfArJGorALkKNhvw.png";
function animation() { // != animation(img) ..defining with img param throws error why??
var canvas = canvasRef.current;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.arc(mouseCoords.x, mouseCoords.y, 10, 0, 2 * Math.PI, false);
ctx.fill();
animRef.current = requestAnimationFrame(animation);
}
return cancelAnimationFrame(animRef.current);
}, [mouseCoords]);
function getCanvasCoord(canvas, x, y) {
var rect = canvas.getBoundingClientRect();
return {
x: Math.round((x - rect.left) * (canvas.width / rect.width)),
y: Math.round((y - rect.top) * (canvas.height / rect.height))
};
}
const handleMouseDown = e => {
mouseRef.current.down = true;
};
const handleMouseMove = e => {
var { x, y } = getCanvasCoord(canvasRef.current, e.clientX, e.clientY);
if (mouseRef.current.down) {
setMouseCoords({ x, y });
}
};
const handleMouseUp = e => {
mouseRef.current.down = false;
};
return (
<div className="App">
<canvas
ref={canvasRef}
onMouseMove={handleMouseMove}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
width={500}
height={500}
></canvas>
</div>
);
}
export default App;
我不知道为什么我必须在 useEffect 钩子中定义 animation() 。此外,如果我在 img 参数中包含应用程序失败
失败代码示例 1
useEffect(() => {
var img = new Image();
img.onload = () => {
animation(img); // animation(img) .. adding img param here is OK
};
img.src =
"https://cdn-images-1.medium.com/max/1600/1*g6s1lvpfArJGorALkKNhvw.png";
function animation(img) { // animation(img) ..adding parameter to definition throws error why??
var canvas = canvasRef.current;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.arc(mouseCoords.x, mouseCoords.y, 10, 0, 2 * Math.PI, false);
ctx.fill();
animRef.current = requestAnimationFrame(animation);
}
失败代码示例 2
另一种失败的方法是尝试在 useEffect 挂钩之外声明动画函数。
useEffect(() => {
var img = new Image();
img.onload = () => {
animation(img);
};
img.src =
"https://cdn-images-1.medium.com/max/1600/1*g6s1lvpfArJGorALkKNhvw.png";
return cancelAnimationFrame(animRef.current);
}, [mouseCoords]);
function animation(img) {
var canvas = canvasRef.current;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.arc(mouseCoords.x, mouseCoords.y, 10, 0, 2 * Math.PI, false);
ctx.fill();
animRef.current = requestAnimationFrame(animation);
}
TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
animation
src/App.js:27
24 | var canvas = canvasRef.current;
25 | var ctx = canvas.getContext("2d");
26 | ctx.clearRect(0, 0, canvas.width, canvas.height);
> 27 | ctx.drawImage(img, 0, 0);
| ^ 28 | ctx.beginPath();
29 | ctx.arc(mouseCoords.x, mouseCoords.y, 10, 0, 2 * Math.PI, false);
30 | ctx.fill();
View compiled
总结:我的app动画功能增长了很多,参数和依赖也很多。我需要解决 CPU 问题,我想首先只声明一次函数。为什么我不能在 img.onload 范围之外声明函数定义? img 参数有什么特别之处,如果我将它包含在函数定义中它会失败?如果我不能移动函数本身,我不知道如何开始简化我的动画负担。
requestAnimationFrame
does pass a DOMHighResTimeStamp 参数给你的回调,所以在第二次迭代中,你试图绘制一个 number,而不是 Image
.
const img = new Image();
animation( img );
function animation( img ) {
console.log( img ); // first round: <img>, second round: a number
if( img instanceof HTMLImageElement ) {
requestAnimationFrame( animation );
}
}