UNPKG

merchi_product_editor

Version:

A React component for editing product images using Fabric.js

343 lines (342 loc) 16.3 kB
"use strict"; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.renderClippedImage = exports.renderCanvasWithoutGrid = exports.addTextToCanvas = void 0; var fabric_1 = require("fabric"); /** * 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) */ var addTextToCanvas = function (canvas, width, height, defaultText, fontSize, fontFamily) { if (defaultText === void 0) { defaultText = 'Text'; } if (fontSize === void 0) { fontSize = 24; } if (fontFamily === void 0) { fontFamily = '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 var text_1 = new fabric_1.fabric.IText(defaultText, { left: width / 2, top: height / 2, fontFamily: fontFamily, fontSize: fontSize, fill: '#000000', originX: 'center', originY: 'center', selectable: true, editable: false }); // Add the text to the canvas canvas.add(text_1); // Ensure the text is rendered canvas.renderAll(); // Make the text active - this is critical var setActiveTextObject = function () { try { // Check if canvas is still valid if (canvas && canvas.getElement() && canvas.getElement().parentNode) { canvas.setActiveObject(text_1); 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_1; } catch (error) { console.error('Error adding text to canvas:', error); return null; } }; exports.addTextToCanvas = addTextToCanvas; /** * Renders the entire canvas as an image without displaying grid elements * * @param canvas - The Fabric.js canvas instance * @param options - Optional rendering options * @returns A promise that resolves to the dataURL of the canvas without grid */ var renderCanvasWithoutGrid = function (canvas, options) { return new Promise(function (resolve) { if (!canvas) { console.error('Canvas is null or undefined'); resolve(null); return; } try { // Get all objects on the canvas var objects = canvas.getObjects(); // Store original visibility of each object var originalVisibilities_1 = objects.map(function (obj) { return ({ object: obj, visible: obj.visible }); }); // Hide grid elements - typically grid elements might have a specific ID or class // You may need to adjust this logic based on how grid elements are identified objects.forEach(function (obj) { var _a, _b, _c, _d, _e; // Hide objects that represent grid elements // This assumes grid elements have a property like 'type' or 'id' that identifies them if (((_a = obj.id) === null || _a === void 0 ? void 0 : _a.includes('grid')) || obj.type === 'grid' || obj.isGridElement || ( // Add more comprehensive grid detection (_b = obj.data) === null || _b === void 0 ? void 0 : _b.isGrid) || ((_c = obj.name) === null || _c === void 0 ? void 0 : _c.includes('grid')) || ((_d = obj.className) === null || _d === void 0 ? void 0 : _d.includes('grid')) || // Check for common grid-related properties obj.strokeDashArray || // Dashed lines are often used for grids // Check for line objects with evenly spaced positions (typical grid pattern) (obj.type === 'line' && (obj.x1 === obj.x2 || obj.y1 === obj.y2)) || ( // Check for objects tagged with grid-related classes (_e = obj.classes) === null || _e === void 0 ? void 0 : _e.includes('grid')) || // Check for objects with certain grid styling (obj.stroke && (obj.strokeWidth === 0.5 || obj.strokeWidth === 1) && (obj.stroke === '#ddd' || obj.stroke === '#eee' || obj.stroke === 'rgba(0,0,0,0.1)'))) { obj.visible = false; } }); // Save the original selection state var originalSelectionBorder = canvas.selectionBorderColor; var originalSelectionColor = canvas.selectionColor; var originalActiveObject = canvas.getActiveObject(); // Temporarily disable selection indicators canvas.selectionBorderColor = 'transparent'; canvas.selectionColor = 'transparent'; canvas.discardActiveObject(); // Apply any background color if specified var originalBackgroundColor = canvas.backgroundColor; if ((options === null || options === void 0 ? void 0 : options.backgroundColor) !== undefined) { canvas.backgroundColor = options.backgroundColor || 'transparent'; } // Render the canvas canvas.renderAll(); // Get rendering options var format = (options === null || options === void 0 ? void 0 : options.format) || 'png'; var quality = (options === null || options === void 0 ? void 0 : options.quality) || 1; var multiplier = (options === null || options === void 0 ? void 0 : options.multiplier) || 1; // Create a data URL from the canvas try { var dataUrl = canvas.toDataURL({ format: format, quality: quality, multiplier: multiplier }); // Restore the original state objects.forEach(function (obj, index) { obj.visible = originalVisibilities_1[index].visible; }); // Restore selection state canvas.selectionBorderColor = originalSelectionBorder; canvas.selectionColor = originalSelectionColor; if (originalActiveObject) { canvas.setActiveObject(originalActiveObject); } // Restore background color canvas.backgroundColor = originalBackgroundColor; // Render with original state canvas.renderAll(); resolve(dataUrl); } catch (error) { console.error('Error generating data URL:', error); resolve(null); } } catch (error) { console.error('Error rendering canvas without grid:', error); resolve(null); } }); }; exports.renderCanvasWithoutGrid = renderCanvasWithoutGrid; /** * Renders an image containing only what's visible inside a clipPath on the canvas * * @param canvas - The Fabric.js canvas instance * @param options - Optional rendering options * @returns A promise that resolves to the dataURL of the contents inside the clipPath, or null if no clipped object is found */ var renderClippedImage = function (canvas, options) { return new Promise(function (resolve) { if (!canvas) { console.error('Canvas is null or undefined'); resolve(null); return; } try { // Find objects with clipPath var objects = canvas.getObjects(); var targetObject = null; targetObject = objects.find(function (obj) { return obj.id === 'design-boundary-rect'; }) || null; if (!targetObject) { console.warn('No boundary object found'); resolve(null); return; } // Get bounds of the object with clipPath var objBounds = targetObject.getBoundingRect(); // Create a new canvas element var dpr = window.devicePixelRatio || 1; var tempCanvasEl = document.createElement('canvas'); tempCanvasEl.width = objBounds.width * dpr; tempCanvasEl.height = objBounds.height * dpr; tempCanvasEl.style.width = "".concat(objBounds.width, "px"); tempCanvasEl.style.height = "".concat(objBounds.height, "px"); // Explicitly set alpha to true to support transparency var tempCtx = tempCanvasEl.getContext('2d', { alpha: true }); tempCtx && tempCtx.scale(dpr, dpr); if (!tempCtx) { console.error('Could not get canvas context'); resolve(null); return; } // Clear the canvas to ensure transparency tempCtx.clearRect(0, 0, tempCanvasEl.width, tempCanvasEl.height); // Set background color if provided, otherwise keep transparent if (options === null || options === void 0 ? void 0 : options.backgroundColor) { tempCtx.fillStyle = options.backgroundColor; tempCtx.fillRect(0, 0, tempCanvasEl.width, tempCanvasEl.height); } // Save current canvas state var originalZoom = canvas.getZoom(); var originalViewportTransform = canvas.viewportTransform ? __spreadArray([], canvas.viewportTransform, true) : [1, 0, 0, 1, 0, 0]; // Save the original background color of the canvas var originalBackgroundColor = canvas.backgroundColor; // Temporarily set canvas background to transparent canvas.backgroundColor = 'transparent'; // Store original object positions var originalPositions = objects.map(function (obj) { return ({ object: obj, left: obj.left, top: obj.top, scaleX: obj.scaleX, scaleY: obj.scaleY }); }); // Temporarily modify the canvas view to focus on our object canvas.setZoom(1); canvas.setViewportTransform([1, 0, 0, 1, -objBounds.left, -objBounds.top]); // Temporarily hide all objects except our target and its clipped content var originalVisibilities = objects.map(function (obj) { return ({ object: obj, visible: obj.visible }); }); objects.forEach(function (obj) { if (!obj.fieldId) { obj.visible = false; } }); // Temporarily disable selection borders at canvas level var originalSelectionBorder = canvas.selectionBorderColor; var originalSelectionColor = canvas.selectionColor; canvas.selectionBorderColor = 'transparent'; canvas.selectionColor = 'transparent'; // Temporarily disable selection borders at object level var originalObjectStates = objects.map(function (obj) { return ({ object: obj, borderColor: obj.borderColor, cornerColor: obj.cornerColor, cornerStrokeColor: obj.cornerStrokeColor, transparentCorners: obj.transparentCorners, hasControls: obj.hasControls, hasBorders: obj.hasBorders }); }); objects.forEach(function (obj) { obj.borderColor = 'transparent'; obj.cornerColor = 'transparent'; obj.cornerStrokeColor = 'transparent'; obj.transparentCorners = true; obj.hasControls = false; obj.hasBorders = false; }); // Render just the isolated object canvas.renderAll(); // Draw the current canvas view to our temp canvas tempCtx.drawImage(canvas.getElement(), 0, 0, objBounds.width * dpr, objBounds.height * dpr, 0, 0, objBounds.width, objBounds.height); // Restore the original object positions originalPositions.forEach(function (item) { item.object.left = item.left; item.object.top = item.top; item.object.scaleX = item.scaleX; item.object.scaleY = item.scaleY; item.object.setCoords(); }); // Restore the original canvas state canvas.setZoom(originalZoom); canvas.setViewportTransform(originalViewportTransform); canvas.backgroundColor = originalBackgroundColor; originalVisibilities.forEach(function (item) { item.object.visible = item.visible; }); // Restore object-level selection borders originalObjectStates.forEach(function (state) { state.object.borderColor = state.borderColor; state.object.cornerColor = state.cornerColor; state.object.cornerStrokeColor = state.cornerStrokeColor; state.object.transparentCorners = state.transparentCorners; state.object.hasControls = state.hasControls; state.object.hasBorders = state.hasBorders; }); // Restore canvas-level selection borders canvas.selectionBorderColor = originalSelectionBorder; canvas.selectionColor = originalSelectionColor; canvas.renderAll(); // Get the data URL from the temp canvas var format = (options === null || options === void 0 ? void 0 : options.format) || 'png'; var quality = (options === null || options === void 0 ? void 0 : options.quality) || 1; // Create a new canvas for final rendering with proper transparency var finalCanvas = document.createElement('canvas'); finalCanvas.width = tempCanvasEl.width; finalCanvas.height = tempCanvasEl.height; var finalCtx = finalCanvas.getContext('2d', { alpha: true }); finalCtx && finalCtx.scale(1, 1); if (!finalCtx) { console.error('Could not get final canvas context'); resolve(null); return; } // Clear the final canvas to ensure transparency finalCtx.clearRect(0, 0, finalCanvas.width, finalCanvas.height); // Draw the temp canvas content to the final canvas finalCtx.drawImage(tempCanvasEl, 0, 0); // Try to get the data URL from the final canvas try { // Ensure PNG format for transparency support var dataUrl = finalCanvas.toDataURL("image/".concat(format === 'jpg' ? 'png' : format), quality); resolve(dataUrl); } catch (error) { console.error('Error getting data URL:', error); resolve(null); } } catch (error) { console.error('Error rendering clipped image:', error); resolve(null); } }); }; exports.renderClippedImage = renderClippedImage;