определение функции animate () вне области действия параметра 'img' завершается неудачно с использованием реагирования useEffect - PullRequest
0 голосов
/ 04 февраля 2020

Цель : я хочу определить свою функцию animation () вне моей ловушки React useEffect () (которая загружает изображение для анимации), потому что я хочу определить его только один раз, а не каждый раз при зависимости меняется.

Проблема : Несмотря на то, что я вызываю функцию animation () в правильной области img (в img.onload), произойдет сбой, если я объявлю функцию вне области img. Кроме того, если я передаю параметр img в определение анимации (img), он также не работает, что для меня не имеет никакого смысла.

Почему это сбивает с толку : я никогда прежде не видел сценария, где определение функции должно быть объявлено в области видимости ее параметров. Обычно требуется только вызвать в области действия параметра. Я также никогда не видел функцию, которая дает сбой при объявлении параметра в определении.

Код, который делает то, что я ожидаю (вроде)

, чтобы воспроизвести эту проблему, создать приложение create-реагировать, а затем заменить файл "/ src / Приложение. js "с этим кодом.

Этот компонент работает, потому что функция animation () определена внутри области img в хуке useEffect (), а 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;



Я не знаю, почему я должен определить animation () внутри хука useEffect. Кроме того, приложение перестает работать, если я включаю в параметр 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

Резюме : функция анимации моего приложения сильно выросла, со многими параметрами и зависимостями. Мне нужно устранять проблемы с процессором, и я хочу начать с объявления функции только один раз. Почему я не могу объявить определение функции вне области img.onload? Что такого особенного в параметре img, что он не работает, если я включаю его в определение функции? Я не знаю, как начать упрощать анимацию, если не могу переместить саму функцию.

1 Ответ

1 голос
/ 04 февраля 2020

requestAnimationFrame передает параметр DOMHighResTimeStamp в ваш обратный вызов, поэтому на второй итерации вы пытаетесь нарисовать число , а не 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 );
  }
}
...