mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 13:23:14 +01:00
Create a rotate 90 degrees clockwise button for each image (#666)
* Create a rotate 90 degrees clockwise button for each image * Added more error handling and turned rotate function to async as recommended by coderabbitai
This commit is contained in:
@@ -75,6 +75,26 @@
|
||||
<p>Delete photo</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
size="icon"
|
||||
type="button"
|
||||
variant="default"
|
||||
@click.prevent="
|
||||
async () => {
|
||||
await rotateBase64Image90Deg(photo.fileBase64, index);
|
||||
}
|
||||
"
|
||||
>
|
||||
<MdiRotateClockwise />
|
||||
<div class="sr-only">Rotate photo</div>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Rotate photo</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<!-- TODO: re-enable when we have a way to set primary photos -->
|
||||
<!-- <Tooltip>
|
||||
<TooltipTrigger>
|
||||
@@ -114,6 +134,7 @@
|
||||
import MdiPackageVariant from "~icons/mdi/package-variant";
|
||||
import MdiPackageVariantClosed from "~icons/mdi/package-variant-closed";
|
||||
import MdiDelete from "~icons/mdi/delete";
|
||||
import MdiRotateClockwise from "~icons/mdi/rotate-clockwise";
|
||||
// import MdiStarOutline from "~icons/mdi/star-outline";
|
||||
// import MdiStar from "~icons/mdi/star";
|
||||
import { AttachmentTypes } from "~~/lib/api/types/non-generated";
|
||||
@@ -294,4 +315,79 @@
|
||||
navigateTo(`/item/${data.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
function dataURLtoFile(dataURL: string, fileName: string) {
|
||||
try {
|
||||
const arr = dataURL.split(",");
|
||||
const mimeMatch = arr[0].match(/:(.*?);/);
|
||||
if (!mimeMatch || !mimeMatch[1]) {
|
||||
throw new Error("Invalid data URL format");
|
||||
}
|
||||
const mime = mimeMatch[1];
|
||||
|
||||
// Validate mime type is an image
|
||||
if (!mime.startsWith("image/")) {
|
||||
throw new Error("Invalid mime type, expected image");
|
||||
}
|
||||
|
||||
const bstr = atob(arr[arr.length - 1]);
|
||||
let n = bstr.length;
|
||||
const u8arr = new Uint8Array(n);
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new File([u8arr], fileName, { type: mime });
|
||||
} catch (error) {
|
||||
console.error("Error converting data URL to file:", error);
|
||||
// Return a fallback or rethrow based on your error handling strategy
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function rotateBase64Image90Deg(base64Image: string, index: number) {
|
||||
// Create an off-screen canvas
|
||||
const offScreenCanvas = document.createElement("canvas");
|
||||
const offScreenCanvasCtx = offScreenCanvas.getContext("2d");
|
||||
|
||||
if (!offScreenCanvasCtx) {
|
||||
toast.error("Your browser doesn't support canvas operations");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an image
|
||||
const img = new Image();
|
||||
|
||||
// Create a promise to handle the image loading
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
img.onload = () => resolve();
|
||||
img.onerror = () => reject(new Error("Failed to load image"));
|
||||
img.src = base64Image;
|
||||
}).catch(error => {
|
||||
toast.error("Failed to rotate image: " + error.message);
|
||||
});
|
||||
|
||||
// Set its dimensions to rotated size
|
||||
offScreenCanvas.height = img.width;
|
||||
offScreenCanvas.width = img.height;
|
||||
|
||||
// Rotate and draw source image into the off-screen canvas
|
||||
offScreenCanvasCtx.rotate((90 * Math.PI) / 180);
|
||||
offScreenCanvasCtx.translate(0, -offScreenCanvas.width);
|
||||
offScreenCanvasCtx.drawImage(img, 0, 0);
|
||||
|
||||
const imageType = base64Image.match(/^data:(.+);base64/)?.[1] || "image/jpeg";
|
||||
|
||||
// Encode image to data-uri with base64
|
||||
try {
|
||||
form.photos[index].fileBase64 = offScreenCanvas.toDataURL(imageType, 100);
|
||||
form.photos[index].file = dataURLtoFile(form.photos[index].fileBase64, form.photos[index].photoName);
|
||||
} catch (error) {
|
||||
toast.error("Failed to process rotated image");
|
||||
console.error(error);
|
||||
} finally {
|
||||
// Clean up resources
|
||||
offScreenCanvas.width = 0;
|
||||
offScreenCanvas.height = 0;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user