UNPKG

merchi_product_editor

Version:

A React component for editing product images using Fabric.js

165 lines (144 loc) 3.97 kB
import { fabric } from 'fabric'; import { MerchiFile } from '../types'; export const loadImageFromUrl = ( canvas: fabric.Canvas, viewUrl: string, width: number, height: number ): Promise<void> => { return new Promise((resolve, reject) => { fabric.Image.fromURL(viewUrl, (img) => { if (!img) { reject(new Error('Failed to load image')); return; } // Scale image to fit canvas while maintaining aspect ratio const scale = Math.min( width / img.width!, height / img.height! ); img.scale(scale); // Center the image img.set({ left: (width - img.width! * scale) / 2, top: (height - img.height! * scale) / 2, selectable: false, evented: false, }); canvas.add(img); canvas.sendToBack(img); canvas.renderAll(); resolve(); }); }); }; /** * Adds an editable text object to the canvas * @param canvas - The Fabric.js canvas instance * @param width - The width of the canvas * @param height - The height of the canvas * @param defaultText - Optional default text (defaults to "Text") * @param fontSize - Optional font size (defaults to 24) * @param fontFamily - Optional font family (defaults to Arial) */ export const addTextToCanvas = ( canvas: fabric.Canvas, width: number, height: number, defaultText: string = 'Text', fontSize: number = 24, fontFamily: string = 'Nunito' ) => { if (!canvas) return null; try { // Safe check if canvas is still valid if (!canvas.getElement() || !canvas.getElement().parentNode) { return null; } // Create a new text object with minimal options const text = new fabric.IText( defaultText, { left: width / 2, top: height / 2, fontFamily: fontFamily, fontSize: fontSize, fill: '#000000', originX: 'center', originY: 'center', selectable: true, editable: true } ); // Add the text to the canvas canvas.add(text); // Ensure the text is rendered canvas.renderAll(); // Make the text active - this is critical const setActiveTextObject = () => { try { // Check if canvas is still valid if (canvas && canvas.getElement() && canvas.getElement().parentNode) { canvas.setActiveObject(text); canvas.renderAll(); } } catch (err) { console.error('Error setting active text object:', err); } }; // Initial selection setActiveTextObject(); // Delayed selection to ensure text becomes active setTimeout(setActiveTextObject, 50); return text; } catch (error) { console.error('Error adding text to canvas:', error); return null; } }; export const addFilesToCanvas = async ( canvas: fabric.Canvas, files: MerchiFile[], width: number, height: number ): Promise<void> => { if (!canvas || !files.length) return; for (const file of files) { if (!file.viewUrl) continue; await new Promise<void>((resolve) => { if (!file.viewUrl) { resolve(); return; } fabric.Image.fromURL(file.viewUrl, (img) => { if (!img) { resolve(); return; } // Scale image to fit canvas while maintaining aspect ratio const scale = Math.min( (width * 0.5) / img.width!, (height * 0.5) / img.height!, 1 ); img.scale(scale); // Center the image img.set({ left: width / 2, top: height / 2, cornerSize: 8, borderColor: '#303DBF', cornerColor: '#303DBF', cornerStrokeColor: '#303DBF', transparentCorners: false, selectable: true, evented: true }); canvas.add(img); canvas.setActiveObject(img); canvas.renderAll(); resolve(); }); }); } };