canvas-backend/src/components/CanvasSetting.jsx

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;