(selector));
- for (const element of elements) {
- const style = window.getComputedStyle(element);
- if (style.display === "none" || style.visibility === "hidden") continue;
-
- const overlayRect = element.getBoundingClientRect();
- if (overlayRect.width <= 0 || overlayRect.height <= 0) continue;
- if (overlayRect.bottom <= 0 || overlayRect.top >= window.innerHeight) continue;
- if (overlayRect.top < window.innerHeight * 0.55) continue;
-
- unsafeBottomTop = Math.min(unsafeBottomTop, overlayRect.top);
- }
- }
-
- const safeBottom = Math.max(
- viewportPadding,
- Math.min(window.innerHeight - viewportPadding, unsafeBottomTop - viewportPadding)
- );
- const maxTop = Math.max(viewportPadding, safeBottom - toolbarHeight);
+ const { viewportPadding, maxTop, safeLeft, safeRight } = getFloatingViewportBounds(toolbarHeight);
let top = rect.top - toolbarHeight - titleSafeHeight - gap;
if (top < viewportPadding) {
@@ -1365,29 +1474,6 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
}
top = Math.min(Math.max(top, viewportPadding), maxTop);
- const stageElement = document.querySelector('[data-canvas-stage="true"]') as HTMLElement | null;
- const panelElement = document.querySelector('[data-canvas-right-panel="true"]') as HTMLElement | null;
-
- let safeLeft = viewportPadding;
- let safeRight = window.innerWidth - viewportPadding;
- if (stageElement) {
- const stageRect = stageElement.getBoundingClientRect();
- if (stageRect.width > 0) {
- safeLeft = Math.max(safeLeft, stageRect.left + viewportPadding);
- safeRight = Math.min(safeRight, stageRect.right - viewportPadding);
- }
- }
- if (panelElement) {
- const panelRect = panelElement.getBoundingClientRect();
- if (panelRect.width > 0 && panelRect.left < window.innerWidth) {
- safeRight = Math.min(safeRight, panelRect.left - viewportPadding);
- }
- }
- if (safeRight <= safeLeft + 8) {
- safeLeft = viewportPadding;
- safeRight = window.innerWidth - viewportPadding;
- }
-
const maxToolbarWidth = Math.max(180, safeRight - safeLeft);
const effectiveToolbarWidth = Math.min(toolbarWidth, maxToolbarWidth);
const halfToolbar = effectiveToolbarWidth / 2;
@@ -1396,10 +1482,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
if (effectiveToolbarWidth >= maxToolbarWidth - 1) {
left = (safeLeft + safeRight) / 2;
} else {
- left = Math.min(
- Math.max(left, safeLeft + halfToolbar),
- safeRight - halfToolbar
- );
+ left = Math.min(Math.max(left, safeLeft + halfToolbar), safeRight - halfToolbar);
}
return {
@@ -1411,6 +1494,42 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
return { top: 0, left: 0, maxWidth: 480 };
};
+ 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);
+ const toolbarHeight = Math.max(42, boxCutoutToolbarSize.height * toolbarScale || 48);
+ const gap = 14;
+ const { viewportPadding, safeBottom, safeLeft, safeRight, maxTop } =
+ getFloatingViewportBounds(toolbarHeight);
+
+ let top = rect.bottom + gap;
+ if (top + toolbarHeight > safeBottom) {
+ top = rect.top - toolbarHeight - gap;
+ }
+ top = Math.min(Math.max(top, viewportPadding), maxTop);
+
+ const maxToolbarWidth = Math.max(180, safeRight - safeLeft);
+ const effectiveToolbarWidth = Math.min(toolbarWidth, maxToolbarWidth);
+ const halfToolbar = effectiveToolbarWidth / 2;
+
+ let left = rect.left + rect.width / 2;
+ if (effectiveToolbarWidth >= maxToolbarWidth - 1) {
+ left = (safeLeft + safeRight) / 2;
+ } else {
+ left = Math.min(Math.max(left, safeLeft + halfToolbar), safeRight - halfToolbar);
+ }
+
+ return {
+ top,
+ left,
+ maxWidth: maxToolbarWidth,
+ };
+ }
+ return { top: 0, left: 0, maxWidth: 460 };
+ };
+
const getTextPanelPosition = () => {
if (!containerRef.current || typeof window === "undefined") {
return { top: 0, left: 0 };
@@ -2223,6 +2342,12 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
};
const toolbarPosition = getToolbarPosition();
+ const boxCutoutToolbarPosition = getBoxCutoutToolbarPosition();
+ const boxCutoutHintText = boxCutoutSelection
+ ? "确认后将仅抠出框选区域"
+ : isBoxCutoutDrawing
+ ? "松开完成框选"
+ : "在图片中拖拽矩形框";
return (
<>
@@ -2416,42 +2541,6 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
) : (
)}
-
- {boxCutoutSelection
- ? "已框选,可确认抠图"
- : isBoxCutoutDrawing
- ? "松开完成框选"
- : "拖拽框选要抠出的元素"}
-
- {
- event.stopPropagation();
- }}
- onClick={(event) => {
- event.stopPropagation();
- }}
- >
-
-
- {boxCutoutSelection ? "确认后将仅抠出框选区域" : "在图片中拖拽矩形框"}
-
- {boxCutoutSelection ? (
-
- ) : null}
-
-
>
)}
@@ -2783,16 +2872,6 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
抠图
-
-