const loadImage = (file) => new Promise((resolve, reject) => { const image = new Image(); image.onload = () => { URL.revokeObjectURL(image.src); resolve(image); }; image.onerror = (error) => { URL.revokeObjectURL(image.src); reject(error); }; image.src = URL.createObjectURL(file); }); const clamp = (value, min, max) => Math.min(Math.max(value, min), max); const normalizeCropConfig = (config) => { if (!config?.imageWidth || !config?.imageHeight) { throw new Error('Geçerli bir crop referansı bulunamadı.'); } const safeWidth = Math.max( 1, config.width - (config.left ?? 0) - (config.right ?? 0), ); const safeHeight = Math.max( 1, config.height - (config.top ?? 0) - (config.bottom ?? 0), ); const xStart = Math.max(0, config.cropAreaX + (config.left ?? 0)); const yStart = Math.max(0, config.cropAreaY + (config.top ?? 0)); return { xRatio: xStart / config.imageWidth, yRatio: yStart / config.imageHeight, widthRatio: safeWidth / config.imageWidth, heightRatio: safeHeight / config.imageHeight, }; }; const cropImage = async (file, normalizedConfig) => { const image = await loadImage(file); const { width: imgWidth, height: imgHeight } = image; const cropWidth = clamp( Math.round(normalizedConfig.widthRatio * imgWidth), 1, imgWidth, ); const cropHeight = clamp( Math.round(normalizedConfig.heightRatio * imgHeight), 1, imgHeight, ); const startX = clamp( Math.round(normalizedConfig.xRatio * imgWidth), 0, imgWidth - cropWidth, ); const startY = clamp( Math.round(normalizedConfig.yRatio * imgHeight), 0, imgHeight - cropHeight, ); const canvas = document.createElement('canvas'); canvas.width = cropWidth; canvas.height = cropHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(image, startX, startY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight); const blob = await new Promise((resolve, reject) => { canvas.toBlob((result) => { if (result) { resolve(result); } else { reject(new Error('Canvas blob oluşturulamadı.')); } }, 'image/png'); }); const url = URL.createObjectURL(blob); return { blob, url }; }; const buildCropResult = (imageMeta, blob, url) => ({ id: imageMeta.id, filename: imageMeta.file?.name || imageMeta.filename, blob, url, order: imageMeta.order, }); export const cropSingleImage = async (image, config) => { if (!config || !config.imageWidth) { throw new Error('Kapak için geçerli bir crop ayarı bulunamadı.'); } const normalized = normalizeCropConfig(config); const { blob, url } = await cropImage(image.file, normalized); return buildCropResult(image, blob, url); }; export const applyCropToImages = async (images, config) => { if (!images?.length) { throw new Error('Önce görsel yüklemelisin.'); } if (!config || !config.imageWidth) { throw new Error('Crop ayarı bulunamadı.'); } const normalized = normalizeCropConfig(config); const results = []; for (const image of images) { const { blob, url } = await cropImage(image.file, normalized); results.push(buildCropResult(image, blob, url)); } return results; };