优化
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { TemplateElement } from '../types';
|
||||
import { mmToPx } from '../utils';
|
||||
import { renderLabelToDataUrl } from '../labelRenderer';
|
||||
@@ -8,6 +8,28 @@ interface ElementFloatPreviewProps {
|
||||
renderScale: number;
|
||||
displayZoom: number;
|
||||
variableDefaults?: Record<string, string> | null;
|
||||
/** 已离屏绘制的位图,传入后不再重复渲染 */
|
||||
dataUrl?: string;
|
||||
}
|
||||
|
||||
export async function renderElementFloatPreview(
|
||||
element: TemplateElement,
|
||||
renderScale: number,
|
||||
variableDefaults?: Record<string, string> | null
|
||||
): Promise<string> {
|
||||
return renderLabelToDataUrl({
|
||||
widthMm: element.width,
|
||||
heightMm: element.height,
|
||||
elements: [{ ...element, x: 0, y: 0 }],
|
||||
rowData: null,
|
||||
rowIndex: 0,
|
||||
showBorder: false,
|
||||
transparentBackground: true,
|
||||
variableDefaults,
|
||||
mode: 'design',
|
||||
scale: renderScale,
|
||||
clipToCanvas: false,
|
||||
});
|
||||
}
|
||||
|
||||
/** 单元素离屏绘制(不裁切到标签页边界),用于拖动/缩放浮层 */
|
||||
@@ -16,27 +38,18 @@ export const ElementFloatPreview: React.FC<ElementFloatPreviewProps> = ({
|
||||
renderScale,
|
||||
displayZoom,
|
||||
variableDefaults = null,
|
||||
dataUrl: dataUrlProp,
|
||||
}) => {
|
||||
const [dataUrl, setDataUrl] = useState('');
|
||||
|
||||
const elementKey = useMemo(() => JSON.stringify(element), [element]);
|
||||
const [dataUrl, setDataUrl] = useState(dataUrlProp ?? '');
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
const normalized = { ...element, x: 0, y: 0 };
|
||||
if (dataUrlProp) {
|
||||
setDataUrl(dataUrlProp);
|
||||
return;
|
||||
}
|
||||
|
||||
renderLabelToDataUrl({
|
||||
widthMm: element.width,
|
||||
heightMm: element.height,
|
||||
elements: [normalized],
|
||||
rowData: null,
|
||||
rowIndex: 0,
|
||||
showBorder: false,
|
||||
transparentBackground: true,
|
||||
variableDefaults,
|
||||
mode: 'design',
|
||||
scale: renderScale,
|
||||
})
|
||||
let active = true;
|
||||
renderElementFloatPreview(element, renderScale, variableDefaults)
|
||||
.then((url) => {
|
||||
if (active) setDataUrl(url);
|
||||
})
|
||||
@@ -47,7 +60,7 @@ export const ElementFloatPreview: React.FC<ElementFloatPreviewProps> = ({
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [element, elementKey, renderScale, variableDefaults]);
|
||||
}, [element, renderScale, variableDefaults, dataUrlProp]);
|
||||
|
||||
const left = mmToPx(element.x, displayZoom);
|
||||
const top = mmToPx(element.y, displayZoom);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { LabelTemplate, TemplateElement } from '../types';
|
||||
import { mmToPx, MM_TO_PX, shouldRenderElement } from '../utils';
|
||||
import { useIsNarrowScreen } from '../hooks/useIsMobile';
|
||||
import { CanvasLabelImage } from './CanvasLabelImage';
|
||||
import { ElementFloatPreview } from './ElementFloatPreview';
|
||||
import { ElementFloatPreview, renderElementFloatPreview } from './ElementFloatPreview';
|
||||
import { renderLabelToDataUrl } from '../labelRenderer';
|
||||
import { useAppDialog } from './AppDialog';
|
||||
import {
|
||||
ZoomIn,
|
||||
@@ -76,6 +78,9 @@ function applyCornerResize(
|
||||
|
||||
const MARQUEE_MIN_PX = 4;
|
||||
const DRAG_START_THRESHOLD_PX = 4;
|
||||
/** 松手前极短时间内的微小位移视为抖动,提交上一稳定位置 */
|
||||
const DRAG_RELEASE_JITTER_PX = 4;
|
||||
const DRAG_RELEASE_JITTER_MS = 48;
|
||||
const MODERATE_ZOOM_MAX = 2.0;
|
||||
|
||||
function blurActiveField() {
|
||||
@@ -219,7 +224,21 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
>(null);
|
||||
const dragRafRef = useRef<number | null>(null);
|
||||
const pendingDragElementsRef = useRef<TemplateElement[] | null>(null);
|
||||
const pendingDragDeltaRef = useRef({ x: 0, y: 0 });
|
||||
type DragCommitSnapshot = {
|
||||
elements: TemplateElement[];
|
||||
delta: { x: number; y: number };
|
||||
time: number;
|
||||
};
|
||||
const dragCommitHistoryRef = useRef<DragCommitSnapshot | null>(null);
|
||||
const dragCommitHistoryPrevRef = useRef<DragCommitSnapshot | null>(null);
|
||||
const dragFloatLayerRef = useRef<HTMLDivElement | null>(null);
|
||||
const canvasWrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
const interactionPortalRef = useRef<HTMLDivElement | null>(null);
|
||||
const dragFloatImgRefs = useRef<Map<string, HTMLImageElement>>(new Map());
|
||||
const dragFloatUrlsRef = useRef<Record<string, string>>({});
|
||||
const dragTransformLoopRef = useRef<number | null>(null);
|
||||
const [dragFloatElements, setDragFloatElements] = useState<TemplateElement[]>([]);
|
||||
const interactionFloatPaintRef = useRef(0);
|
||||
const interactionFloatTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
@@ -487,8 +506,18 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
if (dragFloatLayerRef.current) {
|
||||
dragFloatLayerRef.current.style.transform = '';
|
||||
}
|
||||
if (dragTransformLoopRef.current !== null) {
|
||||
cancelAnimationFrame(dragTransformLoopRef.current);
|
||||
dragTransformLoopRef.current = null;
|
||||
}
|
||||
setInteractionSnapshot(null);
|
||||
setInteractionFloatElements(null);
|
||||
setDragFloatElements([]);
|
||||
dragFloatImgRefs.current.clear();
|
||||
dragFloatUrlsRef.current = {};
|
||||
pendingDragDeltaRef.current = { x: 0, y: 0 };
|
||||
dragCommitHistoryRef.current = null;
|
||||
dragCommitHistoryPrevRef.current = null;
|
||||
}, []);
|
||||
|
||||
const cancelActiveInteraction = useCallback(() => {
|
||||
@@ -712,11 +741,45 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
if (!dragState) return;
|
||||
dragMovedRef.current = false;
|
||||
|
||||
const syncInteractionPortalLayout = () => {
|
||||
const wrapper = canvasWrapperRef.current;
|
||||
const portal = interactionPortalRef.current;
|
||||
if (wrapper && portal) {
|
||||
const rect = wrapper.getBoundingClientRect();
|
||||
portal.style.left = `${rect.left}px`;
|
||||
portal.style.top = `${rect.top}px`;
|
||||
portal.style.width = `${rect.width}px`;
|
||||
portal.style.height = `${rect.height}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const runDragTransformLoop = () => {
|
||||
const tick = () => {
|
||||
syncInteractionPortalLayout();
|
||||
const layer = dragFloatLayerRef.current;
|
||||
if (layer) {
|
||||
const { x, y } = pendingDragDeltaRef.current;
|
||||
layer.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
||||
}
|
||||
dragTransformLoopRef.current = requestAnimationFrame(tick);
|
||||
};
|
||||
dragTransformLoopRef.current = requestAnimationFrame(tick);
|
||||
};
|
||||
|
||||
if (dragState.type === 'drag') {
|
||||
runDragTransformLoop();
|
||||
}
|
||||
|
||||
const handlePointerMove = (e: PointerEvent) => {
|
||||
if (multiTouchActiveRef.current) return;
|
||||
|
||||
const deltaX = e.clientX - dragState.startX;
|
||||
const deltaY = e.clientY - dragState.startY;
|
||||
|
||||
if (dragState.type === 'drag') {
|
||||
pendingDragDeltaRef.current = { x: deltaX, y: deltaY };
|
||||
}
|
||||
|
||||
const pastDragThreshold =
|
||||
Math.abs(deltaX) > DRAG_START_THRESHOLD_PX || Math.abs(deltaY) > DRAG_START_THRESHOLD_PX;
|
||||
|
||||
@@ -744,18 +807,12 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
};
|
||||
});
|
||||
pendingDragElementsRef.current = updated;
|
||||
if (dragFloatLayerRef.current) {
|
||||
dragFloatLayerRef.current.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
|
||||
}
|
||||
if (dragRafRef.current === null) {
|
||||
dragRafRef.current = requestAnimationFrame(() => {
|
||||
dragRafRef.current = null;
|
||||
const next = pendingDragElementsRef.current;
|
||||
if (next) {
|
||||
localElementsRef.current = next;
|
||||
}
|
||||
});
|
||||
}
|
||||
dragCommitHistoryPrevRef.current = dragCommitHistoryRef.current;
|
||||
dragCommitHistoryRef.current = {
|
||||
elements: updated,
|
||||
delta: { x: deltaX, y: deltaY },
|
||||
time: performance.now(),
|
||||
};
|
||||
} else if (dragState.type === 'resize') {
|
||||
const elem = template.elements.find((el) => el.id === dragState.elementId);
|
||||
if (!elem || elem.locked) return;
|
||||
@@ -821,6 +878,23 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const resolveDragCommitElements = (): TemplateElement[] | null => {
|
||||
if (dragState.type !== 'drag') {
|
||||
return pendingDragElementsRef.current ?? localElementsRef.current;
|
||||
}
|
||||
const cur = dragCommitHistoryRef.current;
|
||||
const prev = dragCommitHistoryPrevRef.current;
|
||||
if (cur && prev) {
|
||||
const micro = Math.hypot(cur.delta.x - prev.delta.x, cur.delta.y - prev.delta.y);
|
||||
const dt = cur.time - prev.time;
|
||||
if (micro > 0 && micro <= DRAG_RELEASE_JITTER_PX && dt <= DRAG_RELEASE_JITTER_MS) {
|
||||
return prev.elements;
|
||||
}
|
||||
return cur.elements;
|
||||
}
|
||||
return cur?.elements ?? pendingDragElementsRef.current ?? localElementsRef.current;
|
||||
};
|
||||
|
||||
const handlePointerUp = () => {
|
||||
if (multiTouchActiveRef.current) {
|
||||
cancelActiveInteraction();
|
||||
@@ -830,10 +904,11 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
cancelAnimationFrame(dragRafRef.current);
|
||||
dragRafRef.current = null;
|
||||
}
|
||||
if (pendingDragElementsRef.current) {
|
||||
localElementsRef.current = pendingDragElementsRef.current;
|
||||
pendingDragElementsRef.current = null;
|
||||
const commitElements = resolveDragCommitElements();
|
||||
if (commitElements) {
|
||||
localElementsRef.current = commitElements;
|
||||
}
|
||||
pendingDragElementsRef.current = null;
|
||||
if (localElementsRef.current) {
|
||||
const shouldCommit =
|
||||
dragState.type === 'resize' || dragMovedRef.current;
|
||||
@@ -850,6 +925,10 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
window.addEventListener('pointercancel', handlePointerUp);
|
||||
return () => {
|
||||
if (dragTransformLoopRef.current !== null) {
|
||||
cancelAnimationFrame(dragTransformLoopRef.current);
|
||||
dragTransformLoopRef.current = null;
|
||||
}
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
window.removeEventListener('pointercancel', handlePointerUp);
|
||||
@@ -954,9 +1033,22 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
if (element.locked) return;
|
||||
|
||||
e.currentTarget.setPointerCapture(e.pointerId);
|
||||
const groupStartPositions = buildGroupStartPositions(element);
|
||||
const moveIds = new Set(Object.keys(groupStartPositions));
|
||||
const floating = template.elements.filter(
|
||||
(el) =>
|
||||
moveIds.has(el.id) &&
|
||||
shouldRenderElement(el, null, 0, previewDefaults, 'design')
|
||||
);
|
||||
|
||||
setInteractionSnapshot(template.elements);
|
||||
setInteractionFloatElements(null);
|
||||
interactionFloatPaintRef.current = 0;
|
||||
dragFloatUrlsRef.current = {};
|
||||
dragFloatImgRefs.current.clear();
|
||||
dragCommitHistoryRef.current = null;
|
||||
dragCommitHistoryPrevRef.current = null;
|
||||
setDragFloatElements(floating);
|
||||
setDragState({
|
||||
elementId: element.id,
|
||||
type: actionType,
|
||||
@@ -966,8 +1058,34 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
startElemY: element.y,
|
||||
startElemW: element.width,
|
||||
startElemH: element.height,
|
||||
groupStartPositions: buildGroupStartPositions(element),
|
||||
groupStartPositions,
|
||||
});
|
||||
|
||||
if (actionType === 'drag' && floating.length > 0) {
|
||||
void Promise.all(
|
||||
floating.map(async (el) => {
|
||||
try {
|
||||
const url = await renderElementFloatPreview(
|
||||
el,
|
||||
canvasRenderScale,
|
||||
previewDefaults
|
||||
);
|
||||
dragFloatUrlsRef.current[el.id] = url;
|
||||
const img = dragFloatImgRefs.current.get(el.id);
|
||||
if (img) {
|
||||
img.src = url;
|
||||
if (img.complete) {
|
||||
img.style.opacity = '1';
|
||||
const placeholder = img.previousElementSibling as HTMLElement | null;
|
||||
if (placeholder) placeholder.style.display = 'none';
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Drag float preview error:', err);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResizeStart = (
|
||||
@@ -1052,23 +1170,40 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
filterVisibleElements,
|
||||
]);
|
||||
|
||||
const dragFloatElements = useMemo(() => {
|
||||
if (dragState?.type !== 'drag' || !interactionSnapshot || !dragMovingIdSet) {
|
||||
return null;
|
||||
}
|
||||
const moving = interactionSnapshot.filter((el) => dragMovingIdSet.has(el.id));
|
||||
return moving.length > 0 ? filterVisibleElements(moving) : null;
|
||||
}, [dragState?.type, interactionSnapshot, dragMovingIdSet, filterVisibleElements]);
|
||||
|
||||
const resizeFloatElements = useMemo(() => {
|
||||
if (dragState?.type !== 'resize' || !interactionFloatElements?.length) return null;
|
||||
return filterVisibleElements(interactionFloatElements);
|
||||
}, [dragState?.type, interactionFloatElements, filterVisibleElements]);
|
||||
|
||||
const interactionFloatCanvasElements = dragFloatElements ?? resizeFloatElements;
|
||||
const allowInteractionOverflow = dragState !== null;
|
||||
|
||||
const hideSelectionChrome = suppressSelectionChrome || dragState !== null;
|
||||
const syncInteractionPortalLayout = useCallback(() => {
|
||||
const wrapper = canvasWrapperRef.current;
|
||||
const portal = interactionPortalRef.current;
|
||||
if (!wrapper || !portal) return;
|
||||
const rect = wrapper.getBoundingClientRect();
|
||||
portal.style.left = `${rect.left}px`;
|
||||
portal.style.top = `${rect.top}px`;
|
||||
portal.style.width = `${rect.width}px`;
|
||||
portal.style.height = `${rect.height}px`;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!allowInteractionOverflow) return;
|
||||
syncInteractionPortalLayout();
|
||||
window.addEventListener('scroll', syncInteractionPortalLayout, true);
|
||||
window.addEventListener('resize', syncInteractionPortalLayout);
|
||||
return () => {
|
||||
window.removeEventListener('scroll', syncInteractionPortalLayout, true);
|
||||
window.removeEventListener('resize', syncInteractionPortalLayout);
|
||||
};
|
||||
}, [allowInteractionOverflow, syncInteractionPortalLayout]);
|
||||
|
||||
const hideSelectionChrome = suppressSelectionChrome || dragState?.type === 'drag';
|
||||
const resizingPreviewElement =
|
||||
dragState?.type === 'resize'
|
||||
? interactionFloatElements?.find((el) => el.id === dragState.elementId)
|
||||
: null;
|
||||
|
||||
const overlayElements = filterVisibleElements(template.elements);
|
||||
|
||||
@@ -1152,7 +1287,103 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
</div>
|
||||
);
|
||||
|
||||
const showInteractionFloatPortal =
|
||||
typeof document !== 'undefined' &&
|
||||
((dragState?.type === 'drag' && dragFloatElements.length > 0) ||
|
||||
(resizeFloatElements && resizeFloatElements.length > 0));
|
||||
|
||||
const interactionFloatPortal = showInteractionFloatPortal
|
||||
? createPortal(
|
||||
<div
|
||||
ref={interactionPortalRef}
|
||||
className="pointer-events-none"
|
||||
style={{
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
overflow: 'visible',
|
||||
zIndex: 10000,
|
||||
}}
|
||||
>
|
||||
{dragState?.type === 'drag' && dragFloatElements.length > 0 && (
|
||||
<div
|
||||
ref={dragFloatLayerRef}
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{
|
||||
overflow: 'visible',
|
||||
willChange: 'transform',
|
||||
}}
|
||||
>
|
||||
{dragFloatElements.map((el) => (
|
||||
<div
|
||||
key={el.id}
|
||||
className="absolute pointer-events-none"
|
||||
style={{
|
||||
left: `${mmToPx(el.x, displayZoom)}px`,
|
||||
top: `${mmToPx(el.y, displayZoom)}px`,
|
||||
width: `${mmToPx(el.width, displayZoom)}px`,
|
||||
height: `${mmToPx(el.height, displayZoom)}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
data-drag-float-placeholder={el.id}
|
||||
className="absolute inset-0 bg-white border border-[#31a8ff]/40 shadow-[0_1px_4px_rgba(0,0,0,0.12)]"
|
||||
/>
|
||||
<img
|
||||
ref={(node) => {
|
||||
if (node) {
|
||||
dragFloatImgRefs.current.set(el.id, node);
|
||||
const url = dragFloatUrlsRef.current[el.id];
|
||||
if (url) {
|
||||
node.src = url;
|
||||
if (node.complete) {
|
||||
node.style.opacity = '1';
|
||||
const placeholder = node.previousElementSibling as HTMLElement | null;
|
||||
if (placeholder) placeholder.style.display = 'none';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dragFloatImgRefs.current.delete(el.id);
|
||||
}
|
||||
}}
|
||||
alt=""
|
||||
className="absolute inset-0 w-full h-full pointer-events-none select-none opacity-0"
|
||||
onLoad={(e) => {
|
||||
const img = e.currentTarget;
|
||||
img.style.opacity = '1';
|
||||
const placeholder = img.previousElementSibling as HTMLElement | null;
|
||||
if (placeholder) placeholder.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{resizeFloatElements && resizeFloatElements.length > 0 && (
|
||||
<div
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{ overflow: 'visible' }}
|
||||
>
|
||||
{resizeFloatElements.map((el) => (
|
||||
<ElementFloatPreview
|
||||
key={el.id}
|
||||
element={el}
|
||||
renderScale={canvasRenderScale}
|
||||
displayZoom={displayZoom}
|
||||
variableDefaults={previewDefaults}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>,
|
||||
document.body
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
isMobileLayout
|
||||
@@ -1189,6 +1420,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={canvasWrapperRef}
|
||||
className="relative shrink-0 grow-0"
|
||||
style={{
|
||||
width: `${canvasDisplayW}px`,
|
||||
@@ -1252,17 +1484,22 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
dragState?.type === 'drag' && !!dragMovingIdSet?.has(element.id);
|
||||
const isBeingResized =
|
||||
dragState?.type === 'resize' && dragState.elementId === element.id;
|
||||
if (isBeingDragged || isBeingResized) {
|
||||
if (isBeingDragged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const overlayElement =
|
||||
isBeingResized && resizingPreviewElement ? resizingPreviewElement : element;
|
||||
|
||||
const showSelectionRing = isSelected && !hideSelectionChrome;
|
||||
const showResizeHandles =
|
||||
showSelectionRing && selectedElementIds.length === 1;
|
||||
const elemX = mmToPx(element.x, displayZoom);
|
||||
const elemY = mmToPx(element.y, displayZoom);
|
||||
const elemW = mmToPx(element.width, displayZoom);
|
||||
const elemH = mmToPx(element.height, displayZoom);
|
||||
showSelectionRing &&
|
||||
selectedElementIds.length === 1 &&
|
||||
dragState?.type !== 'resize';
|
||||
const elemX = mmToPx(overlayElement.x, displayZoom);
|
||||
const elemY = mmToPx(overlayElement.y, displayZoom);
|
||||
const elemW = mmToPx(overlayElement.width, displayZoom);
|
||||
const elemH = mmToPx(overlayElement.height, displayZoom);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -1282,12 +1519,21 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
top: `${elemY}px`,
|
||||
width: `${elemW}px`,
|
||||
height: `${elemH}px`,
|
||||
zIndex: isSelected ? index + 1000 : index + 10,
|
||||
transform: element.rotation ? `rotate(${element.rotation}deg)` : undefined,
|
||||
zIndex: isBeingResized
|
||||
? index + 2000
|
||||
: isSelected
|
||||
? index + 1000
|
||||
: index + 10,
|
||||
transform: overlayElement.rotation
|
||||
? `rotate(${overlayElement.rotation}deg)`
|
||||
: undefined,
|
||||
transformOrigin: 'center center',
|
||||
pointerEvents: isBeingResized ? 'none' : undefined,
|
||||
}}
|
||||
onPointerDown={
|
||||
isLocked ? undefined : (e) => handleElementPointerDown(e, element, 'drag')
|
||||
isLocked || isBeingResized
|
||||
? undefined
|
||||
: (e) => handleElementPointerDown(e, element, 'drag')
|
||||
}
|
||||
onClick={isLocked ? undefined : (e) => handleElementClick(e, element)}
|
||||
>
|
||||
@@ -1336,28 +1582,6 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
})}
|
||||
</div>
|
||||
|
||||
{interactionFloatCanvasElements && interactionFloatCanvasElements.length > 0 && (
|
||||
<div
|
||||
ref={dragFloatLayerRef}
|
||||
className="absolute top-0 left-0 pointer-events-none z-[10]"
|
||||
style={{
|
||||
width: `${canvasDisplayW}px`,
|
||||
height: `${canvasDisplayH}px`,
|
||||
overflow: 'visible',
|
||||
willChange: dragState?.type === 'drag' ? 'transform' : undefined,
|
||||
}}
|
||||
>
|
||||
{interactionFloatCanvasElements.map((el) => (
|
||||
<ElementFloatPreview
|
||||
key={el.id}
|
||||
element={el}
|
||||
renderScale={canvasRenderScale}
|
||||
displayZoom={displayZoom}
|
||||
variableDefaults={previewDefaults}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1420,5 +1644,7 @@ export const LabelDesigner: React.FC<LabelDesignerProps> = ({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{interactionFloatPortal}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -153,7 +153,7 @@ const PropTextarea: React.FC<PropTextareaProps> = ({ value, onCommit, ...props }
|
||||
|
||||
export type MobilePropModule = 'content' | 'properties' | 'layers' | 'nudge';
|
||||
|
||||
const MOBILE_NUDGE_STEP_MM = 0.5;
|
||||
const MOBILE_NUDGE_STEP_MM = 0.1;
|
||||
|
||||
interface PropSidebarProps {
|
||||
template: LabelTemplate;
|
||||
|
||||
Reference in New Issue
Block a user