Add a visual preview of the next pixel placement
Implement a pixel preview feature on hover, showing the selected color before committing the pixel placement. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 0385ea33-cde8-4bbd-8fce-8d192d30eb41 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/870d08ce-da3b-4822-9874-c2fe2b7628b1/0385ea33-cde8-4bbd-8fce-8d192d30eb41/ZegP3fv
This commit is contained in:
parent
83fc03b313
commit
f017853c09
@ -25,6 +25,7 @@ export function Canvas({
|
|||||||
const [zoom, setZoom] = useState(1);
|
const [zoom, setZoom] = useState(1);
|
||||||
const [pixelSize, setPixelSize] = useState(8);
|
const [pixelSize, setPixelSize] = useState(8);
|
||||||
const [mouseCoords, setMouseCoords] = useState<{x: number, y: number} | null>(null);
|
const [mouseCoords, setMouseCoords] = useState<{x: number, y: number} | null>(null);
|
||||||
|
const [previewPixel, setPreviewPixel] = useState<{x: number, y: number} | null>(null);
|
||||||
|
|
||||||
// Create pixel map for O(1) lookup
|
// Create pixel map for O(1) lookup
|
||||||
const pixelMap = new Map<string, string>();
|
const pixelMap = new Map<string, string>();
|
||||||
@ -39,10 +40,14 @@ export function Canvas({
|
|||||||
|
|
||||||
const handlePixelMouseEnter = (x: number, y: number) => {
|
const handlePixelMouseEnter = (x: number, y: number) => {
|
||||||
setMouseCoords({ x, y });
|
setMouseCoords({ x, y });
|
||||||
|
if (!cooldownActive) {
|
||||||
|
setPreviewPixel({ x, y });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePixelMouseLeave = () => {
|
const handlePixelMouseLeave = () => {
|
||||||
setMouseCoords(null);
|
setMouseCoords(null);
|
||||||
|
setPreviewPixel(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleZoomIn = () => {
|
const handleZoomIn = () => {
|
||||||
@ -136,19 +141,24 @@ export function Canvas({
|
|||||||
{Array.from({ length: canvasHeight }, (_, y) =>
|
{Array.from({ length: canvasHeight }, (_, y) =>
|
||||||
Array.from({ length: canvasWidth }, (_, x) => {
|
Array.from({ length: canvasWidth }, (_, x) => {
|
||||||
const pixelColor = pixelMap.get(`${x},${y}`) || "#FFFFFF";
|
const pixelColor = pixelMap.get(`${x},${y}`) || "#FFFFFF";
|
||||||
|
const isPreview = previewPixel && previewPixel.x === x && previewPixel.y === y;
|
||||||
|
const previewColor = isPreview && !cooldownActive ? selectedColor : pixelColor;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`${x}-${y}`}
|
key={`${x}-${y}`}
|
||||||
className={cn(
|
className={cn(
|
||||||
"pixel cursor-pointer hover:scale-110 hover:z-10 absolute",
|
"pixel cursor-pointer hover:scale-110 hover:z-10 absolute",
|
||||||
cooldownActive && "cursor-not-allowed"
|
cooldownActive && "cursor-not-allowed",
|
||||||
|
isPreview && !cooldownActive && "pixel-preview"
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: pixelColor,
|
backgroundColor: previewColor,
|
||||||
width: `${pixelSize}px`,
|
width: `${pixelSize}px`,
|
||||||
height: `${pixelSize}px`,
|
height: `${pixelSize}px`,
|
||||||
left: `${x * pixelSize}px`,
|
left: `${x * pixelSize}px`,
|
||||||
top: `${y * pixelSize}px`
|
top: `${y * pixelSize}px`,
|
||||||
|
opacity: isPreview && !cooldownActive ? 0.7 : 1
|
||||||
}}
|
}}
|
||||||
onClick={() => handlePixelClick(x, y)}
|
onClick={() => handlePixelClick(x, y)}
|
||||||
onMouseEnter={() => handlePixelMouseEnter(x, y)}
|
onMouseEnter={() => handlePixelMouseEnter(x, y)}
|
||||||
@ -178,6 +188,11 @@ export function Canvas({
|
|||||||
Mouse: ({mouseCoords.x}, {mouseCoords.y})
|
Mouse: ({mouseCoords.x}, {mouseCoords.y})
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{previewPixel && !cooldownActive && (
|
||||||
|
<div className="text-xs text-blue-400 mt-1">
|
||||||
|
Vorschau: {selectedColor}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Zoom Controls */}
|
{/* Zoom Controls */}
|
||||||
|
|||||||
@ -148,7 +148,7 @@
|
|||||||
|
|
||||||
/* Optimierte Pixel-Hover-Effekte */
|
/* Optimierte Pixel-Hover-Effekte */
|
||||||
.pixel {
|
.pixel {
|
||||||
transition: transform 0.15s ease-out, box-shadow 0.15s ease-out;
|
transition: transform 0.15s ease-out, box-shadow 0.15s ease-out, opacity 0.1s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pixel:hover {
|
.pixel:hover {
|
||||||
@ -158,6 +158,18 @@
|
|||||||
box-shadow: 0 0 8px rgba(255, 255, 255, 0.3);
|
box-shadow: 0 0 8px rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pixel-Vorschau */
|
||||||
|
.pixel-preview {
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.8);
|
||||||
|
box-shadow: 0 0 12px rgba(255, 255, 255, 0.5);
|
||||||
|
animation: previewPulse 1s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes previewPulse {
|
||||||
|
0% { box-shadow: 0 0 12px rgba(255, 255, 255, 0.5); }
|
||||||
|
100% { box-shadow: 0 0 20px rgba(255, 255, 255, 0.8); }
|
||||||
|
}
|
||||||
|
|
||||||
/* Toast animations */
|
/* Toast animations */
|
||||||
.toast-enter {
|
.toast-enter {
|
||||||
animation: slideInRight 0.3s ease-out;
|
animation: slideInRight 0.3s ease-out;
|
||||||
|
|||||||
1
exports/canvas-2025-08-18T12-40-19-350Z.svg
Normal file
1
exports/canvas-2025-08-18T12-40-19-350Z.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="#FFFFFF"/></svg>
|
||||||
|
After Width: | Height: | Size: 120 B |
1
exports/canvas-2025-08-18T12-41-19-351Z.svg
Normal file
1
exports/canvas-2025-08-18T12-41-19-351Z.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="#FFFFFF"/></svg>
|
||||||
|
After Width: | Height: | Size: 120 B |
Loading…
Reference in New Issue
Block a user