compact resize ratio picker
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-03-26 21:28:04 +08:00
parent 6c1583a2fa
commit f32146b3f3

View File

@ -5,7 +5,7 @@ import { createPortal } from "react-dom";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useTheme } from "next-themes";
import { X, Check, Undo, Trash2, Scissors, Sparkles, Download, Expand, Move, Maximize2, ZoomIn, ZoomOut, Loader2, Move3D, Layers, Upload as UploadIcon, Image as ImageIcon, Droplet, Redo, MousePointer2 } from "lucide-react";
import { X, Check, Undo, Trash2, Scissors, Sparkles, Download, Expand, Move, Maximize2, ZoomIn, ZoomOut, Loader2, Move3D, Layers, Upload as UploadIcon, Image as ImageIcon, Droplet, Redo, MousePointer2, ChevronDown } from "lucide-react";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import { Slider } from "./ui/slider";
@ -20,6 +20,7 @@ import {
DialogDescription,
} from "@/components/ui/dialog";
import { cn } from "@/lib/utils";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import {
Tooltip,
TooltipContent,
@ -327,6 +328,7 @@ export function ImageAnnotator({
const [dragStart, setDragStart] = useState<{ x: number; y: number } | null>(null);
const [selectedAspectRatio, setSelectedAspectRatio] = useState<string | null>(null); // 选中的比例,null表示自定义
const [isResizing, setIsResizing] = useState(false); // 尺寸调整处理中状态
const [resizeRatioPickerOpen, setResizeRatioPickerOpen] = useState(false);
const resizeCanvasRef = useRef<HTMLCanvasElement>(null);
const resizeContainerRef = useRef<HTMLDivElement>(null);
@ -400,6 +402,10 @@ export function ImageAnnotator({
}
};
const currentAspectRatioLabel =
aspectRatios.find((ratio) => ratio.value === selectedAspectRatio)?.label ||
(selectedAspectRatio ? selectedAspectRatio : "自定义");
const handleResizeQuickPresetSelect = (
preset: (typeof resizeQuickPresets)[number]
) => {
@ -1737,26 +1743,48 @@ export function ImageAnnotator({
<div className="absolute bottom-8 left-1/2 z-20 w-[min(92vw,1120px)] -translate-x-1/2 pointer-events-none">
<div className="pointer-events-auto flex items-center justify-between gap-3 overflow-x-auto rounded-[28px] border border-border/50 bg-background/95 px-3 py-2 shadow-2xl backdrop-blur-xl scrollbar-hide">
<div className="flex min-w-max items-center gap-2">
<div className="flex items-center gap-1 rounded-[22px] bg-muted/35 px-1 py-1">
{aspectRatios.map((ratio) => (
<Popover open={resizeRatioPickerOpen} onOpenChange={setResizeRatioPickerOpen}>
<PopoverTrigger asChild>
<Button
key={ratio.value}
variant={selectedAspectRatio === ratio.value ? "secondary" : "ghost"}
size="sm"
onClick={() =>
handleAspectRatioSelect(
ratio.value === selectedAspectRatio ? null : ratio.value
)
}
className={cn(
"h-8 rounded-[18px] px-3 text-xs font-medium",
selectedAspectRatio === ratio.value && "shadow-sm"
)}
variant="ghost"
className="h-10 shrink-0 rounded-[20px] bg-muted/35 px-4 text-xs font-medium text-foreground hover:bg-muted/45"
>
{ratio.label}
比例
<span className="ml-2 text-foreground/80">{currentAspectRatioLabel}</span>
<ChevronDown className="ml-2 h-4 w-4 text-muted-foreground" />
</Button>
))}
</div>
</PopoverTrigger>
<PopoverContent
align="start"
side="top"
className="w-[320px] rounded-[22px] border-border/60 bg-background/96 p-3 backdrop-blur-xl"
>
<div className="mb-2 text-xs font-medium text-muted-foreground">
选择画布比例
</div>
<div className="grid grid-cols-4 gap-2">
{aspectRatios.map((ratio) => (
<Button
key={ratio.value}
variant={selectedAspectRatio === ratio.value ? "secondary" : "ghost"}
size="sm"
onClick={() => {
handleAspectRatioSelect(
ratio.value === selectedAspectRatio ? null : ratio.value
);
setResizeRatioPickerOpen(false);
}}
className={cn(
"h-9 rounded-[16px] px-3 text-xs font-medium",
selectedAspectRatio === ratio.value && "shadow-sm"
)}
>
{ratio.label}
</Button>
))}
</div>
</PopoverContent>
</Popover>
<div className="h-7 w-px shrink-0 bg-border/60" />
<div className="flex items-center gap-2 rounded-[20px] bg-muted/30 px-2 py-1">
<Input