merchi_product_editor
Version:
A React component for editing product images using Fabric.js
145 lines (130 loc) • 4.05 kB
text/typescript
import { fabric } from 'fabric';
import { generatePsdPreview, loadPsdFromUrl } from './PsdPreviewGenerator';
/**
* uploadImage
* @param canvas
* @param width
* @param height
* @param onImageAdded
* @param onPreviewGenerated
* @param psdTemplateUrl
*/
export const uploadImage = (
canvas: fabric.Canvas,
width: number,
height: number,
onImageAdded?: (dataUrl: string) => void,
onPreviewGenerated?: (previewDataUrl: string) => void,
psdTemplateUrl?: string
) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = async (e: Event) => {
const target = e.target as HTMLInputElement;
if (target.files && target.files[0] && canvas) {
const file = target.files[0];
const reader = new FileReader();
reader.onload = async (event) => {
if (event.target?.result && canvas) {
const imgUrl = event.target.result as string;
fabric.Image.fromURL(
imgUrl,
(img) => {
if (!canvas) return;
// adjust the image size to fit the canvas
const imgWidth = img.width || 200;
const imgHeight = img.height || 200;
const scale = Math.min(
(width * 0.5) / imgWidth,
(height * 0.5) / imgHeight,
1
);
img.scale(scale);
// rotation control
img.setControlsVisibility({
mtr: true,
});
// put the image in the center of the canvas
img.set({
left: width / 2,
top: height / 2,
cornerSize: 8,
borderColor: '#303DBF',
cornerColor: '#303DBF',
cornerStrokeColor: '#303DBF',
transparentCorners: false
});
canvas.add(img);
canvas.setActiveObject(img);
canvas.renderAll();
if (psdTemplateUrl && onPreviewGenerated) {
try {
loadPsdFromUrl(psdTemplateUrl)
.then(psdBuffer => {
return generatePsdPreview(psdBuffer, imgUrl);
})
.then(previewDataUrl => {
if (onPreviewGenerated) {
onPreviewGenerated(previewDataUrl);
}
})
.catch(error => {
console.error('Failed to generate PSD preview:', error);
});
} catch (error) {
console.error('Failed to process preview:', error);
}
}
if (onImageAdded) {
try {
const dataUrl = canvas.toDataURL({
format: 'png',
quality: 1,
});
onImageAdded(dataUrl);
} catch (err) {
console.warn('Failed to export dataUrl', err);
}
}
},
{ crossOrigin: 'anonymous' }
);
}
};
reader.readAsDataURL(file);
}
};
input.click();
};
/**
* setupKeyboardEvents
* @param canvas
* @param onObjectRemoved
* @returns
*/
export const setupKeyboardEvents = (
canvas: fabric.Canvas,
onObjectRemoved?: (dataUrl: string) => void
) => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Delete') {
const activeObject = canvas.getActiveObject();
if (activeObject) {
canvas.remove(activeObject);
canvas.renderAll();
if (onObjectRemoved) {
const dataUrl = canvas.toDataURL({
format: 'png',
quality: 1,
});
onObjectRemoved(dataUrl);
}
}
}
};
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
};