Reputation: 4807
I am using the following code to dynamically create canvas height. But even though there is onLoad
, console log for canvas height is 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);
The sandbox link is here
Upvotes: 0
Views: 844
Reputation: 15688
The problem is that if you set <img hidden/>
then the image dimensions would simply be 0 height, 0 width.
You can still hide the image without using the hidden prop. Use a style that sets visibility to hidden, the image dimensions will be passed correctly. If you want to push img
behind the canvas so that it will never be clicked. Use the following:
<img
ref={image}
src={props.background}
onLoad={handleOnLoad}
style={{ visibility: "hidden", position: "absolute", top: "0", zIndex: "-1" }}
alt=""
/>
See sandbox: https://codesandbox.io/s/affectionate-wildflower-jov21
Additionally, you can workaround the synchronous
behavior of your handleLoad()
function. State-updating functions do not wait for other logic to complete before they execute. Before setCanX
has time to complete, setxLoc
is already running and in that case canX
is still undefined
. Which is why we get NaN
when we try to divide undefined
by 2. They do not wait like an asynchronous
manner.
You should just pass in event.target.offsetStuff
to the setter functions directly and it will work
const handleOnLoad = e => {
const { offsetHeight, offsetWidth } = e.target
setCanX(offsetWidth);
setCanY(offsetHeight);
setxLoc(offsetWidth / 2);
setyLocTop(offsetHeight * 0.87);
setyLocBottom(offsetHeight * 0.13);
};
Upvotes: 3
Reputation: 1653
It seems like there are two onload
functions for the image, one in the useEffect()
and one in the property of the image. You can actually combine them and it'll work well. Here's the sandbox 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);
Upvotes: 0