74 lines
1.9 KiB
TypeScript
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',
|
|
}}
|
|
/>
|
|
);
|
|
};
|