301 lines
9.4 KiB
JavaScript
301 lines
9.4 KiB
JavaScript
import { useContext, useRef, useState } from "react";
|
|
import CanvasContext from "./Context/canvasContext/CanvasContext";
|
|
import { Card, CardTitle } from "./ui/card";
|
|
import { Button } from "./ui/button";
|
|
import { Trash2, UploadIcon, X } from "lucide-react";
|
|
import { Separator } from "./ui/separator";
|
|
import ColorComponent from "./ColorComponent";
|
|
import { Label } from "./ui/label";
|
|
import { Input } from "./ui/input";
|
|
import { Slider } from "./ui/slider";
|
|
import { fabric } from "fabric";
|
|
import OpenContext from "./Context/openContext/OpenContext";
|
|
import { ScrollArea } from "./ui/scroll-area";
|
|
import RndComponent from "./Layouts/RndComponent";
|
|
|
|
const CanvasSetting = () => {
|
|
const { canvas, canvasHeight, canvasWidth, screenWidth } =
|
|
useContext(CanvasContext);
|
|
const { setOpenSetting } = useContext(OpenContext);
|
|
const bgImgRef = useRef(null);
|
|
|
|
const [canvasSettings, setCanvasSettings] = useState({
|
|
width: canvas?.width,
|
|
height: canvas?.height,
|
|
perPixelTargetFind: true, // Enable per-pixel detection globally
|
|
targetFindTolerance: 4, // Adjust for leniency in pixel-perfect detection
|
|
});
|
|
|
|
const handleChange = (key, value) => {
|
|
setCanvasSettings((prevSettings) => ({
|
|
...prevSettings,
|
|
[key]: value,
|
|
}));
|
|
|
|
// Update canvas dimensions
|
|
if (key === "width") {
|
|
canvas.setWidth(value); // Update canvas width
|
|
} else if (key === "height") {
|
|
canvas.setHeight(value); // Update canvas height
|
|
}
|
|
|
|
// Adjust the background image to fit the updated canvas
|
|
const bgImage = canvas.backgroundImage;
|
|
if (bgImage) {
|
|
const canvasWidth = canvas.width;
|
|
const canvasHeight = canvas.height;
|
|
|
|
// Calculate scaling factors for width and height
|
|
const scaleX = canvasWidth / bgImage.width;
|
|
const scaleY = canvasHeight / bgImage.height;
|
|
|
|
// Choose the larger scale to cover the entire canvas
|
|
const scale = Math.max(scaleX, scaleY);
|
|
|
|
// Apply the scale and center the image
|
|
bgImage.scaleX = scale;
|
|
bgImage.scaleY = scale;
|
|
bgImage.left = 0; // Align left
|
|
bgImage.top = 0; // Align top
|
|
|
|
// Mark the background image as needing an update
|
|
canvas.setBackgroundImage(bgImage, canvas.renderAll.bind(canvas));
|
|
} else {
|
|
canvas.renderAll(); // Render if no background image
|
|
}
|
|
};
|
|
|
|
const setBackgroundImage = (e) => {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
const blobUrl = URL.createObjectURL(file);
|
|
|
|
// Create an image element
|
|
const imgElement = new Image();
|
|
imgElement.src = blobUrl;
|
|
|
|
imgElement.onload = () => {
|
|
// Create a fabric.Image instance
|
|
const img = new fabric.Image(imgElement, {
|
|
scaleX: canvas.width / imgElement.width,
|
|
scaleY: canvas.height / imgElement.height,
|
|
});
|
|
|
|
// Set the background image on the canvas
|
|
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
|
|
|
|
// Revoke the object URL to free memory
|
|
URL.revokeObjectURL(blobUrl);
|
|
};
|
|
|
|
imgElement.onerror = () => {
|
|
console.error("Failed to load the image.");
|
|
URL.revokeObjectURL(blobUrl);
|
|
};
|
|
}
|
|
};
|
|
|
|
const setBackgroundOverlayImage = (e) => {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
const blobUrl = URL.createObjectURL(file);
|
|
|
|
// Create an image element
|
|
const imgElement = new Image();
|
|
imgElement.src = blobUrl;
|
|
|
|
imgElement.onload = () => {
|
|
const img = new fabric.Image(imgElement, {
|
|
scaleX: canvas.width / imgElement.width,
|
|
scaleY: canvas.height / imgElement.height,
|
|
});
|
|
|
|
// Use setOverlayImage method to add the image as an overlay
|
|
canvas.setOverlayImage(img, () => {
|
|
canvas.renderAll();
|
|
});
|
|
|
|
// Revoke the object URL after image is loaded and set
|
|
URL.revokeObjectURL(blobUrl);
|
|
};
|
|
}
|
|
};
|
|
|
|
const adjustBackgroundOpacity = (value) => {
|
|
if (canvas) {
|
|
if (canvas.backgroundImage) {
|
|
// Update the opacity of the background image if it exists
|
|
canvas.backgroundImage.set("opacity", value);
|
|
} else if (canvas.overlayImage) {
|
|
// Update the opacity of the overlay image if it exists
|
|
canvas.overlayImage.set("opacity", value);
|
|
} else {
|
|
console.error("No background or overlay image found on the canvas.");
|
|
return;
|
|
}
|
|
|
|
// Re-render the canvas to apply changes
|
|
canvas.renderAll();
|
|
} else {
|
|
console.error("Canvas is not initialized.");
|
|
}
|
|
};
|
|
|
|
const removeBackgroundImage = () => {
|
|
if (canvas) {
|
|
canvas.backgroundImage = null;
|
|
canvas.renderAll();
|
|
}
|
|
if (bgImgRef.current) {
|
|
bgImgRef.current.value = "";
|
|
}
|
|
};
|
|
|
|
const removeBackgroundOverlayImage = () => {
|
|
if (canvas) {
|
|
canvas.overlayImage = null;
|
|
canvas.renderAll();
|
|
}
|
|
};
|
|
|
|
const rndValue = {
|
|
valueX: 0,
|
|
valueY: 20,
|
|
width: 250,
|
|
height: 0,
|
|
minWidth: 250,
|
|
maxWidth: 300,
|
|
minHeight: 0,
|
|
maxHeight: 400,
|
|
bound: "parent",
|
|
};
|
|
const content = () => {
|
|
return (
|
|
<Card className="xl:p-0 lg:p-0 md:p-0 p-2 border-none shadow-none">
|
|
<CardTitle className="flex items-center flex-wrap justify-between gap-1 xl:hidden lg:hidden md:hidden">
|
|
Canvas Setting{" "}
|
|
<Button
|
|
className="rnd-escape"
|
|
variant="secondary"
|
|
onClick={() => setOpenSetting(false)}
|
|
>
|
|
<X />
|
|
</Button>{" "}
|
|
</CardTitle>
|
|
<Separator className="mt-4 block xl:hidden lg:hidden md:hidden" />
|
|
<ScrollArea className="h-[400px] xl:h-fit lg:h-fit md:h-fit">
|
|
<div className="rnd-escape">
|
|
<ColorComponent />
|
|
|
|
<Separator className="mt-2" />
|
|
|
|
<div className="flex flex-col my-2 gap-2 rnd-escape">
|
|
<div>
|
|
<Label>Background:</Label>
|
|
<div className="flex items-center w-fit gap-2 flex-wrap relative">
|
|
<Button className="top-0 absolute flex items-center w-[30px]">
|
|
<UploadIcon className="cursor-pointer" />
|
|
<Input
|
|
ref={bgImgRef}
|
|
className="absolute top-0 opacity-0"
|
|
type="file"
|
|
accept="image/*"
|
|
onChange={setBackgroundImage}
|
|
/>
|
|
</Button>
|
|
|
|
<Button
|
|
variant="secondary"
|
|
className="ml-[35px]"
|
|
onClick={removeBackgroundImage}
|
|
>
|
|
<Trash2 />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<Label>Background Overlay:</Label>
|
|
<div className="flex items-center w-fit gap-2 flex-wrap relative">
|
|
<Button className="top-0 absolute flex items-center w-[30px]">
|
|
<UploadIcon className="cursor-pointer" />
|
|
<Input
|
|
className="absolute top-0 opacity-0"
|
|
type="file"
|
|
accept="image/*"
|
|
onChange={setBackgroundOverlayImage}
|
|
/>
|
|
</Button>
|
|
|
|
<Button
|
|
variant="secondary"
|
|
className="ml-[35px]"
|
|
onClick={removeBackgroundOverlayImage}
|
|
>
|
|
<Trash2 />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* opacity */}
|
|
<div className="flex flex-col gap-2 rnd-escape mt-2">
|
|
<Label>Background Opacity:</Label>
|
|
<Slider
|
|
defaultValue={[1.0]} // Default value, you can set it to 0.0 or another value
|
|
min={0.0}
|
|
max={1.0}
|
|
step={0.01} // Step size for fine control
|
|
onValueChange={(value) => {
|
|
const newOpacity = value[0]; // Extract slider value
|
|
adjustBackgroundOpacity(newOpacity); // Adjust Fabric.js background opacity
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator className="mt-4" />
|
|
|
|
{/* canvas size customization (width/height) */}
|
|
<div className="flex gap-2 my-2">
|
|
<div className="flex flex-col gap-2">
|
|
<Label>Width:</Label>
|
|
<Input
|
|
min={300}
|
|
type="number"
|
|
value={canvasSettings.width}
|
|
onChange={(e) => {
|
|
if (canvasWidth > parseInt(e.target.value)) {
|
|
handleChange("width", parseInt(e.target.value, 10));
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-2">
|
|
<Label>Height:</Label>
|
|
<Input
|
|
min={300}
|
|
type="number"
|
|
value={canvasSettings.height}
|
|
onChange={(e) => {
|
|
if (canvasHeight > parseInt(e.target.value)) {
|
|
handleChange("height", parseInt(e.target.value, 10));
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ScrollArea>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
return screenWidth <= 768 ? (
|
|
<RndComponent value={rndValue}>{content()}</RndComponent>
|
|
) : (
|
|
<div>{content()}</div>
|
|
);
|
|
};
|
|
|
|
export default CanvasSetting;
|