fix: stabilize psd text preview positioning
This commit is contained in:
parent
9b745e5591
commit
d450bec1d7
@ -821,31 +821,47 @@ function getTextLayerRenderFrame(
|
||||
};
|
||||
}
|
||||
|
||||
function getTextLayerContentStyle(
|
||||
function renderTextLayerPreviewContent(
|
||||
layer: AgentPsdTemplateTextLayerSlot,
|
||||
value: string,
|
||||
color: string
|
||||
): CSSProperties {
|
||||
const resolvedLayout = getTextLayerRenderFrame(layer, value);
|
||||
) {
|
||||
const resolvedText = getResolvedTextPreview(layer, value);
|
||||
const resolvedLayout = getTextLayerRenderFrame(layer, resolvedText);
|
||||
|
||||
return {
|
||||
width: resolvedLayout.width,
|
||||
height: resolvedLayout.height,
|
||||
fontFamily: buildTextLayerFontFamily(layer),
|
||||
fontSize: resolvedLayout.fontSize,
|
||||
lineHeight: layer.lineHeight,
|
||||
letterSpacing: `${layer.letterSpacing}px`,
|
||||
color,
|
||||
fontWeight: layer.fontWeight,
|
||||
textAlign: layer.textAlign,
|
||||
paddingLeft: layer.paddingX,
|
||||
paddingRight: layer.paddingX,
|
||||
paddingTop: layer.paddingY,
|
||||
paddingBottom: layer.paddingY,
|
||||
whiteSpace: "pre",
|
||||
wordBreak: "keep-all",
|
||||
overflowWrap: "normal",
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className="relative overflow-visible"
|
||||
style={{
|
||||
width: resolvedLayout.width,
|
||||
height: resolvedLayout.height,
|
||||
}}
|
||||
>
|
||||
{resolvedLayout.lines.map((line, index) => (
|
||||
<div
|
||||
className="absolute overflow-visible whitespace-pre"
|
||||
key={`${layer.id}-preview-line-${index + 1}`}
|
||||
style={{
|
||||
top: layer.paddingY + index * resolvedLayout.lineHeightPx,
|
||||
width: resolvedLayout.width,
|
||||
paddingLeft: layer.paddingX,
|
||||
paddingRight: layer.paddingX,
|
||||
fontFamily: buildTextLayerFontFamily(layer),
|
||||
fontSize: resolvedLayout.fontSize,
|
||||
lineHeight: `${resolvedLayout.fontSize}px`,
|
||||
letterSpacing: `${layer.letterSpacing}px`,
|
||||
color,
|
||||
fontWeight: layer.fontWeight,
|
||||
textAlign: layer.textAlign,
|
||||
wordBreak: "keep-all",
|
||||
overflowWrap: "normal",
|
||||
}}
|
||||
>
|
||||
{line || "\u00a0"}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getAssignedImageMaskStyle(
|
||||
@ -916,24 +932,6 @@ function hasCustomizedTextLayerAppearance(
|
||||
);
|
||||
}
|
||||
|
||||
function getInlineTextEditorStyle(
|
||||
layer: AgentPsdTemplateTextLayerSlot,
|
||||
preserveSourceAppearance: boolean,
|
||||
value: string,
|
||||
color: string
|
||||
): CSSProperties {
|
||||
const baseStyle = getTextLayerContentStyle(layer, value, color);
|
||||
return {
|
||||
...baseStyle,
|
||||
background: "transparent",
|
||||
boxSizing: "border-box",
|
||||
overflow: "visible",
|
||||
color: preserveSourceAppearance ? "transparent" : color,
|
||||
caretColor: color,
|
||||
WebkitTextFillColor: preserveSourceAppearance ? "transparent" : color,
|
||||
};
|
||||
}
|
||||
|
||||
function getTextLayerNameBadgeClassName() {
|
||||
return "pointer-events-none absolute left-0 top-0 -translate-y-[calc(100%+6px)] rounded-full bg-black/70 px-2 py-1 text-[11px] whitespace-nowrap text-white shadow-lg backdrop-blur-md";
|
||||
}
|
||||
@ -1059,7 +1057,6 @@ export function AgentPsdTemplateEditorDialog({
|
||||
|
||||
const previewViewportRef = useRef<HTMLDivElement | null>(null);
|
||||
const uploadInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const inlineTextEditorRef = useRef<HTMLTextAreaElement | null>(null);
|
||||
const previousSelectedTemplateRef = useRef<AgentPsdTemplate | null>(null);
|
||||
const imageDragStateRef = useRef<{
|
||||
layerId: string;
|
||||
@ -1735,45 +1732,6 @@ export function AgentPsdTemplateEditorDialog({
|
||||
};
|
||||
}, [selectedTemplate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedTextLayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const textarea = inlineTextEditorRef.current;
|
||||
if (!textarea) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focusTextarea = () => {
|
||||
textarea.focus();
|
||||
const valueLength = textarea.value.length;
|
||||
try {
|
||||
textarea.setSelectionRange(valueLength, valueLength);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[AgentPsdTemplateEditorDialog] setSelectionRange failed:",
|
||||
error
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (
|
||||
typeof window !== "undefined" &&
|
||||
typeof window.requestAnimationFrame === "function"
|
||||
) {
|
||||
const frameId = window.requestAnimationFrame(focusTextarea);
|
||||
return () => {
|
||||
window.cancelAnimationFrame(frameId);
|
||||
};
|
||||
}
|
||||
|
||||
const timeoutId = window.setTimeout(focusTextarea, 0);
|
||||
return () => {
|
||||
window.clearTimeout(timeoutId);
|
||||
};
|
||||
}, [selectedTextLayer?.id]);
|
||||
|
||||
async function handleSlotUpload(
|
||||
slotId: string,
|
||||
event: ChangeEvent<HTMLInputElement>
|
||||
@ -2490,16 +2448,11 @@ export function AgentPsdTemplateEditorDialog({
|
||||
height: textFrame.height,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="whitespace-pre"
|
||||
style={getTextLayerContentStyle(
|
||||
layer,
|
||||
value || layer.placeholder || layer.layerName,
|
||||
resolvedTextColor
|
||||
)}
|
||||
>
|
||||
{value || layer.placeholder || layer.layerName}
|
||||
</div>
|
||||
{renderTextLayerPreviewContent(
|
||||
layer,
|
||||
value || layer.placeholder || layer.layerName,
|
||||
resolvedTextColor
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@ -2673,43 +2626,31 @@ export function AgentPsdTemplateEditorDialog({
|
||||
src={layer.sourceUrl}
|
||||
/>
|
||||
) : null}
|
||||
<div
|
||||
className="absolute"
|
||||
style={
|
||||
preserveSourceAppearance
|
||||
? { inset: 0 }
|
||||
: {
|
||||
left: editedTextFrame.offsetX,
|
||||
top: editedTextFrame.offsetY,
|
||||
width: editedTextFrame.width,
|
||||
height: editedTextFrame.height,
|
||||
}
|
||||
}
|
||||
>
|
||||
<textarea
|
||||
className="resize-none border-none bg-transparent outline-none"
|
||||
onChange={(event) =>
|
||||
setTextAssignments((current) => ({
|
||||
...current,
|
||||
[layer.id]: event.target.value,
|
||||
}))
|
||||
}
|
||||
ref={inlineTextEditorRef}
|
||||
style={getInlineTextEditorStyle(
|
||||
{preserveSourceAppearance ? null : (
|
||||
<div
|
||||
className="absolute"
|
||||
style={{
|
||||
left: editedTextFrame.offsetX,
|
||||
top: editedTextFrame.offsetY,
|
||||
width: editedTextFrame.width,
|
||||
height: editedTextFrame.height,
|
||||
}}
|
||||
>
|
||||
{renderTextLayerPreviewContent(
|
||||
layer,
|
||||
preserveSourceAppearance,
|
||||
currentValue,
|
||||
resolvedTextColor
|
||||
)}
|
||||
value={currentValue}
|
||||
wrap="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={getTextLayerNameBadgeClassName()}
|
||||
>
|
||||
{layer.layerName}
|
||||
</div>
|
||||
<div className={getTextLayerHintBadgeClassName()}>
|
||||
请在右侧面板编辑
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user