Files
label-designer/src/components/ElementFloatPreview.tsx
2026-06-13 03:41:20 +08:00

74 lines
1.9 KiB
TypeScript

import React, { useState, useEffect, useMemo } from 'react';
import { TemplateElement } from '../types';
import { mmToPx } from '../utils';
import { renderLabelToDataUrl } from '../labelRenderer';
interface ElementFloatPreviewProps {
element: TemplateElement;
renderScale: number;
displayZoom: number;
variableDefaults?: Record<string, string> | null;
}
/** 单元素离屏绘制(不裁切到标签页边界),用于拖动/缩放浮层 */
export const ElementFloatPreview: React.FC<ElementFloatPreviewProps> = ({
element,
renderScale,
displayZoom,
variableDefaults = null,
}) => {
const [dataUrl, setDataUrl] = useState('');
const elementKey = useMemo(() => JSON.stringify(element), [element]);
useEffect(() => {
let active = true;
const normalized = { ...element, x: 0, y: 0 };
renderLabelToDataUrl({
widthMm: element.width,
heightMm: element.height,
elements: [normalized],
rowData: null,
rowIndex: 0,
showBorder: false,
transparentBackground: true,
variableDefaults,
mode: 'design',
scale: renderScale,
})
.then((url) => {
if (active) setDataUrl(url);
})
.catch((err) => {
console.error('Element float preview error:', err);
});
return () => {
active = false;
};
}, [element, elementKey, renderScale, variableDefaults]);
const left = mmToPx(element.x, displayZoom);
const top = mmToPx(element.y, displayZoom);
const width = mmToPx(element.width, displayZoom);
const height = mmToPx(element.height, displayZoom);
if (!dataUrl) return null;
return (
<img
src={dataUrl}
alt=""
className="absolute pointer-events-none select-none"
style={{
left: `${left}px`,
top: `${top}px`,
width: `${width}px`,
height: `${height}px`,
imageRendering: 'auto',
}}
/>
);
};