我有一个用于创建动画的下一个应用程序。这些帧被保存到时间线上,用户可以在其中滚动旧帧。我试图添加一个简单的功能,通过单击附加按钮来删除其中一个框架,但即使语法与任何其他函数相同并且没有错误,该函数也不会被调用。这是组件、父组件和 package.json:
import React from "react";
import { useCanvas } from "./CanvasContext";
import { settings } from "./Signals";
import ReusableLayer from "../components/utility/ResuableLayer";
import { timeline } from "./Signals";
import { v4 as uuidv4 } from "uuid";
import IndexLabel from "../components/utility/IndexLabel";
import JSZip from "jszip";
import { saveAs } from "file-saver";
const Timeline: React.FC = () => {
const handleDownloadImages = () => {
const zip = new JSZip();
// Add each image to the zip file
timeline.value.forEach((imageDataURL, index) => {
const base64Data = imageDataURL.split(",")[1];
zip.file(`image_${index + 1}.png`, base64Data, { base64: true });
});
// Generate the zip file and trigger download
zip.generateAsync({ type: "blob" }).then((blob) => {
saveAs(blob, "timeline_images.zip");
});
};
const handleDeleteImage = (index: number, e: React.MouseEvent) => {
e.stopPropagation();
console.log("click");
// Create a copy of the timeline array
const updatedImages = [...timeline.value];
// Modify the copy
updatedImages.splice(index, 1);
// Update the timeline array with the modified copy
timeline.value = updatedImages;
console.log("splicing");
};
const timelineImageSize = 400
return (
<div className="relative w-full overflow-x-auto bg-gray-200">
<div
className="flex h-auto overflow-y-hidden flex-row-reverse gap-1 py-1"
style={{
width:
(timelineImageSize) * timeline.value.length - 0
// settings.value.canvasSize.x,
}}
>
{timeline.value.map((imageDataURL, index) => (
<div
className="relative border-2"
key={uuidv4()}
style={{
border: "black 1px solid",
width: (timelineImageSize),
height: (timelineImageSize),
}}
>
<button
onClick={(e) => handleDeleteImage(index, e)}
className="z-50 top-0 right-0 p-2 bg-red-500 text-white rounded-md cursor-pointer"
>
Delete
</button>
<button
onClick={(e) => handleDeleteImage(index, e)}
className=" "
>
Delete
</button>
<img
src={imageDataURL}
width={timelineImageSize} // Render two times smaller
height={timelineImageSize} // Render two times smaller
alt={`Image ${index}`}
/>
{/* <IndexLabel
label={(index + 1).toString()}
customClasses="absolute top-0 left-0 text-lg"
/> */}
</div>
))}
</div>
{timeline.value.length > 0 && (
<button
// onClick={handleDownloadImages}
className="mt-4 p-2 bg-blue-500 text-white rounded-md cursor-pointer absolute bottom-0 right-0"
>
Download Images
</button>
)}
<button
onClick={
(e) => handleDeleteImage(0, e )
}
className="z-10 top-0 right-0 p-2 bg-red-500 text-white rounded-md cursor-pointer"
>
Delete
</button>
</div>
);
};
parent component:
const Page: React.FC = () => {
const { canvasRef, frontlineCanvasRef, markerCanvasRef,backgroundCanvasRef } =
useCanvas();
const { GlobalData, updateGlobalData } = useGlobalValue();
const [mouseDownTimeStamp, setMouseDownTimeStamp] = useState<number | null>(
null
);
const [elapsedTime, setElapsedTime] = useState<number>(0);
const handleMouseDown = (e: MouseEvent) => {
if (e.button === 2) {
e.preventDefault();
}
setMouseDownTimeStamp(Date.now());
};
const handleMouseUp = (e: MouseEvent) => {
e.preventDefault();
// Using requestAnimationFrame to delay the execution until the next frame
requestAnimationFrame(() => {
setElapsedTime(0);
setMouseDownTimeStamp(null);
});
};
useEffect(() => {
// Add event listeners when the component mounts
document.addEventListener("mousedown", handleMouseDown);
document.addEventListener("mouseup", handleMouseUp, true);
// Remove event listeners when the component unmounts
return () => {
document.removeEventListener("mousedown", handleMouseDown);
document.removeEventListener("mouseup", handleMouseUp, true);
};
}, []);
useEffect(() => {
// Update elapsed time while the button is pressed
if (mouseDownTimeStamp !== null) {
const intervalId = setInterval(() => {
setElapsedTime((prevElapsedTime) => prevElapsedTime + 100);
updateGlobalData("mouseDownTime", elapsedTime + 100);
}, 100);
return () => clearInterval(intervalId);
}
}, [mouseDownTimeStamp, elapsedTime]);
useEffect(() => {
updateGlobalData("mouseDownTime", elapsedTime);
}, [elapsedTime]);
return (
< >
<CanvasSettings />
<CanvasEditor />
<Timeline />
</>
);
};
package.json
{
"name": "mapping-software",
"version": "0.1.0",
"private": true,
"configurations": [
{
"name": "Next: Chrome",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}",
"sourceMapPathOverrides": {
"webpack:///./*": "${webRoot}/*"
}
}
],
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.14.18",
"@mui/system": "^5.14.18",
"@preact/signals": "^1.2.1",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",
"jszip": "^3.10.1",
"next": "14.0.0",
"react": "^18",
"react-dom": "^18",
"react-dropzone": "^14.2.3",
"uuidv4": "^6.2.13"
},
"devDependencies": {
"@types/file-saver": "^2.0.7",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/tinycolor2": "^1.4.6",
"@wixc3/react-board": "^2.3.0",
"autoprefixer": "^10",
"eslint": "^8",
"eslint-config-next": "14.0.0",
"postcss": "^8",
"postcss-preset-env": "^9.2.0",
"tailwindcss": "^3.3.5",
"typescript": "^5"
}
}
我描述的行为仅发生在时间线内。 value.map 块。外面的每个按钮都有效。此外,当我注释掉这个块时,它也开始工作:
useEffect(() => {
// Add event listeners when the component mounts
document.addEventListener("mousedown", handleMouseDown);
document.addEventListener("mouseup", handleMouseUp, true);
// Remove event listeners when the component unmounts
return () => {
document.removeEventListener("mousedown", handleMouseDown);
document.removeEventListener("mouseup", handleMouseUp, true);
};
}, []);
出于某种原因,当我删除 uuidv4 作为 keyprop 时,删除按钮开始工作
{timeline.value.map((imageDataURL, index) => (
<div
className="relative border-2"
key={index} // changed uuidv4() for index
style={{
border: "black 1px solid",
width: (timelineImageSize),
height: (timelineImageSize),
}}
>
<button
onClick={(e) => handleDeleteImage(index, e)}
className="z-50 absolute top-0 right-0 p-2 bg-red-500 text-white rounded-md cursor-pointer"
>
Delete
</button>
<img
src={imageDataURL}
width={timelineImageSize}
height={timelineImageSize}
alt={`Image ${index}`}
/>
<IndexLabel
label={(index + 1).toString()}
customClasses="absolute top-0 left-0 text-lg"
/>
</div>
))}