fix: make canvas grid visible when enabled
Browse files
src/components/Canvas.tsx
CHANGED
|
@@ -140,13 +140,7 @@ export const Canvas = () => {
|
|
| 140 |
setIsDrawing(false);
|
| 141 |
if (currentPath.length > 1 && !isEraser) {
|
| 142 |
const baseStrokeWidth = annotationSize * (isHighlighter ? 3 : 1);
|
| 143 |
-
setAnnotations(prev => [...prev, {
|
| 144 |
-
id: crypto.randomUUID(),
|
| 145 |
-
points: currentPath,
|
| 146 |
-
color: annotationColor,
|
| 147 |
-
strokeWidth: baseStrokeWidth,
|
| 148 |
-
isHighlighter: isHighlighter || undefined
|
| 149 |
-
}]);
|
| 150 |
}
|
| 151 |
setCurrentPath([]);
|
| 152 |
}
|
|
@@ -154,10 +148,13 @@ export const Canvas = () => {
|
|
| 154 |
};
|
| 155 |
|
| 156 |
const currentStrokeWidth = annotationSize * (isHighlighter ? 3 : 1);
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
return <div ref={containerRef} className={`absolute inset-0 w-full h-full overflow-hidden bg-[var(--canvas-bg)] ${isSpaceDown ? 'cursor-grab' : isAnnotationMode ? 'cursor-crosshair' : 'cursor-default'} ${isDraggingCanvas ? '!cursor-grabbing' : ''}`} onWheel={handleWheel} onPointerDown={handlePointerDown} onPointerMove={handlePointerMove} onPointerUp={finishPointer} onPointerCancel={finishPointer} onPointerLeave={e => { if (isDrawing || isDraggingCanvas) finishPointer(e); }} onContextMenu={(e) => { e.preventDefault(); setContextMenu({ x: e.clientX, y: e.clientY, imageId: null }); }}>
|
| 159 |
{isDropActive && <div className="absolute inset-0 z-[100] pointer-events-none flex items-center justify-center"><div className="absolute inset-4 border-2 border-dashed border-[var(--accent)] rounded-2xl bg-[var(--accent)]/5" /><div className="relative z-10 bg-[var(--panel-surface)] border border-[var(--accent)] rounded-xl px-6 py-4 shadow-2xl flex flex-col items-center gap-2"><svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg><span className="text-[var(--accent)] font-semibold text-sm">Drop images to add to canvas</span><span className="text-[var(--ui-secondary)] text-xs">PNG 路 JPG 路 WEBP 路 GIF 路 BMP 路 ICO 路 TIFF</span></div></div>}
|
| 160 |
-
{showGrid && <div className="absolute inset-0 pointer-events-none
|
| 161 |
<div id="canvas-inner" style={{ transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`, transformOrigin: '0 0' }} className="w-full h-full absolute top-0 left-0 z-10">
|
| 162 |
{Array.from(new Set(images.filter(img => img.groupId).map(img => img.groupId!))).map(gid => { const gi = images.filter(img => img.groupId === gid); if (!gi.length) return null; const minX = Math.min(...gi.map(i => i.x)), minY = Math.min(...gi.map(i => i.y)), maxX = Math.max(...gi.map(i => i.x + i.width)), maxY = Math.max(...gi.map(i => i.y + i.height)); return <div key={`g-${gid}`} className="absolute bg-white/5 border border-white/20 rounded-xl pointer-events-none" style={{ left: minX-20, top: minY-40, width: maxX-minX+40, height: maxY-minY+60, zIndex: 0 }}><div className="text-gray-500 text-xs font-semibold uppercase tracking-wider pl-4 pt-2">Group</div></div>; })}
|
| 163 |
{images.map(img => <RefImageNode key={img.id} image={img} />)}{textNotes?.map(note => <TextNoteNode key={note.id} note={note} />)}
|
|
|
|
| 140 |
setIsDrawing(false);
|
| 141 |
if (currentPath.length > 1 && !isEraser) {
|
| 142 |
const baseStrokeWidth = annotationSize * (isHighlighter ? 3 : 1);
|
| 143 |
+
setAnnotations(prev => [...prev, { id: crypto.randomUUID(), points: currentPath, color: annotationColor, strokeWidth: baseStrokeWidth, isHighlighter: isHighlighter || undefined }]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
}
|
| 145 |
setCurrentPath([]);
|
| 146 |
}
|
|
|
|
| 148 |
};
|
| 149 |
|
| 150 |
const currentStrokeWidth = annotationSize * (isHighlighter ? 3 : 1);
|
| 151 |
+
const gridSize = Math.max(8, 24 * zoom);
|
| 152 |
+
const majorGridSize = gridSize * 4;
|
| 153 |
+
const gridPosition = `${pan.x}px ${pan.y}px`;
|
| 154 |
|
| 155 |
return <div ref={containerRef} className={`absolute inset-0 w-full h-full overflow-hidden bg-[var(--canvas-bg)] ${isSpaceDown ? 'cursor-grab' : isAnnotationMode ? 'cursor-crosshair' : 'cursor-default'} ${isDraggingCanvas ? '!cursor-grabbing' : ''}`} onWheel={handleWheel} onPointerDown={handlePointerDown} onPointerMove={handlePointerMove} onPointerUp={finishPointer} onPointerCancel={finishPointer} onPointerLeave={e => { if (isDrawing || isDraggingCanvas) finishPointer(e); }} onContextMenu={(e) => { e.preventDefault(); setContextMenu({ x: e.clientX, y: e.clientY, imageId: null }); }}>
|
| 156 |
{isDropActive && <div className="absolute inset-0 z-[100] pointer-events-none flex items-center justify-center"><div className="absolute inset-4 border-2 border-dashed border-[var(--accent)] rounded-2xl bg-[var(--accent)]/5" /><div className="relative z-10 bg-[var(--panel-surface)] border border-[var(--accent)] rounded-xl px-6 py-4 shadow-2xl flex flex-col items-center gap-2"><svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg><span className="text-[var(--accent)] font-semibold text-sm">Drop images to add to canvas</span><span className="text-[var(--ui-secondary)] text-xs">PNG 路 JPG 路 WEBP 路 GIF 路 BMP 路 ICO 路 TIFF</span></div></div>}
|
| 157 |
+
{showGrid && <div className="absolute inset-0 pointer-events-none z-[1]" style={{ backgroundImage: 'linear-gradient(to right, color-mix(in srgb, var(--ui-primary) 10%, transparent) 1px, transparent 1px), linear-gradient(to bottom, color-mix(in srgb, var(--ui-primary) 10%, transparent) 1px, transparent 1px), linear-gradient(to right, color-mix(in srgb, var(--accent) 16%, transparent) 1px, transparent 1px), linear-gradient(to bottom, color-mix(in srgb, var(--accent) 16%, transparent) 1px, transparent 1px)', backgroundSize: `${gridSize}px ${gridSize}px, ${gridSize}px ${gridSize}px, ${majorGridSize}px ${majorGridSize}px, ${majorGridSize}px ${majorGridSize}px`, backgroundPosition: `${gridPosition}, ${gridPosition}, ${gridPosition}, ${gridPosition}`, opacity: 0.72 }} />}
|
| 158 |
<div id="canvas-inner" style={{ transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`, transformOrigin: '0 0' }} className="w-full h-full absolute top-0 left-0 z-10">
|
| 159 |
{Array.from(new Set(images.filter(img => img.groupId).map(img => img.groupId!))).map(gid => { const gi = images.filter(img => img.groupId === gid); if (!gi.length) return null; const minX = Math.min(...gi.map(i => i.x)), minY = Math.min(...gi.map(i => i.y)), maxX = Math.max(...gi.map(i => i.x + i.width)), maxY = Math.max(...gi.map(i => i.y + i.height)); return <div key={`g-${gid}`} className="absolute bg-white/5 border border-white/20 rounded-xl pointer-events-none" style={{ left: minX-20, top: minY-40, width: maxX-minX+40, height: maxY-minY+60, zIndex: 0 }}><div className="text-gray-500 text-xs font-semibold uppercase tracking-wider pl-4 pt-2">Group</div></div>; })}
|
| 160 |
{images.map(img => <RefImageNode key={img.id} image={img} />)}{textNotes?.map(note => <TextNoteNode key={note.id} note={note} />)}
|