API 참조
이미지 크롭퍼(Image Cropper) 기능을 통합하거나 확장하려는 개발자를 위한 기술 참조 문서입니다.
이미지 크롭 인터페이스를 제공하는 메인 Vue 컴포넌트입니다.
<template>
<ImageCropper
:aspect-ratio="aspectRatio"
:output-format="format"
:quality="quality"
@crop-complete="handleCropComplete"
/>
</template>
| 속성 | 타입 | 기본값 | 설명 |
|---|
aspectRatio | number | 'free' | 'free' | 크롭 종횡비 제한 |
outputFormat | 'png' | 'jpeg' | 'webp' | 'png' | 출력 이미지 형식 |
quality | number[] | [85] | JPEG 품질 (10-100) |
maxWidth | number | undefined | 최대 출력 너비 |
maxHeight | number | undefined | 최대 출력 높이 |
| 이벤트 | 페이로드 | 설명 |
|---|
crop-complete | CropResult | 크롭이 완료될 때 발생 |
image-loaded | ImageInfo | 소스 이미지가 로드될 때 발생 |
error | Error | 에러가 발생할 때 발생 |
interface CropResult {
url: string; // 크롭된 이미지의 Blob URL
blob: Blob; // 이미지 Blob 데이터
canvas: HTMLCanvasElement; // Canvas 요소
info: {
width: number; // 출력 너비 (픽셀)
height: number; // 출력 높이 (픽셀)
size: number; // 파일 크기 (바이트)
format: string; // 출력 형식
};
}
interface ImageInfo {
width: number; // 원본 너비
height: number; // 원본 높이
size: number; // 파일 크기 (바이트)
type: string; // MIME 타입
name: string; // 원본 파일명
}
interface CropSettings {
aspectRatio: number | "free";
outputFormat: "png" | "jpeg" | "webp";
quality: number;
outputWidth?: number;
outputHeight?: number;
}
const cropperOptions = {
aspectRatio: NaN, // 자유로운 종횡비
viewMode: 1, // 크롭 박스를 캔버스 내로 제한
responsive: true, // 반응형 크롭퍼
restore: false, // 크기 조정 후 복원 안 함
guides: true, // 점선 표시
center: true, // 중앙 표시기 표시
highlight: false, // 크롭 영역 강조 안 함
cropBoxMovable: true, // 크롭 박스 이동 허용
cropBoxResizable: true, // 크롭 박스 크기 조정 허용
toggleDragModeOnDblclick: false, // 더블 클릭 토글 비활성화
};
| 메서드 | 매개변수 | 반환값 | 설명 |
|---|
getCroppedCanvas() | options? | HTMLCanvasElement | 크롭 영역을 캔버스로 가져오기 |
setAspectRatio() | ratio: number | void | 종횡비 변경 |
reset() | - | void | 원래 상태로 재설정 |
destroy() | - | void | 크롭퍼 인스턴스 파괴 |
// 파일에서 이미지 로드
function loadImageFromFile(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target?.result as string);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// blob을 파일로 다운로드
function downloadBlob(blob: Blob, filename: string): void {
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
// 캔버스를 blob으로 변환
function canvasToBlob(canvas: HTMLCanvasElement, format: string, quality?: number): Promise<Blob> {
return new Promise((resolve) => {
canvas.toBlob(resolve, `image/${format}`, quality);
});
}
// 이미지 크기 가져오기
function getImageDimensions(src: string): Promise<{ width: number; height: number }> {
return new Promise((resolve) => {
const img = new Image();
img.onload = () =>
resolve({
width: img.naturalWidth,
height: img.naturalHeight,
});
img.src = src;
});
}
// 표시용 파일 크기 포맷팅
function formatFileSize(bytes: number): string {
const sizes = ["Bytes", "KB", "MB", "GB"];
if (bytes === 0) return "0 Bytes";
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + " " + sizes[i];
}
// 이미지 파일 유효성 검사
function isValidImageFile(file: File): boolean {
return file.type.startsWith("image/");
}
- Canvas API: 이미지 조작용
- File API: 파일 업로드 처리용
- Blob API: 결과 생성용
- URL.createObjectURL: 이미지 미리보기용
- Clipboard API: 복사 기능용 (선택 사항)
| 기능 | Chrome | Firefox | Safari | Edge |
|---|
| 핵심 기능 | ✅ 50+ | ✅ 52+ | ✅ 10+ | ✅ 79+ |
| WebP 출력 | ✅ 32+ | ✅ 65+ | ✅ 14+ | ✅ 79+ |
| 클립보드 API | ✅ 66+ | ✅ 63+ | ✅ 13.1+ | ✅ 79+ |
// 파일 유효성 검사 에러
class InvalidFileTypeError extends Error {
constructor() {
super("Invalid file type. Please select an image file.");
}
}
// 메모리 에러
class ImageTooLargeError extends Error {
constructor() {
super("Image is too large to process in this browser.");
}
}
// 처리 에러
class CropProcessingError extends Error {
constructor(message: string) {
super(`Crop processing failed: ${message}`);
}
}
// 우아한 에러 처리
function handleCropError(error: Error): void {
if (error instanceof InvalidFileTypeError) {
// 파일 유형 에러 메시지 표시
showErrorMessage("Please select a valid image file");
} else if (error instanceof ImageTooLargeError) {
// 이미지 크기 축소 제안
showErrorMessage("Image too large. Please use a smaller image");
} else {
// 일반적인 에러 처리
showErrorMessage("An error occurred. Please try again");
}
}
// 리소스 정리
function cleanup(): void {
// blob URL 취소
blobUrls.forEach((url) => URL.revokeObjectURL(url));
// 크롭퍼 인스턴스 파괴
if (cropper) {
cropper.destroy();
cropper = null;
}
// 캔버스 참조 지우기
canvasRefs.length = 0;
}
// 큰 이미지를 효율적으로 처리
function processLargeImage(canvas: HTMLCanvasElement): HTMLCanvasElement {
const maxDimension = 4096; // 브라우저 제한
const scale = Math.min(maxDimension / canvas.width, maxDimension / canvas.height, 1);
if (scale < 1) {
const scaledCanvas = document.createElement("canvas");
scaledCanvas.width = canvas.width * scale;
scaledCanvas.height = canvas.height * scale;
const ctx = scaledCanvas.getContext("2d");
ctx.drawImage(canvas, 0, 0, scaledCanvas.width, scaledCanvas.height);
return scaledCanvas;
}
return canvas;
}