在这个组件中,我在画布中的图像上绘制文本。我想跟踪所有的文本并存储它们的一些属性,但是当我将它们保存在一个状态中时,它们不会在UI中更新,而是在dev工具中更新。但是,当数组长度超过一个时,它只更新UI上的一个项,而只更新一个项。如果我的状态数组是1,2。在UI中,它只显示1。但是在react开发工具中,它是可以的。但是,如果我将文本存储在一个全局数组中,它就能正常工作。
组件
import { useRef, useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import "./styles/Generator.css";
//Why this one Work but not state
let textArr = [];
let startX, startY;
export default function Generator() {
//const [textArr,setTextArr]=useState([]);
const [text, setText] = useState("");
const [isClear, setIsClear] = useState(false);
const [image, setImage] = useState("");
const [offsets, setOffsets] = useState({
selectedText: -1,
offsetX: 0,
offsetY: 0,
});
const { state } = useLocation();
const { item } = state;
const canvasRef = useRef(null);
let contextRef = useRef(null);
useEffect(() => {
const canvas = canvasRef.current;
setOffsets({
...offsets,
offsetX: canvasRef.current.offsetLeft,
offsetY: canvasRef.current.offsetTop,
});
const context = canvas.getContext("2d");
contextRef.current = context;
//Our first draw
let asp = item.width / item.height;
canvas.width = 400;
canvas.height = 400 / asp;
const meme = new Image();
meme.setAttribute("crossOrigin", "anonymous");
meme.src = item.url;
meme.onload = () => {
context.drawImage(meme, 0, 0, canvas.width, canvas.height);
setImage(meme);
};
}, [isClear]);
const addText = () => {
if (text.length < 1) return;
var y = textArr.length * 20 + 20;
// get the text from the input element
var t = {
text: text,
x: 50,
y: y + 70,
};
// calc the size of this text for hit-testing purposes
contextRef.current.font = "20px arial";
t.width = contextRef.current.measureText(t.text).width;
t.height = 20;
//setState([...textArr,t])
textArr.push(t);
draw();
setText("");
};
const handleMouseDown = (e) => {
e.preventDefault();
startX = parseInt(e.clientX - offsets.offsetX);
startY = parseInt(e.clientY - offsets.offsetY);
textArr.map((i, index) => {
if (textHittest(startX, startY, index)) {
return setOffsets({ ...offsets, selectedText: index });
}
});
};
const handleMouseMove = (e) => {
if (offsets.selectedText < 0) {
return;
}
e.preventDefault();
let mouseX = parseInt(e.pageX - offsets.offsetX);
let mouseY = parseInt(e.pageY - offsets.offsetY);
var text = textArr[offsets.selectedText];
text.y += dy;
startX = mouseX;
draw();
};
const handleMouseUp = (e) => {
e.preventDefault();
setOffsets({ ...offsets, selectedText: -1 });
};
function textHittest(x, y, textIndex) {
var text = textArr[textIndex];
const isTrue =
x >= text.x &&
x <= text.x + text.width &&
y >= text.y - text.height &&
y <= text.y;
return isTrue;
}
function handleMouseOut(e) {
e.preventDefault();
setOffsets({ ...offsets, selectedText: -1 });
}
const draw = () => {
contextRef.current.drawImage(
image,
0,
0,
canvasRef.current.width,
canvasRef.current.height
);
textArr.length > 0 &&
textArr.map((i) => {
contextRef.current.fillText(i.text, i.x, i.y);
return;
});
};
const download = () => {
let img = canvasRef.current
.toDataURL("image/png", 1.0)
.replace("image/png", "image/octet-stream");
var link = document.createElement("a");
link.download = "my-image.png";
link.href = img;
link.click();
};
return (
<div className="generator">
<div className="generator__container">
<div className="generator__header">
<h1 className="home__title">Meme-Generator</h1>
<a href="/" className="home__link-top">
back
</a>
</div>
<div className="generator__content">
<div className="generator__left">
<canvas
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseOut={handleMouseOut}
ref={canvasRef}
className="canvas"
/>
<div className="generator__btn-container">
<button onClick={download} className="generator__btn">
Download
</button>
</div>
</div>
<div className="generator__right">
<h3 className="generator__sub-title">Add Text to Meme</h3>
<label className="generator__label" htmlFor="text1">
Enter Text
</label>
<input
value={text}
className="generator__input"
type="text"
placeholder="Enter text.."
onChange={(e) => setText(e.target.value)}
/>
<button
onClick={addText}
className="generator__btn generator__btn--text"
>
Add Text
</button>
</div>
</div>
</div>
</div>
);
}
State
//setting array like this work
let textArr = [];
let startX, startY;
export default function Generator() {
//setting state like this doesn't work
//const [textArr,setTextArr]=useState([]);
return (....)
}
添加文本函数
const addText = () => {
if (text.length < 1) return;
var y = textArr.length * 20 + 20;
var t = {
text: text,
x: 50,
y: y + 70,
};
contextRef.current.font = "20px arial";
t.width = contextRef.current.measureText(t.text).width;
t.height = 20;
//state updating like this don't effect on UI but it update on devtools
//setState([...textArr,t])
//This like work just fine
textArr.push(t);
draw();
setText("");
};
发布于 2022-06-23 19:45:56
状态更新是异步的,但是在设置了新状态(状态尚未更新时),就调用了“绘制”函数。
如果您希望每次更新textArr时绘图函数都能工作,则需要使用效果钩子。应该像这样工作:
useEffect(() => {
draw();
}, [textArr]);
如果绘图函数依赖于其他值,那么也应该将它们包含到依赖项数组中(就在textArr之后)。
https://stackoverflow.com/questions/72738718
复制相似问题