fix: clamp canvas image toolbar inside viewport
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 12:57:57 +08:00
parent d6149bd05d
commit 3569582b43

View File

@ -121,6 +121,7 @@ interface BoxCutoutPoint {
const BOX_CUTOUT_DEFAULT_PADDING_RATIO = 0.08;
const BOX_CUTOUT_MIN_EDGE_PX = 24;
const FLOATING_TOOLBAR_SCALE = 0.8;
const clampNumber = (value: number, min: number, max: number) =>
Math.min(max, Math.max(min, value));
@ -972,8 +973,10 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
let rafId: number | null = null;
const measure = () => {
const rect = toolbarEl.getBoundingClientRect();
const next = { width: rect.width, height: rect.height };
const next = {
width: toolbarEl.offsetWidth,
height: toolbarEl.offsetHeight,
};
setToolbarSize((prev) =>
Math.abs(prev.width - next.width) < 0.5 &&
Math.abs(prev.height - next.height) < 0.5
@ -1028,8 +1031,10 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
let rafId: number | null = null;
const measure = () => {
const rect = toolbarEl.getBoundingClientRect();
const next = { width: rect.width, height: rect.height };
const next = {
width: toolbarEl.offsetWidth,
height: toolbarEl.offsetHeight,
};
setBoxCutoutToolbarSize((prev) =>
Math.abs(prev.width - next.width) < 0.5 &&
Math.abs(prev.height - next.height) < 0.5
@ -1729,14 +1734,13 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
const getToolbarPosition = () => {
if (containerRef.current && typeof window !== "undefined") {
const rect = containerRef.current.getBoundingClientRect();
const toolbarScale = 0.8;
const toolbarWidth = Math.max(
280,
toolbarSize.width * toolbarScale || 980
(toolbarSize.width || 980) * FLOATING_TOOLBAR_SCALE
);
const toolbarHeight = Math.max(
42,
toolbarSize.height * toolbarScale || 48
(toolbarSize.height || 60) * FLOATING_TOOLBAR_SCALE
);
const titleSafeHeight = 34;
const gap = 12;
@ -1751,15 +1755,13 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
const maxToolbarWidth = Math.max(180, safeRight - safeLeft);
const effectiveToolbarWidth = Math.min(toolbarWidth, maxToolbarWidth);
const halfToolbar = effectiveToolbarWidth / 2;
let left = rect.left + rect.width / 2;
let left = rect.left + rect.width / 2 - effectiveToolbarWidth / 2;
if (effectiveToolbarWidth >= maxToolbarWidth - 1) {
left = (safeLeft + safeRight) / 2;
left = safeLeft;
} else {
left = Math.min(
Math.max(left, safeLeft + halfToolbar),
safeRight - halfToolbar
Math.max(left, safeLeft),
safeRight - effectiveToolbarWidth
);
}
@ -1775,14 +1777,13 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
const getBoxCutoutToolbarPosition = () => {
if (containerRef.current && typeof window !== "undefined") {
const rect = containerRef.current.getBoundingClientRect();
const toolbarScale = 0.8;
const toolbarWidth = Math.max(
240,
boxCutoutToolbarSize.width * toolbarScale || 460
(boxCutoutToolbarSize.width || 460) * FLOATING_TOOLBAR_SCALE
);
const toolbarHeight = Math.max(
42,
boxCutoutToolbarSize.height * toolbarScale || 48
(boxCutoutToolbarSize.height || 60) * FLOATING_TOOLBAR_SCALE
);
const gap = 14;
const { viewportPadding, safeBottom, safeLeft, safeRight, maxTop } =
@ -1796,15 +1797,13 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
const maxToolbarWidth = Math.max(180, safeRight - safeLeft);
const effectiveToolbarWidth = Math.min(toolbarWidth, maxToolbarWidth);
const halfToolbar = effectiveToolbarWidth / 2;
let left = rect.left + rect.width / 2;
let left = rect.left + rect.width / 2 - effectiveToolbarWidth / 2;
if (effectiveToolbarWidth >= maxToolbarWidth - 1) {
left = (safeLeft + safeRight) / 2;
left = safeLeft;
} else {
left = Math.min(
Math.max(left, safeLeft + halfToolbar),
safeRight - halfToolbar
Math.max(left, safeLeft),
safeRight - effectiveToolbarWidth
);
}
@ -3425,10 +3424,13 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
style={{
top: toolbarPosition.top,
left: toolbarPosition.left,
transform: "translateX(-50%) scale(0.8)",
transformOrigin: "top center",
transform: `scale(${FLOATING_TOOLBAR_SCALE})`,
transformOrigin: "top left",
pointerEvents: "auto",
maxWidth: `${Math.max(180, Math.floor(toolbarPosition.maxWidth))}px`,
maxWidth: `${Math.max(
180,
Math.floor(toolbarPosition.maxWidth / FLOATING_TOOLBAR_SCALE)
)}px`,
overflowX: "auto",
scrollbarWidth: "none",
}}
@ -3593,10 +3595,13 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
style={{
top: boxCutoutToolbarPosition.top,
left: boxCutoutToolbarPosition.left,
transform: "translateX(-50%) scale(0.8)",
transformOrigin: "top center",
transform: `scale(${FLOATING_TOOLBAR_SCALE})`,
transformOrigin: "top left",
pointerEvents: "auto",
maxWidth: `${Math.max(180, Math.floor(boxCutoutToolbarPosition.maxWidth))}px`,
maxWidth: `${Math.max(
180,
Math.floor(boxCutoutToolbarPosition.maxWidth / FLOATING_TOOLBAR_SCALE)
)}px`,
overflowX: "auto",
scrollbarWidth: "none",
}}