115 lines
3.2 KiB
JavaScript
115 lines
3.2 KiB
JavaScript
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;
|
||
};
|