feat: add on-canvas zoom controls for psd image slots
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-31 20:47:42 +08:00
parent dc04c1d91b
commit dceebf0652

View File

@ -9,10 +9,12 @@ import {
Loader2,
Move,
MousePointerClick,
RefreshCcw,
Sparkles,
Type,
Upload,
ZoomIn,
ZoomOut,
} from "lucide-react";
import { fetchUploadWithRetry } from "@/lib/client/upload-with-retry";
import {
@ -565,6 +567,20 @@ export function AgentPsdTemplateEditorDialog({
}));
}
function nudgeImageScale(layerId: string, delta: number) {
updateImagePlacement(layerId, (current) => ({
...current,
scale: Math.max(0.4, Math.min(3, Number((current.scale + delta).toFixed(2)))),
}));
}
function resetImagePlacement(layerId: string) {
setImagePlacements((current) => ({
...current,
[layerId]: getDefaultAgentPsdImagePlacement(),
}));
}
function handleImageDragStart(
layer: AgentPsdTemplateImageLayerSlot,
event: ReactPointerEvent<HTMLDivElement>
@ -611,6 +627,21 @@ export function AgentPsdTemplateEditorDialog({
window.addEventListener("pointercancel", handlePointerUp);
}
function handleImageWheel(
layer: AgentPsdTemplateImageLayerSlot,
event: ReactPointerEvent<HTMLDivElement>
) {
if (!String(effectiveImageAssignments[layer.id] || "").trim()) {
return;
}
event.preventDefault();
event.stopPropagation();
const delta = event.deltaY < 0 ? 0.08 : -0.08;
nudgeImageScale(layer.id, delta);
}
async function handleRender() {
if (!selectedTemplate) {
toast.error("请先选择 PSD 模板");
@ -898,6 +929,7 @@ export function AgentPsdTemplateEditorDialog({
tabIndex={0}
onClick={() => setSelectedLayerId(layer.id)}
onPointerDown={(event) => handleImageDragStart(layer, event)}
onWheel={(event) => handleImageWheel(layer, event)}
className="absolute overflow-hidden rounded-lg transition-all"
style={{
...frameStyle,
@ -912,9 +944,50 @@ export function AgentPsdTemplateEditorDialog({
<div className="absolute left-2 top-2 rounded-full bg-black/70 px-2 py-1 text-[11px] text-white">
{layer.layerName}
</div>
<div className="absolute right-2 top-2 flex items-center gap-1 rounded-full bg-black/70 p-1 text-white shadow-lg">
<button
type="button"
className="rounded-full p-1 transition hover:bg-white/10"
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => {
event.stopPropagation();
nudgeImageScale(layer.id, -0.1);
}}
aria-label="缩小图片"
title="缩小"
>
<ZoomOut className="h-3.5 w-3.5" />
</button>
<button
type="button"
className="rounded-full p-1 transition hover:bg-white/10"
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => {
event.stopPropagation();
nudgeImageScale(layer.id, 0.1);
}}
aria-label="放大图片"
title="放大"
>
<ZoomIn className="h-3.5 w-3.5" />
</button>
<button
type="button"
className="rounded-full p-1 transition hover:bg-white/10"
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => {
event.stopPropagation();
resetImagePlacement(layer.id);
}}
aria-label="重置图片位置和缩放"
title="重置"
>
<RefreshCcw className="h-3.5 w-3.5" />
</button>
</div>
<div className="pointer-events-none absolute bottom-2 right-2 flex items-center gap-1 rounded-full bg-orange-500/85 px-2 py-1 text-[11px] text-white">
<Move className="h-3 w-3" />
拖动调整
拖动或滚轮缩放
</div>
</div>
) : (
@ -1102,12 +1175,7 @@ export function AgentPsdTemplateEditorDialog({
type="button"
variant="ghost"
size="sm"
onClick={() =>
setImagePlacements((current) => ({
...current,
[selectedImageLayer.id]: getDefaultAgentPsdImagePlacement(),
}))
}
onClick={() => resetImagePlacement(selectedImageLayer.id)}
>
重置位置与缩放
</Button>