diff options
| -rw-r--r-- | app/lib/camera/component/dispatchCamera.tsx | 61 | ||||
| -rw-r--r-- | app/lib/camera/component/pakageCamera.tsx | 64 | ||||
| -rw-r--r-- | app/lib/camera/component/sjCamera.tsx | 73 |
3 files changed, 149 insertions, 49 deletions
diff --git a/app/lib/camera/component/dispatchCamera.tsx b/app/lib/camera/component/dispatchCamera.tsx index 2591413..6780c12 100644 --- a/app/lib/camera/component/dispatchCamera.tsx +++ b/app/lib/camera/component/dispatchCamera.tsx @@ -3,17 +3,68 @@ import useCameraStore from "../hooks/useCameraStore"; import { IconButton } from "@mui/material"; import { LocalShipping } from "@mui/icons-material"; +const MAX_WIDTH = 1024; +const JPEG_QUALITY = 0.7; + const DispatchCamera: React.FC = () => { const { setImageDispatch } = useCameraStore(); - const handleCapture = (event: React.ChangeEvent<HTMLInputElement>) => { - const file = event.target.files?.[0]; - if (file) { + + const compressImage = (file: File): Promise<string> => { + return new Promise((resolve, reject) => { const reader = new FileReader(); - reader.onloadend = () => { - setImageDispatch(reader.result as string); + + reader.onload = () => { + const img = new Image(); + img.onload = () => { + let { width, height } = img; + + if (width > MAX_WIDTH) { + height = (height * MAX_WIDTH) / width; + width = MAX_WIDTH; + } + + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + + const ctx = canvas.getContext("2d"); + if (!ctx) { + reject("Canvas not supported"); + return; + } + + ctx.drawImage(img, 0, 0, width, height); + + const compressedBase64 = canvas.toDataURL("image/jpeg", JPEG_QUALITY); + + resolve(compressedBase64); + }; + + img.onerror = reject; + img.src = reader.result as string; }; + + reader.onerror = reject; reader.readAsDataURL(file); + }); + }; + const handleCapture = async (event: React.ChangeEvent<HTMLInputElement>) => { + const file = event.target.files?.[0]; + if (!file) return; + + try { + const compressesd = await compressImage(file); + setImageDispatch(compressesd); + } catch (err) { + console.error("Image compress failed", err); } + // if (file) { + // const reader = new FileReader(); + // reader.onloadend = () => { + // setImageDispatch(reader.result as string); + // }; + // reader.readAsDataURL(file); + // } }; return ( diff --git a/app/lib/camera/component/pakageCamera.tsx b/app/lib/camera/component/pakageCamera.tsx index 23ed9a4..7efabb6 100644 --- a/app/lib/camera/component/pakageCamera.tsx +++ b/app/lib/camera/component/pakageCamera.tsx @@ -3,34 +3,76 @@ import useCameraStore from "../hooks/useCameraStore"; import { IconButton } from "@mui/material"; import { PhotoCameraFrontOutlined } from "@mui/icons-material"; +const MAX_WIDTH = 1024; +const JPEG_QUALITY = 0.7; + const PackageCamera: React.FC = () => { const { setImagePackage } = useCameraStore(); - const handleCapture = (event: React.ChangeEvent<HTMLInputElement>) => { - const file = event.target.files?.[0]; - if (file) { + + const compressImage = (file: File): Promise<string> => { + return new Promise((resolve, reject) => { const reader = new FileReader(); - reader.onloadend = () => { - setImagePackage(reader.result as string); + + reader.onload = () => { + const img = new Image(); + img.onload = () => { + let { width, height } = img; + + if (width > MAX_WIDTH) { + height = (height * MAX_WIDTH) / width; + width = MAX_WIDTH; + } + + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + + const ctx = canvas.getContext("2d"); + if (!ctx) { + reject("Canvas not supported"); + return; + } + + ctx.drawImage(img, 0, 0, width, height); + + const compressedBase64 = canvas.toDataURL("image/jpeg", JPEG_QUALITY); + + resolve(compressedBase64); + }; + + img.onerror = reject; + img.src = reader.result as string; }; + + reader.onerror = reject; reader.readAsDataURL(file); + }); + }; + + const handleCapture = async (event: React.ChangeEvent<HTMLInputElement>) => { + const file = event.target.files?.[0]; + if (!file) return; + + try { + const compressed = await compressImage(file); + setImagePackage(compressed); + } catch (err) { + console.error("Image compress failed", err); } }; return ( - <div className="px-4 py-8 items-center border-2 rounded-md shadow-sm w-[49%] text-center "> + <div className="px-4 py-8 items-center border-2 rounded-md shadow-sm w-[49%] text-center"> <input type="file" accept="image/*" + capture="environment" onChange={handleCapture} className="hidden" id="pakageCameraInput" /> <label htmlFor="pakageCameraInput" className="text-gray-600"> - <IconButton - color="primary" - aria-label="upload picture" - component="span" - > + <IconButton color="primary" component="span"> <PhotoCameraFrontOutlined fontSize="large" /> </IconButton> <br /> diff --git a/app/lib/camera/component/sjCamera.tsx b/app/lib/camera/component/sjCamera.tsx index 73062e2..0cb43ac 100644 --- a/app/lib/camera/component/sjCamera.tsx +++ b/app/lib/camera/component/sjCamera.tsx @@ -3,41 +3,51 @@ import useCameraStore from "../hooks/useCameraStore"; import { IconButton } from "@mui/material"; import { PendingActions } from "@mui/icons-material"; +const compressImage = ( + file: File, + maxWidth = 1600, + quality = 0.6 +): Promise<string> => { + return new Promise((resolve) => { + const img = new Image(); + const reader = new FileReader(); + + reader.onload = () => { + img.src = reader.result as string; + }; + + img.onload = () => { + const scale = Math.min(1, maxWidth / img.width); + const canvas = document.createElement("canvas"); + + canvas.width = img.width * scale; + canvas.height = img.height * scale; + + const ctx = canvas.getContext("2d")!; + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + + resolve(canvas.toDataURL("image/jpeg", quality)); + }; + + reader.readAsDataURL(file); + }); +}; + const SjCamera: React.FC = () => { const { setImageSj } = useCameraStore(); const fileRef = useRef<HTMLInputElement | null>(null); - const readFilesAsDataURL = (files: FileList | null): Promise<string[]> => - new Promise((resolve) => { - if (!files || files.length === 0) return resolve([]); - const list = Array.from(files); - const out: string[] = []; - let done = 0; - list.forEach((f) => { - const fr = new FileReader(); - fr.onload = (e) => { - const dataUrl = (e.target?.result as string) || ""; - if (dataUrl) out.push(dataUrl); - done += 1; - if (done === list.length) resolve(out); - }; - fr.onerror = () => { - done += 1; - if (done === list.length) resolve(out); - }; - fr.readAsDataURL(f); - }); - }); - const handleSuratJalanCapture = async ( event: React.ChangeEvent<HTMLInputElement> ) => { - const imgs = await readFilesAsDataURL(event.target.files); - if (imgs.length > 0) { - // APPEND: panggil setImageSj untuk tiap foto - imgs.forEach((img) => setImageSj(img)); - } - // reset supaya bisa pilih file yang sama lagi + const imgs = await Promise.all( + Array.from(event.target.files ?? []).map((file) => + compressImage(file, 1600, 0.6) + ) + ); + + imgs.forEach((img) => setImageSj(img)); + if (fileRef.current) fileRef.current.value = ""; }; @@ -49,6 +59,7 @@ const SjCamera: React.FC = () => { type="file" accept="image/*" multiple + capture="environment" className="hidden" onChange={handleSuratJalanCapture} /> @@ -56,11 +67,7 @@ const SjCamera: React.FC = () => { htmlFor="suratJalanInput" className="text-gray-600 cursor-pointer select-none" > - <IconButton - color="primary" - aria-label="upload picture" - component="span" - > + <IconButton color="primary" component="span"> <PendingActions fontSize="large" /> </IconButton> <br /> |
