Add draggable preview and keep 30 histories

This commit is contained in:
wuyanwanwu
2026-08-14 00:12:38 +08:00
parent 67683a9de1
commit 28dadddf71
14 changed files with 88 additions and 21 deletions
+68 -2
View File
@@ -156,8 +156,18 @@ export default function Home() {
const fileInputRef = useRef<HTMLInputElement | null>(null);
const cropCanvasRef = useRef<HTMLCanvasElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const patternStageRef = useRef<HTMLDivElement | null>(null);
const cropPreviewRef = useRef<HTMLDivElement | null>(null);
const cropDragRef = useRef<{ pointerId: number; x: number; y: number; cropX: number; cropY: number } | null>(null);
const patternDragRef = useRef<{
pointerId: number;
x: number;
y: number;
scrollLeft: number;
scrollTop: number;
moved: boolean;
} | null>(null);
const suppressCanvasClickRef = useRef(false);
useEffect(() => {
const saved = localStorage.getItem("bead-pattern-history");
@@ -362,7 +372,50 @@ export default function Home() {
});
};
const handlePatternPointerDown = (event: React.PointerEvent<HTMLCanvasElement>) => {
if (event.button !== 0) return;
const stage = patternStageRef.current;
if (!stage) return;
stage.setPointerCapture(event.pointerId);
patternDragRef.current = {
pointerId: event.pointerId,
x: event.clientX,
y: event.clientY,
scrollLeft: stage.scrollLeft,
scrollTop: stage.scrollTop,
moved: false,
};
};
const handlePatternPointerMove = (event: React.PointerEvent<HTMLCanvasElement>) => {
const stage = patternStageRef.current;
const drag = patternDragRef.current;
if (!stage || !drag || drag.pointerId !== event.pointerId) return;
const deltaX = event.clientX - drag.x;
const deltaY = event.clientY - drag.y;
if (!drag.moved && Math.hypot(deltaX, deltaY) < 5) return;
drag.moved = true;
stage.classList.add("is-dragging");
stage.scrollLeft = drag.scrollLeft - deltaX;
stage.scrollTop = drag.scrollTop - deltaY;
setHoveredPixel(null);
};
const handlePatternPointerUp = (event: React.PointerEvent<HTMLCanvasElement>) => {
const stage = patternStageRef.current;
const drag = patternDragRef.current;
if (!drag || drag.pointerId !== event.pointerId) return;
suppressCanvasClickRef.current = drag.moved;
patternDragRef.current = null;
stage?.classList.remove("is-dragging");
if (stage?.hasPointerCapture(event.pointerId)) stage.releasePointerCapture(event.pointerId);
};
const handleCanvasClick = (event: React.MouseEvent<HTMLCanvasElement>) => {
if (suppressCanvasClickRef.current) {
suppressCanvasClickRef.current = false;
return;
}
const canvas = canvasRef.current;
if (!canvas) return;
const rect = canvas.getBoundingClientRect();
@@ -499,9 +552,22 @@ export default function Home() {
</div>
</div>
<div className="canvas-stage">
<div
className="canvas-stage"
ref={patternStageRef}
>
<div className="canvas-wrap">
<canvas ref={canvasRef} onClick={handleCanvasClick} onMouseMove={handleCanvasMove} onMouseLeave={() => setHoveredPixel(null)} aria-label="转换后的拼豆像素图,点击格子可选择颜色" />
<canvas
ref={canvasRef}
onPointerDown={handlePatternPointerDown}
onPointerMove={handlePatternPointerMove}
onPointerUp={handlePatternPointerUp}
onPointerCancel={handlePatternPointerUp}
onClick={handleCanvasClick}
onMouseMove={handleCanvasMove}
onMouseLeave={() => setHoveredPixel(null)}
aria-label="转换后的拼豆像素图,可拖动查看,轻点格子选择颜色"
/>
{hoveredPixel && <div className="pixel-tooltip" style={{ left: hoveredPixel.left, top: hoveredPixel.top }}>
<span className="tooltip-swatch" style={{ backgroundColor: hoveredPixel.color.hex }} />
<strong> {hoveredPixel.row} · {hoveredPixel.column} </strong>