fix: stabilize enhance preview compare
This commit is contained in:
parent
82dda143eb
commit
8a722ed849
@ -160,13 +160,6 @@ function clampNumber(value: number, min: number, max: number) {
|
||||
return Math.min(max, Math.max(min, value));
|
||||
}
|
||||
|
||||
function isEnhanceCompareControlTarget(target: EventTarget | null) {
|
||||
return (
|
||||
target instanceof HTMLElement &&
|
||||
Boolean(target.closest("[data-enhance-pro-compare-control='true']"))
|
||||
);
|
||||
}
|
||||
|
||||
function fitDimensions(width: number, height: number, maxOutputEdge: number) {
|
||||
const safeWidth = Math.max(1, Math.round(width || 1024));
|
||||
const safeHeight = Math.max(1, Math.round(height || 1024));
|
||||
@ -403,16 +396,8 @@ export function EnhanceProWorkspace({
|
||||
const [settingsOpen, setSettingsOpen] = useState(true);
|
||||
const [adjustmentsOpen, setAdjustmentsOpen] = useState(true);
|
||||
const [faceOpen, setFaceOpen] = useState(true);
|
||||
const [previewOffset, setPreviewOffset] = useState({ x: 0, y: 0 });
|
||||
const [comparePosition, setComparePosition] = useState(50);
|
||||
const [isPreviewDragging, setIsPreviewDragging] = useState(false);
|
||||
const previewDragStateRef = useRef<{
|
||||
pointerId: number;
|
||||
startX: number;
|
||||
startY: number;
|
||||
originX: number;
|
||||
originY: number;
|
||||
} | null>(null);
|
||||
const compareDragPointerIdRef = useRef<number | null>(null);
|
||||
|
||||
const { data: configPayload, isLoading } = useSWR<{
|
||||
success?: boolean;
|
||||
@ -523,12 +508,6 @@ export function EnhanceProWorkspace({
|
||||
setLastErrorMessage("");
|
||||
}, [sourceImageUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
setPreviewOffset({ x: 0, y: 0 });
|
||||
setIsPreviewDragging(false);
|
||||
previewDragStateRef.current = null;
|
||||
}, [activePreviewUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
setComparePosition(50);
|
||||
}, [selectedResultIndex]);
|
||||
@ -682,67 +661,43 @@ export function EnhanceProWorkspace({
|
||||
return items;
|
||||
}, [generatedImages, sourceImageUrl]);
|
||||
|
||||
const handlePreviewPointerDown = (
|
||||
const updateComparePositionFromPointer = (
|
||||
event: ReactPointerEvent<HTMLDivElement>
|
||||
) => {
|
||||
if (!activePreviewUrl || event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
if (isEnhanceCompareControlTarget(event.target)) {
|
||||
return;
|
||||
}
|
||||
const rect = event.currentTarget.getBoundingClientRect();
|
||||
if (rect.width <= 0) return;
|
||||
const nextPosition = ((event.clientX - rect.left) / rect.width) * 100;
|
||||
setComparePosition(clampNumber(nextPosition, 0, 100));
|
||||
};
|
||||
|
||||
previewDragStateRef.current = {
|
||||
pointerId: event.pointerId,
|
||||
startX: event.clientX,
|
||||
startY: event.clientY,
|
||||
originX: previewOffset.x,
|
||||
originY: previewOffset.y,
|
||||
};
|
||||
setIsPreviewDragging(true);
|
||||
const handleComparePointerDown = (
|
||||
event: ReactPointerEvent<HTMLDivElement>
|
||||
) => {
|
||||
if (event.button !== 0) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
compareDragPointerIdRef.current = event.pointerId;
|
||||
event.currentTarget.setPointerCapture(event.pointerId);
|
||||
updateComparePositionFromPointer(event);
|
||||
};
|
||||
|
||||
const handlePreviewPointerMove = (
|
||||
const handleComparePointerMove = (
|
||||
event: ReactPointerEvent<HTMLDivElement>
|
||||
) => {
|
||||
const dragState = previewDragStateRef.current;
|
||||
if (!dragState || dragState.pointerId !== event.pointerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxOffsetX = Math.max(48, event.currentTarget.clientWidth * 0.18);
|
||||
const maxOffsetY = Math.max(48, event.currentTarget.clientHeight * 0.18);
|
||||
|
||||
setPreviewOffset({
|
||||
x: clampNumber(
|
||||
dragState.originX + event.clientX - dragState.startX,
|
||||
-maxOffsetX,
|
||||
maxOffsetX
|
||||
),
|
||||
y: clampNumber(
|
||||
dragState.originY + event.clientY - dragState.startY,
|
||||
-maxOffsetY,
|
||||
maxOffsetY
|
||||
),
|
||||
});
|
||||
if (compareDragPointerIdRef.current !== event.pointerId) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
updateComparePositionFromPointer(event);
|
||||
};
|
||||
|
||||
const finishPreviewDrag = (event?: ReactPointerEvent<HTMLDivElement>) => {
|
||||
const dragState = previewDragStateRef.current;
|
||||
if (!dragState) {
|
||||
return;
|
||||
const finishCompareDrag = (event: ReactPointerEvent<HTMLDivElement>) => {
|
||||
if (compareDragPointerIdRef.current !== event.pointerId) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
||||
event.currentTarget.releasePointerCapture(event.pointerId);
|
||||
}
|
||||
|
||||
if (
|
||||
event &&
|
||||
event.currentTarget.hasPointerCapture(dragState.pointerId)
|
||||
) {
|
||||
event.currentTarget.releasePointerCapture(dragState.pointerId);
|
||||
}
|
||||
|
||||
previewDragStateRef.current = null;
|
||||
setIsPreviewDragging(false);
|
||||
compareDragPointerIdRef.current = null;
|
||||
};
|
||||
|
||||
return (
|
||||
@ -751,24 +706,9 @@ export function EnhanceProWorkspace({
|
||||
<div className="pointer-events-none absolute inset-0 bg-[#ffffff]" />
|
||||
<div className="pattern-grid-lg pointer-events-none absolute inset-0 opacity-[0.025] [mask-image:radial-gradient(circle_at_center,black_38%,transparent_82%)]" />
|
||||
{activePreviewUrl ? (
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex h-full w-full min-w-0 touch-none select-none items-center justify-center overflow-hidden",
|
||||
isPreviewDragging ? "cursor-grabbing" : "cursor-grab"
|
||||
)}
|
||||
onDoubleClick={() => setPreviewOffset({ x: 0, y: 0 })}
|
||||
onPointerCancel={finishPreviewDrag}
|
||||
onPointerDown={handlePreviewPointerDown}
|
||||
onPointerMove={handlePreviewPointerMove}
|
||||
onPointerUp={finishPreviewDrag}
|
||||
>
|
||||
<div className="relative flex h-full w-full min-w-0 select-none items-center justify-center overflow-hidden">
|
||||
<div className="relative flex h-full w-full min-w-0 items-center justify-center overflow-hidden px-4 py-4">
|
||||
<div
|
||||
className="relative h-full w-full max-w-[min(1320px,calc(100vw-30rem))] overflow-hidden"
|
||||
style={{
|
||||
transform: `translate(${previewOffset.x}px, ${previewOffset.y}px)`,
|
||||
}}
|
||||
>
|
||||
<div className="relative h-full w-full max-w-[min(1320px,calc(100vw-30rem))] overflow-hidden">
|
||||
<img
|
||||
alt={activePreviewLabel}
|
||||
className="pointer-events-none absolute inset-0 h-full w-full object-contain transition-transform duration-75"
|
||||
@ -792,6 +732,25 @@ export function EnhanceProWorkspace({
|
||||
className="pointer-events-none absolute inset-y-0 w-px bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.32),0_0_18px_rgba(0,0,0,0.35)]"
|
||||
style={{ left: `${comparePosition}%` }}
|
||||
/>
|
||||
<div
|
||||
className="pointer-events-auto absolute inset-0 cursor-col-resize touch-none"
|
||||
data-enhance-pro-compare-control="true"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onPointerCancel={finishCompareDrag}
|
||||
onPointerDown={handleComparePointerDown}
|
||||
onPointerMove={handleComparePointerMove}
|
||||
onPointerUp={finishCompareDrag}
|
||||
>
|
||||
<div
|
||||
className="pointer-events-none absolute inset-y-0 w-12 -translate-x-1/2"
|
||||
style={{ left: `${comparePosition}%` }}
|
||||
>
|
||||
<div className="absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.25),0_0_18px_rgba(0,0,0,0.3)]" />
|
||||
<div className="-translate-x-1/2 -translate-y-1/2 absolute top-1/2 left-1/2 flex h-9 w-9 items-center justify-center rounded-full border border-white/80 bg-black/70 text-white shadow-[0_12px_24px_rgba(0,0,0,0.28)] backdrop-blur-sm">
|
||||
<SlidersHorizontal className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{showComparisonPreview ? (
|
||||
@ -802,38 +761,6 @@ export function EnhanceProWorkspace({
|
||||
<div className="pointer-events-none absolute top-3 right-3 rounded-full bg-black/65 px-2.5 py-1 font-medium text-[11px] text-white backdrop-blur-sm">
|
||||
增强后
|
||||
</div>
|
||||
<div
|
||||
className="pointer-events-auto absolute right-6 bottom-5 left-6 h-8 cursor-col-resize rounded-full"
|
||||
data-enhance-pro-compare-control="true"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
onPointerMove={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="-translate-y-1/2 pointer-events-none absolute top-1/2 right-0 left-0 h-1 rounded-full bg-white/55 shadow-[0_0_0_1px_rgba(0,0,0,0.2)]">
|
||||
<div
|
||||
className="h-full rounded-full bg-[#111111]"
|
||||
style={{ width: `${comparePosition}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="-translate-x-1/2 -translate-y-1/2 pointer-events-none absolute top-1/2 h-5 w-5 rounded-full border-2 border-white bg-[#111111] shadow-[0_8px_18px_rgba(0,0,0,0.28)]"
|
||||
style={{ left: `${comparePosition}%` }}
|
||||
/>
|
||||
<input
|
||||
aria-label="原图和增强结果对比滑块"
|
||||
className="absolute inset-0 h-full w-full cursor-col-resize opacity-0"
|
||||
max={100}
|
||||
min={0}
|
||||
onChange={(event) =>
|
||||
setComparePosition(Number(event.target.value) || 0)
|
||||
}
|
||||
onInput={(event) =>
|
||||
setComparePosition(Number(event.currentTarget.value) || 0)
|
||||
}
|
||||
type="range"
|
||||
value={comparePosition}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
@ -843,7 +770,7 @@ export function EnhanceProWorkspace({
|
||||
<span>{activePreviewLabel}</span>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute top-6 right-8 hidden items-center gap-2 rounded-full border border-[#e5e5e5] bg-white/96 px-3 py-1.5 text-[#666666] text-xs shadow-[0_0_0_1px_rgba(0,0,0,0.03),0_10px_24px_rgba(0,0,0,0.04)] lg:inline-flex">
|
||||
双击回正
|
||||
图片固定显示
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user