动态创建画布高度以显示图像

时间:2019-07-03 20:05:56

标签: reactjs

我正在使用以下代码动态创建画布高度。但是,即使有onLoad,画布高度的控制台日志也为0。

index.js

import React, {useRef, useState, useEffect} from "react";
import ReactDOM from "react-dom";
import background from "./background.png";

const Canvas = (props) => {
  const canvas = useRef(null);
  const image = useRef(null);  
  const [xLoc, setxLoc] = useState()
  const [yLocTop, setyLocTop] = useState()
  const [yLocBottom, setyLocBottom] = useState()
  const [canX, setCanX] = useState()
  const [canY, setCanY] = useState()

  useEffect(() => {
    const ctx = canvas.current.getContext("2d");
    image.current.onload = () => {
      ctx.drawImage(image.current, 0, 0);            
      ctx.font = "20px Courier";
      ctx.textAlign = "center";  
      ctx.fillText(props.textTop, xLoc, yLocTop);
      ctx.textAlign = "center";
      ctx.fillText(props.textBottom, xLoc, yLocBottom);
    };
  });

  useEffect(() => {
    const ctx = canvas.current.getContext("2d");
    ctx.drawImage(image.current, 0, 0);    
    ctx.font = "20px Courier";
    ctx.textAlign = "center";
    ctx.fillText(props.textTop, xLoc, yLocTop);
    ctx.textAlign = "center";
    ctx.fillText(props.textBottom, xLoc, yLocBottom);
  });

  const handleOnLoad = e => {    
    console.log(e.target.offsetHeight)
    setCanX(e.target.offsetWidth)
    setCanY(e.target.offsetHeight)
    setxLoc(canX / 2);
    setyLocTop(canY * 0.87);
    setyLocBottom(canY * 0.13);
  };

  return (
    <div>
      {console.log(canX)}
      {/* <canvas ref={canvas} width={canX || 0} height={canY || 0} /> */}
      <canvas ref={canvas} width="270" height="80" />
      <img 
        ref={image} 
        src={props.background} 
        onLoad={handleOnLoad} 
        hidden/>
    </div>
  );
};


function App() {  
  return (
    <div className="App">      
      <Canvas textTop="Top" textBottom="Bottom" background={background} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

沙盒链接为here

2 个答案:

答案 0 :(得分:3)

问题在于,如果您设置<img hidden/>,则图像尺寸将仅为0高度,0宽度。

您仍然可以隐藏图像,而无需使用隐藏的道具。使用将可见性设置为隐藏的样式,图像尺寸将正确传递。如果您想将img推到画布后面,以使其永远不会被单击。使用以下内容:

  <img
    ref={image}
    src={props.background}
    onLoad={handleOnLoad}
    style={{ visibility: "hidden", position: "absolute", top: "0", zIndex: "-1" }}
    alt=""
  />

请参阅沙箱:https://codesandbox.io/s/affectionate-wildflower-jov21

此外,您可以解决synchronous函数的handleLoad()行为。状态更新功能在执行之前不会等待其他逻辑完成。在setCanX有时间完成之前,setxLoc已经在运行,在这种情况下,canX仍然是undefined。这就是为什么当我们尝试将NaN除以2时得到undefined的原因。它们没有像asynchronous那样等待。

您应该直接将event.target.offsetStuff传递给setter函数,它将起作用

 const handleOnLoad = e => {
    const { offsetHeight, offsetWidth } = e.target

    setCanX(offsetWidth);
    setCanY(offsetHeight);
    setxLoc(offsetWidth / 2);
    setyLocTop(offsetHeight * 0.87);
    setyLocBottom(offsetHeight * 0.13);
  };

答案 1 :(得分:0)

该图像似乎有两个onload函数,一个在useEffect()中,另一个在图像的属性中。您实际上可以将它们组合在一起,并且效果很好。这是沙箱link

import React, { useRef, useState, useEffect } from "react";
import ReactDOM from "react-dom";
import background from "./background.png";

const Canvas = props => {
  const canvas = useRef(null);
  const image = useRef(null);
  const [xLoc, setxLoc] = useState();
  const [yLocTop, setyLocTop] = useState();
  const [yLocBottom, setyLocBottom] = useState();
  const [canX, setCanX] = useState();
  const [canY, setCanY] = useState();

  useEffect(() => {
    const ctx = canvas.current.getContext("2d");
    image.current.onload = () => {
      ctx.drawImage(image.current, 0, 0);
      ctx.font = "20px Courier";
      ctx.textAlign = "center";
      ctx.fillText(props.textTop, xLoc, yLocTop);
      ctx.textAlign = "center";
      ctx.fillText(props.textBottom, xLoc, yLocBottom);

      setCanX(image.current.width);
      setCanY(image.current.height);
      setxLoc(canX / 2);
      setyLocTop(canY * 0.87);
      setyLocBottom(canY * 0.13);
    };
  });

  useEffect(() => {
    const ctx = canvas.current.getContext("2d");
    ctx.drawImage(image.current, 0, 0);
    ctx.font = "20px Courier";
    ctx.textAlign = "center";
    ctx.fillText(props.textTop, xLoc, yLocTop);
    ctx.textAlign = "center";
    ctx.fillText(props.textBottom, xLoc, yLocBottom);
  });

  return (
    <div>
      <canvas ref={canvas} width={canX || 0} height={canY || 0} />
      {/* <canvas ref={canvas} width="270" height="80" /> */}
      <img ref={image} src={props.background} hidden />
    </div>
  );
};

function App() {
  return (
    <div className="App">
      <Canvas textTop="Top" textBottom="Bottom" background={background} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);