From b8de25e9d1e3d1d04fb2ea68a7366d70f93986cc Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Mon, 15 Jun 2026 16:40:08 +0330 Subject: [PATCH] fix big size image --- .../components/canvas/ObjectRenderer.tsx | 8 ++-- .../components/canvas/useTransformHandlers.ts | 31 +++++++++--- src/pages/editor/utils/transformCommit.ts | 47 +++++++++++++++++++ src/pages/viewer/components/BookPage.tsx | 31 +++++++++--- src/pages/viewer/utils/dataTransformer.ts | 5 +- 5 files changed, 105 insertions(+), 17 deletions(-) create mode 100644 src/pages/editor/utils/transformCommit.ts diff --git a/src/pages/editor/components/canvas/ObjectRenderer.tsx b/src/pages/editor/components/canvas/ObjectRenderer.tsx index 397d573..5e7d88e 100644 --- a/src/pages/editor/components/canvas/ObjectRenderer.tsx +++ b/src/pages/editor/components/canvas/ObjectRenderer.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef } from "react"; import { Group, Rect, Circle, RegularPolygon, Star } from "react-konva"; import Konva from "konva"; import type { EditorObject } from "../../store/editorStore"; +import { getNodeTransformDimensions } from "../../utils/transformCommit"; import { RectangleShape, CircleShape, @@ -404,9 +405,10 @@ const ObjectRenderer = ({ newWidth = Math.max(5, (obj.width || 100) * scaleX); newHeight = Math.max(5, (obj.height || 100) * scaleY); } else { - // Default for other objects (image, etc.) - newWidth = Math.max(5, (obj.width || 100) * scaleX); - newHeight = Math.max(5, (obj.height || 100) * scaleY); + const { width: baseWidth, height: baseHeight } = + getNodeTransformDimensions(node); + newWidth = Math.max(5, baseWidth * scaleX); + newHeight = Math.max(5, baseHeight * scaleY); } // به‌روزرسانی object diff --git a/src/pages/editor/components/canvas/useTransformHandlers.ts b/src/pages/editor/components/canvas/useTransformHandlers.ts index 09eab95..e565b92 100644 --- a/src/pages/editor/components/canvas/useTransformHandlers.ts +++ b/src/pages/editor/components/canvas/useTransformHandlers.ts @@ -1,6 +1,11 @@ -import { useEffect, type RefObject } from "react"; +import { useEffect, useRef, type RefObject } from "react"; import Konva from "konva"; import { useEditorStore } from "../../store/editorStore"; +import { + getNodeTransformDimensions, + nodeHasPendingTransform, + resolveObjectFromNodeId, +} from "../../utils/transformCommit"; export const useTransformHandlers = ( transformerRef: RefObject, @@ -12,6 +17,7 @@ export const useTransformHandlers = ( updateObject, applyTableResize, } = useEditorStore(); + const commitTransformRef = useRef<(() => void) | null>(null); // Attach transformer to selected objects when they change useEffect(() => { @@ -60,7 +66,15 @@ export const useTransformHandlers = ( } }, 100); - return () => clearTimeout(timeoutId); + return () => { + clearTimeout(timeoutId); + const transformer = transformerRef.current; + if (!transformer) return; + const nodes = transformer.nodes(); + if (nodes.some(nodeHasPendingTransform)) { + commitTransformRef.current?.(); + } + }; }, [selectedObjectIds, objects, transformerRef, layerRef]); useEffect(() => { @@ -148,7 +162,7 @@ export const useTransformHandlers = ( nodes.forEach((node) => { const nodeId = node.id(); - const selectedObject = objects.find((obj) => obj.id === nodeId); + const selectedObject = resolveObjectFromNodeId(nodeId, objects); if (!selectedObject) return; // Handle group objects @@ -505,23 +519,28 @@ export const useTransformHandlers = ( height: Math.max(1, newHeight), }); } else { - // Default for other objects (image, etc.) + // Default for raster objects (image, sticker, document, video, …) + const { width: baseWidth, height: baseHeight } = + getNodeTransformDimensions(node); updateObject(selectedObject.id, { x: node.x(), y: node.y(), rotation: node.rotation(), - width: Math.max(5, (selectedObject.width || 100) * scaleX), - height: Math.max(5, (selectedObject.height || 100) * scaleY), + width: Math.max(5, baseWidth * scaleX), + height: Math.max(5, baseHeight * scaleY), }); } } }); }; + commitTransformRef.current = handleTransformEnd; + transformer.on("transform", handleTransform); transformer.on("transformend", handleTransformEnd); return () => { + commitTransformRef.current = null; transformer.off("transform", handleTransform); transformer.off("transformend", handleTransformEnd); }; diff --git a/src/pages/editor/utils/transformCommit.ts b/src/pages/editor/utils/transformCommit.ts new file mode 100644 index 0000000..2b7aac4 --- /dev/null +++ b/src/pages/editor/utils/transformCommit.ts @@ -0,0 +1,47 @@ +import Konva from "konva"; +import type { EditorObject } from "../store/editorStore"; + +/** Resolve editor object from a Konva node id (supports masked-* groups). */ +export const resolveObjectFromNodeId = ( + nodeId: string, + objects: EditorObject[], +): EditorObject | undefined => { + if (nodeId.startsWith("masked-")) { + const objectId = nodeId.slice("masked-".length); + return objects.find((obj) => obj.id === objectId); + } + return objects.find((obj) => obj.id === nodeId); +}; + +/** + * Base width/height used for transform baking. + * For images, Konva may use natural image size while store width is still unset. + */ +export const getNodeTransformDimensions = ( + node: Konva.Node, +): { width: number; height: number } => { + const nodeWidth = node.width(); + const nodeHeight = node.height(); + if (nodeWidth > 0 && nodeHeight > 0) { + return { width: nodeWidth, height: nodeHeight }; + } + + const imageNode = + "findOne" in node + ? (node as Konva.Container).findOne("Image") + : undefined; + if (imageNode && imageNode.width() > 0 && imageNode.height() > 0) { + return { width: imageNode.width(), height: imageNode.height() }; + } + + const rect = node.getClientRect({ skipTransform: true }); + if (rect.width > 0 && rect.height > 0) { + return { width: rect.width, height: rect.height }; + } + + return { width: 100, height: 100 }; +}; + +export const nodeHasPendingTransform = (node: Konva.Node): boolean => + Math.abs(node.scaleX() - 1) > 0.0001 || + Math.abs(node.scaleY() - 1) > 0.0001; diff --git a/src/pages/viewer/components/BookPage.tsx b/src/pages/viewer/components/BookPage.tsx index 529ee6c..6d39fa0 100644 --- a/src/pages/viewer/components/BookPage.tsx +++ b/src/pages/viewer/components/BookPage.tsx @@ -14,6 +14,28 @@ import { mergeEntranceAnimationStyle } from '@/pages/viewer/utils/entranceAnimat import { getMaskImageStyle, getMaskedLayout } from '@/pages/viewer/utils/maskStyle'; import type { EntrancePhase } from '@/pages/viewer/hooks/useBookEntranceController'; +const getRasterObjectStyle = ( + obj: EditorObject, + scale: number, + baseStyle: React.CSSProperties, + index: number, +): React.CSSProperties => { + const scaleX = obj.scaleX ?? 1; + const scaleY = obj.scaleY ?? 1; + const width = obj.width != null ? obj.width * scaleX * scale : undefined; + const height = obj.height != null ? obj.height * scaleY * scale : undefined; + + return { + ...baseStyle, + display: 'block', + maxWidth: 'none', + width: width != null ? `${width}px` : 'auto', + height: height != null ? `${height}px` : 'auto', + objectFit: 'fill', + zIndex: index, + }; +}; + type RenderObjectOptions = { skipEntrance?: boolean; }; @@ -200,19 +222,14 @@ const BookPage = forwardRef( case 'image': case 'sticker': + case 'document': return (