mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(erase-object): auto-center compare slider at erased region
Track the painted mask centroid and initialise the before/after slider at that position so the result is immediately visible without manual dragging.
This commit is contained in:
@@ -9,6 +9,8 @@ interface BeforeAfterSliderProps {
|
||||
beforeSize?: number;
|
||||
/** Processed file size in bytes. */
|
||||
afterSize?: number;
|
||||
/** Initial divider position as a percentage (0–100). Defaults to 50. */
|
||||
initialPosition?: number;
|
||||
}
|
||||
|
||||
function formatSize(bytes: number): string {
|
||||
@@ -29,9 +31,10 @@ export function BeforeAfterSlider({
|
||||
afterSrc,
|
||||
beforeSize,
|
||||
afterSize,
|
||||
initialPosition = 50,
|
||||
}: BeforeAfterSliderProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [position, setPosition] = useState(50); // percentage 0-100
|
||||
const [position, setPosition] = useState(initialPosition); // percentage 0-100
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
const updatePosition = useCallback((clientX: number) => {
|
||||
|
||||
@@ -36,7 +36,9 @@ export function CompareSettings() {
|
||||
const result = await res.json();
|
||||
setSimilarity(result.similarity);
|
||||
setDownloadUrl(result.downloadUrl);
|
||||
setProcessedUrl(result.downloadUrl);
|
||||
// Show the second image in the slider (not the diff) so the user can
|
||||
// visually compare their two originals. The diff is still downloadable.
|
||||
setProcessedUrl(URL.createObjectURL(secondFile));
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Comparison failed");
|
||||
} finally {
|
||||
|
||||
@@ -14,6 +14,7 @@ interface EraseObjectSettingsProps {
|
||||
hasStrokes: boolean;
|
||||
brushSize: number;
|
||||
onBrushSizeChange: (size: number) => void;
|
||||
onMaskCenter?: (centerPct: number) => void;
|
||||
}
|
||||
|
||||
export function EraseObjectSettings({
|
||||
@@ -21,6 +22,7 @@ export function EraseObjectSettings({
|
||||
hasStrokes,
|
||||
brushSize,
|
||||
onBrushSizeChange: setBrushSize,
|
||||
onMaskCenter,
|
||||
}: EraseObjectSettingsProps) {
|
||||
const { files, processing, error, setProcessing, setError, setProcessedUrl, setSizes } =
|
||||
useFileStore();
|
||||
@@ -41,6 +43,12 @@ export function EraseObjectSettings({
|
||||
const maskBlob = await eraserRef.current.exportMask();
|
||||
if (!maskBlob) return;
|
||||
|
||||
// Record where the user painted so the comparison slider starts at that location
|
||||
const maskCenter = eraserRef.current.getMaskCenter();
|
||||
if (maskCenter !== null && onMaskCenter) {
|
||||
onMaskCenter(maskCenter);
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setDownloadUrl(null);
|
||||
setProcessing(true);
|
||||
|
||||
@@ -5,6 +5,7 @@ type Stroke = { points: Point[]; size: number };
|
||||
|
||||
export interface EraserCanvasRef {
|
||||
exportMask: () => Promise<Blob | null>;
|
||||
getMaskCenter: () => number | null;
|
||||
clear: () => void;
|
||||
undo: () => void;
|
||||
}
|
||||
@@ -238,6 +239,20 @@ export const EraserCanvas = forwardRef<EraserCanvasRef, EraserCanvasProps>(funct
|
||||
mask.toBlob((b) => resolve(b), "image/png");
|
||||
});
|
||||
},
|
||||
getMaskCenter: () => {
|
||||
if (strokesRef.current.length === 0 || !canvasSize) return null;
|
||||
let minX = Infinity;
|
||||
let maxX = -Infinity;
|
||||
for (const stroke of strokesRef.current) {
|
||||
for (const pt of stroke.points) {
|
||||
minX = Math.min(minX, pt.x - stroke.size / 2);
|
||||
maxX = Math.max(maxX, pt.x + stroke.size / 2);
|
||||
}
|
||||
}
|
||||
if (minX === Infinity) return null;
|
||||
const centerX = (minX + maxX) / 2;
|
||||
return Math.max(0, Math.min(100, (centerX / canvasSize.w) * 100));
|
||||
},
|
||||
clear: () => {
|
||||
strokesRef.current = [];
|
||||
currentPointsRef.current = [];
|
||||
|
||||
Reference in New Issue
Block a user