refactor(canvas): 画布工具栏 - 收敛 Dock 放大与交互状态

保留当前画布视觉体系,统一 Dock 按钮形态、状态反馈与子菜单展开关系。\n\nCloses #244
This commit is contained in:
爱笑的毛毛虫
2026-08-17 09:27:39 +08:00
committed by GitHub
parent 829488a32f
commit 58f41e83cf
3 changed files with 16 additions and 47 deletions

View File

@@ -192,8 +192,8 @@ export function CanvasToolbar({
<AnimatePresence>
{appearanceOpen ? (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: aceternityMotion.duration.instant }} className="pointer-events-auto absolute bottom-[var(--canvas-dock-popover-offset)] z-[var(--dock-z-popover)] w-[224px] max-w-[calc(100vw-24px)] -translate-x-1/2" style={{ left: panelX || "50%" }}>
<SpotlightSurface spotlightColor={theme.toolbar.itemHover} initial={{ y: 6, scale: 0.97 }} animate={{ y: 0, scale: 1 }} exit={{ y: 4, scale: 0.98 }} transition={{ duration: aceternityMotion.duration.instant, ease: aceternityMotion.easing.enter }} className="aceternity-floating-panel overflow-hidden rounded-[var(--panel-radius)] border p-2.5 backdrop-blur-2xl" style={{ background: theme.spatial.elevated, borderColor: theme.toolbar.border, color: theme.toolbar.item }} onWheel={(event) => event.stopPropagation()}>
<motion.div initial={{ opacity: 0, scaleY: 0.9, y: 8 }} animate={{ opacity: 1, scaleY: 1, y: 0 }} exit={{ opacity: 0, scaleY: 0.92, y: 6 }} transition={{ duration: aceternityMotion.duration.panel, ease: aceternityMotion.easing.enter }} className="pointer-events-auto absolute bottom-[var(--canvas-dock-popover-offset)] z-[var(--dock-z-popover)] w-[224px] max-w-[calc(100vw-24px)]" style={{ left: panelX || "50%", transformOrigin: "bottom center", x: "-50%" }}>
<SpotlightSurface spotlightColor={theme.toolbar.itemHover} initial={{ opacity: 0, scale: 0.97 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.97, transition: { duration: 0 } }} transition={{ duration: aceternityMotion.duration.instant, ease: aceternityMotion.easing.enter }} className="aceternity-floating-panel overflow-hidden rounded-[var(--panel-radius)] border p-2.5 backdrop-blur-2xl" style={{ background: theme.spatial.elevated, borderColor: theme.toolbar.border, color: theme.toolbar.item }} onWheel={(event) => event.stopPropagation()}>
<PanelHeading icon={<Palette className="size-4" />} title="画布外观" subtitle="调整整个创作空间" theme={theme} />
<div className="mt-3 text-[var(--fs-micro)] font-semibold uppercase opacity-45">主题模式</div>
<div className="mt-1 grid grid-cols-2 gap-1 rounded-[var(--dock-item-radius-labeled)] border p-1" style={{ background: theme.spatial.surface, borderColor: theme.toolbar.border }}>
@@ -231,8 +231,8 @@ function AddNodeMenu({ x, theme, commands }: {
commands: CanvasCreateCommand[];
}) {
return (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: aceternityMotion.duration.instant }} className="pointer-events-auto absolute bottom-[var(--canvas-dock-popover-offset)] z-[var(--dock-z-popover)] w-[260px] max-w-[calc(100vw-24px)] -translate-x-1/2" style={{ left: x || "50%" }}>
<SpotlightSurface spotlightColor={theme.toolbar.itemHover} initial={{ y: 6, scale: 0.97 }} animate={{ y: 0, scale: 1 }} exit={{ y: 4, scale: 0.98 }} transition={{ duration: aceternityMotion.duration.instant, ease: aceternityMotion.easing.enter }} className="aceternity-floating-panel overflow-hidden rounded-[var(--panel-radius)] border p-2 backdrop-blur-2xl" style={{ background: theme.spatial.elevated, borderColor: theme.toolbar.border, color: theme.node.text }} onWheel={(event) => event.stopPropagation()}>
<motion.div initial={{ opacity: 0, scaleY: 0.9, y: 8 }} animate={{ opacity: 1, scaleY: 1, y: 0 }} exit={{ opacity: 0, scaleY: 0.92, y: 6 }} transition={{ duration: aceternityMotion.duration.panel, ease: aceternityMotion.easing.enter }} className="pointer-events-auto absolute bottom-[var(--canvas-dock-popover-offset)] z-[var(--dock-z-popover)] w-[260px] max-w-[calc(100vw-24px)]" style={{ left: x || "50%", transformOrigin: "bottom center", x: "-50%" }}>
<SpotlightSurface spotlightColor={theme.toolbar.itemHover} initial={{ opacity: 0, scale: 0.97 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.97, transition: { duration: 0 } }} transition={{ duration: aceternityMotion.duration.instant, ease: aceternityMotion.easing.enter }} className="aceternity-floating-panel overflow-hidden rounded-[var(--panel-radius)] border p-2 backdrop-blur-2xl" style={{ background: theme.spatial.elevated, borderColor: theme.toolbar.border, color: theme.node.text }} onWheel={(event) => event.stopPropagation()}>
<CanvasCreateMenu commands={commands} />
</SpotlightSurface>
</motion.div>

View File

@@ -40,9 +40,11 @@ type DockMetrics = {
distance: number;
};
// magnification 收敛:base 略增以适配圆角方块,magnified 幅度从 +14 降到 +8,
// 避免 dock 整体跳动,同时保留 Aceternity 接近放大身份。
const DOCK_METRICS: Record<NonNullable<FloatingDockProps["size"]>, DockMetrics> = {
default: { base: 29, magnified: 43, icon: 14, iconMagnified: 19, distance: 108 },
compact: { base: 25, magnified: 35, icon: 13, iconMagnified: 17, distance: 88 },
default: { base: 30, magnified: 38, icon: 15, iconMagnified: 18, distance: 100 },
compact: { base: 26, magnified: 32, icon: 13, iconMagnified: 16, distance: 84 },
};
const TOUCH_DOCK_METRICS: Record<NonNullable<FloatingDockProps["size"]>, DockMetrics> = {
@@ -228,7 +230,7 @@ function DockCommandButton({ command, mouseX, metrics, motionEnabled, compact, s
aria-expanded={command.expands ? command.active || undefined : undefined}
aria-pressed={command.expands ? undefined : command.active || undefined}
disabled={command.disabled}
className={cn("aceternity-dock-command group relative grid size-full place-items-center rounded-full border outline-none", command.quiet && "is-quiet", command.active && "is-active", command.danger && "is-danger")}
className={cn("aceternity-dock-command group relative grid size-full place-items-center rounded-[var(--dock-item-radius)] border outline-none", command.quiet && "is-quiet", command.active && "is-active", command.danger && "is-danger")}
whileTap={motionEnabled && !command.disabled ? { scale: 0.92 } : undefined}
transition={aceternityMotion.spring.dock}
onMouseEnter={() => setHovered(true)}

View File

@@ -5030,16 +5030,17 @@
box-shadow: 0 24px 72px rgba(15, 23, 42, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.16);
}
/* Dock 命令按钮:BottomMenu 式干净状态——透明默认 + hover/active 仅换底色,
去掉描边/内阴影/发光阴影的多层反馈,hover 与 active 用同一套视觉语言,
保持 dock 整体性(参考 uselayouts BottomMenu 的 bg-muted/bg-accent 切换)。*/
.aceternity-dock-command {
border-color: color-mix(in srgb, currentColor 11%, transparent);
background: var(--dock-command-bg);
border-color: transparent;
background: transparent;
color: inherit;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 5px 15px rgba(15, 23, 42, 0.08);
box-shadow: none;
transition:
border-color var(--motion-instant) ease-out,
background-color var(--motion-instant) ease-out,
color var(--motion-instant) ease-out,
box-shadow var(--motion-instant) ease-out;
color var(--motion-instant) ease-out;
}
.aceternity-dock-command > span:first-child svg {
@@ -5049,33 +5050,12 @@
}
.aceternity-dock-command:hover:not(:disabled) {
border-color: color-mix(in srgb, currentColor 20%, transparent);
background: var(--dock-command-hover);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 12px 30px rgba(15, 23, 42, 0.14);
}
.aceternity-dock-command.is-labeled {
border-color: transparent;
background: transparent;
box-shadow: none;
}
.aceternity-dock-command.is-labeled:hover:not(:disabled) {
border-color: transparent;
background: var(--dock-command-hover);
box-shadow: none;
}
.aceternity-dock-command.is-labeled.is-active {
border-color: transparent;
box-shadow: none;
}
.aceternity-dock-command.is-active {
border-color: color-mix(in srgb, var(--dock-command-active) 28%, transparent);
background: var(--dock-command-active);
color: var(--dock-command-active-text);
box-shadow: 0 12px 30px color-mix(in srgb, var(--dock-command-active) 22%, transparent);
}
.aceternity-dock-command.is-active > span:first-child,
@@ -5085,19 +5065,6 @@
opacity: 1;
}
.aceternity-dock-command.is-quiet {
border-color: transparent;
background: transparent;
box-shadow: none;
}
.aceternity-dock-command.is-quiet:hover:not(:disabled),
.aceternity-dock-command.is-quiet.is-active {
border-color: transparent;
background: var(--dock-command-hover);
box-shadow: none;
}
.aceternity-dock-command.is-danger {
color: var(--dock-command-danger);
}