fix: scale canvas image controls with zoom
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-04-11 11:28:22 +08:00
parent 0d3d5a9507
commit 6cdf3d013e
4 changed files with 48 additions and 26 deletions

View File

@ -7792,7 +7792,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
const brandEntry = (
<div className="absolute top-4 left-4 z-20">
<Button
className="h-10 gap-2 rounded-none border-0 bg-transparent px-0 shadow-none hover:bg-transparent"
className="h-10 gap-2 rounded-full border-0 bg-background px-3 shadow-none hover:bg-background hover:text-foreground"
onClick={goHome}
variant="ghost"
>

View File

@ -1048,42 +1048,45 @@ export function CanvasWorkspace({
}
: isSelected && tool === "select";
const imageHandleSize = IMAGE_HANDLE_SIZE * viewport.zoom;
const imageHandleOffset = imageHandleSize / 2;
const imageSelectionStrokeWidth = SELECTION_STROKE_WIDTH * viewport.zoom;
const imageResizeHandleStyles = isImageNode
? {
topRight: {
width: IMAGE_HANDLE_SIZE,
height: IMAGE_HANDLE_SIZE,
right: -IMAGE_HANDLE_OFFSET,
top: -IMAGE_HANDLE_OFFSET,
width: imageHandleSize,
height: imageHandleSize,
right: -imageHandleOffset,
top: -imageHandleOffset,
borderRadius: 0,
border: `${SELECTION_STROKE_WIDTH}px solid ${SELECTION_COLOR}`,
border: `${imageSelectionStrokeWidth}px solid ${SELECTION_COLOR}`,
backgroundColor: "#ffffff",
},
bottomRight: {
width: IMAGE_HANDLE_SIZE,
height: IMAGE_HANDLE_SIZE,
right: -IMAGE_HANDLE_OFFSET,
bottom: -IMAGE_HANDLE_OFFSET,
width: imageHandleSize,
height: imageHandleSize,
right: -imageHandleOffset,
bottom: -imageHandleOffset,
borderRadius: 0,
border: `${SELECTION_STROKE_WIDTH}px solid ${SELECTION_COLOR}`,
border: `${imageSelectionStrokeWidth}px solid ${SELECTION_COLOR}`,
backgroundColor: "#ffffff",
},
bottomLeft: {
width: IMAGE_HANDLE_SIZE,
height: IMAGE_HANDLE_SIZE,
left: -IMAGE_HANDLE_OFFSET,
bottom: -IMAGE_HANDLE_OFFSET,
width: imageHandleSize,
height: imageHandleSize,
left: -imageHandleOffset,
bottom: -imageHandleOffset,
borderRadius: 0,
border: `${SELECTION_STROKE_WIDTH}px solid ${SELECTION_COLOR}`,
border: `${imageSelectionStrokeWidth}px solid ${SELECTION_COLOR}`,
backgroundColor: "#ffffff",
},
topLeft: {
width: IMAGE_HANDLE_SIZE,
height: IMAGE_HANDLE_SIZE,
left: -IMAGE_HANDLE_OFFSET,
top: -IMAGE_HANDLE_OFFSET,
width: imageHandleSize,
height: imageHandleSize,
left: -imageHandleOffset,
top: -imageHandleOffset,
borderRadius: 0,
border: `${SELECTION_STROKE_WIDTH}px solid ${SELECTION_COLOR}`,
border: `${imageSelectionStrokeWidth}px solid ${SELECTION_COLOR}`,
backgroundColor: "#ffffff",
},
}

View File

@ -362,9 +362,20 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
}),
[node.size.height, node.size.width]
);
const viewportZoom = useCanvasStore((state) => state.viewport.zoom);
const nodeContentScale = contentScale * viewportZoom;
const scaledContentStyle = useMemo(
() => getScaledNodeContentStyle(contentScale),
[contentScale]
() => getScaledNodeContentStyle(nodeContentScale, { allowUpscale: true }),
[nodeContentScale]
);
const selectedTitleStyle = useMemo<React.CSSProperties>(
() => ({
top: `${-28 * nodeContentScale}px`,
transform: `scale(${nodeContentScale})`,
transformOrigin: "bottom left",
width: `${100 / nodeContentScale}%`,
}),
[nodeContentScale]
);
const detailProgressLabel =
typeof data.metadata?.detailProgressLabel === "string"
@ -2825,7 +2836,10 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
<>
{/* 选中时在图片上方显示标题 */}
{isSelected && !isMultiSelected && data.src && !sceneFusionDialogOpen && (
<div className="-top-7 absolute right-0 left-0 z-10 flex items-center justify-between px-1 text-[#2f7df6] text-xs">
<div
className="-top-7 absolute right-0 left-0 z-10 flex items-center justify-between px-1 text-[#2f7df6] text-xs"
style={selectedTitleStyle}
>
<div className="flex min-w-0 items-center gap-1">
<ImagePlus className="h-3 w-3" />
{isEditingTitle ? (

View File

@ -18,9 +18,14 @@ export function getNodeContentScale(
}
export function getScaledNodeContentStyle(
scale: number
scale: number,
options: { allowUpscale?: boolean } = {}
): CSSProperties | undefined {
if (scale >= 0.999) {
const shouldKeepDefault = options.allowUpscale
? Math.abs(scale - 1) < 0.001
: scale >= 0.999;
if (shouldKeepDefault) {
return undefined;
}