133 lines
4.2 KiB
JavaScript
133 lines
4.2 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
|
import { fromBlob, blobToURL } from "image-resize-compress";
|
|
import { Button } from "../ui/button";
|
|
import {
|
|
ImageIcon,
|
|
DownloadIcon,
|
|
SlidersHorizontal,
|
|
FileImage,
|
|
} from "lucide-react";
|
|
|
|
export default function ImageResize() {
|
|
const [previewUrl, setPreviewUrl] = useState(null);
|
|
const [resizedBlob, setResizedBlob] = useState(null);
|
|
const [imgQuality, setImgQuality] = useState(80);
|
|
const [selectedFile, setSelectedFile] = useState(null);
|
|
const [originalSize, setOriginalSize] = useState(0);
|
|
const [resizedSize, setResizedSize] = useState(0);
|
|
const [originalExtension, setOriginalExtension] = useState("webp");
|
|
|
|
useEffect(() => {
|
|
const resizeImage = async () => {
|
|
if (!selectedFile) return;
|
|
|
|
try {
|
|
const width = "auto";
|
|
const height = "auto";
|
|
const ext = selectedFile.name.split(".").pop().toLowerCase();
|
|
|
|
setOriginalExtension(ext);
|
|
setOriginalSize((selectedFile.size / 1024).toFixed(2)); // KB
|
|
|
|
// Only apply quality change for JPEG or WebP
|
|
const format = ["jpg", "jpeg", "webp"].includes(ext)
|
|
? ext
|
|
: "jpeg"; // fallback to jpeg for better quality control
|
|
|
|
const resized = await fromBlob(selectedFile, imgQuality, width, height, format);
|
|
const url = await blobToURL(resized);
|
|
|
|
setResizedBlob(resized);
|
|
setResizedSize((resized.size / 1024).toFixed(2)); // KB
|
|
setPreviewUrl(url);
|
|
} catch (err) {
|
|
console.error("Image resizing failed:", err);
|
|
}
|
|
};
|
|
|
|
resizeImage();
|
|
}, [selectedFile, imgQuality]);
|
|
|
|
const handleFileChange = (event) => {
|
|
const file = event.target.files[0];
|
|
if (file) {
|
|
setSelectedFile(file);
|
|
}
|
|
};
|
|
|
|
const handleDownload = () => {
|
|
if (!resizedBlob) return;
|
|
const link = document.createElement("a");
|
|
link.href = URL.createObjectURL(resizedBlob);
|
|
link.download = `resized-image.${originalExtension}`;
|
|
link.click();
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen text-white p-6 flex items-center justify-center">
|
|
<div className="bg-gray-800 rounded-2xl shadow-lg w-full max-w-2xl p-6 space-y-6">
|
|
<h2 className="text-2xl font-bold flex items-center gap-2 text-[#6d9e37]">
|
|
<ImageIcon className="w-6 h-6" />
|
|
Image Resizer
|
|
</h2>
|
|
|
|
<label className="flex items-center gap-2 text-sm text-gray-300 font-medium">
|
|
<FileImage className="w-4 h-4" />
|
|
Choose an image
|
|
</label>
|
|
<input
|
|
type="file"
|
|
accept="image/*"
|
|
onChange={handleFileChange}
|
|
className="w-full p-2 bg-gray-700 border border-gray-600 rounded-lg text-sm file:bg-[#6d9e37] file:text-white file:border-0 file:px-3 file:py-1"
|
|
/>
|
|
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-sm font-medium flex items-center gap-1">
|
|
<SlidersHorizontal className="w-4 h-4" />
|
|
Quality: <span className="text-[#6d9e37]">{imgQuality}%</span>
|
|
</label>
|
|
<input
|
|
type="range"
|
|
min="1"
|
|
max="100"
|
|
value={imgQuality}
|
|
onChange={(e) => setImgQuality(e.target.value)}
|
|
className="w-full accent-[#6d9e37]"
|
|
/>
|
|
</div>
|
|
|
|
{previewUrl && (
|
|
<div className="border-t border-gray-700 pt-4 space-y-3">
|
|
<div className="text-sm text-gray-300 grid grid-cols-2 gap-2">
|
|
<p>
|
|
<strong>Original Size:</strong> {originalSize} KB
|
|
</p>
|
|
<p>
|
|
<strong>Resized Size:</strong> {resizedSize} KB
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<p className="text-sm font-medium text-gray-400 mb-2">Preview:</p>
|
|
<img
|
|
src={previewUrl}
|
|
alt="Resized Preview"
|
|
className="w-full h-auto max-h-96 object-contain rounded-lg border border-gray-700"
|
|
/>
|
|
</div>
|
|
|
|
<Button
|
|
onClick={handleDownload}
|
|
className="mt-4 w-full gap-2"
|
|
>
|
|
<DownloadIcon className="w-5 h-5" />
|
|
Download Resized Image
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|