diff --git a/src/App.tsx b/src/App.tsx index 4106b83..f64d2b5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -37,21 +37,21 @@ function App() { } else if (settings.selectedGenerator === 'art-institute') { newImages = await generateArtInstituteImages(settings.gridSize, 512) } else if (settings.selectedGenerator === 'waveform') { - newImages = generateWaveformImages(settings.gridSize, 2048) + newImages = generateWaveformImages(settings.gridSize, 512) } else if (settings.selectedGenerator === 'partials') { - newImages = generatePartialsImages(settings.gridSize, 2048) + newImages = generatePartialsImages(settings.gridSize, 512) } else if (settings.selectedGenerator === 'slides') { - newImages = generateSlidesImages(settings.gridSize, 2048) + newImages = generateSlidesImages(settings.gridSize, 512) } else if (settings.selectedGenerator === 'shapes') { - newImages = generateShapesImages(settings.gridSize, 2048) + newImages = generateShapesImages(settings.gridSize, 512) } else if (settings.selectedGenerator === 'bands') { - newImages = generateBandsImages(settings.gridSize, 2048) + newImages = generateBandsImages(settings.gridSize, 512) } else if (settings.selectedGenerator === 'dust') { - newImages = generateDustImages(settings.gridSize, 2048) + newImages = generateDustImages(settings.gridSize, 512) } else if (settings.selectedGenerator === 'geopattern') { newImages = await generateGeopatternImages(settings.gridSize, 512) } else if (settings.selectedGenerator === 'harmonics') { - newImages = generateHarmonicsImages(settings.gridSize, 2048) + newImages = generateHarmonicsImages(settings.gridSize, 512) } else { newImages = [] } @@ -69,7 +69,7 @@ function App() {
{settings.selectedGenerator === 'from-photo' ? ( - + ) : settings.selectedGenerator === 'webcam' ? ( ) : ( diff --git a/src/components/AudioPanel.tsx b/src/components/AudioPanel.tsx index 449d5af..4549e41 100644 --- a/src/components/AudioPanel.tsx +++ b/src/components/AudioPanel.tsx @@ -17,6 +17,7 @@ export default function AudioPanel() { const [isPlaying, setIsPlaying] = useState(false) const [volume, setVolume] = useState(0.7) const audioPlayerRef = useRef(null) + const drawnImageIdRef = useRef(null) const updateParam = (key: K, value: (typeof params)[K]) => { @@ -133,7 +134,7 @@ export default function AudioPanel() {
{ - if (canvas && selected.canvas) { + if (canvas && selected.canvas && drawnImageIdRef.current !== selected.id) { const ctx = canvas.getContext('2d')! canvas.width = selected.canvas.width canvas.height = selected.canvas.height @@ -147,6 +148,7 @@ export default function AudioPanel() { } ctx.drawImage(selected.canvas, 0, 0) + drawnImageIdRef.current = selected.id } }} className="w-full h-full block" diff --git a/src/components/ImageGrid.tsx b/src/components/ImageGrid.tsx index f5f9acf..bf2ac59 100644 --- a/src/components/ImageGrid.tsx +++ b/src/components/ImageGrid.tsx @@ -1,45 +1,59 @@ import { useStore } from '@nanostores/react' -import { useState } from 'react' +import { useRef, memo } from 'react' import { generatedImages, selectedImage, isGenerating } from '../stores' import type { GeneratedImage } from '../stores' +const ImageGridItem = memo(({ image, isSelected, onClick }: { + image: GeneratedImage + isSelected: boolean + onClick: () => void +}) => { + const drawnRef = useRef(false) + + return ( + + ) +}) + +ImageGridItem.displayName = 'ImageGridItem' + export default function ImageGrid() { const images = useStore(generatedImages) const selected = useStore(selectedImage) const generating = useStore(isGenerating) - const [hoveredImage, setHoveredImage] = useState(null) - const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) - const [localMousePosition, setLocalMousePosition] = useState({ x: 0, y: 0 }) const handleImageClick = (image: GeneratedImage) => { selectedImage.set(image) } - const handleMouseEnter = (image: GeneratedImage, event: React.MouseEvent) => { - setHoveredImage(image) - setMousePosition({ x: event.clientX, y: event.clientY }) - const rect = event.currentTarget.getBoundingClientRect() - setLocalMousePosition({ - x: (event.clientX - rect.left) / rect.width, - y: (event.clientY - rect.top) / rect.height, - }) - } - - const handleMouseMove = (event: React.MouseEvent) => { - setMousePosition({ x: event.clientX, y: event.clientY }) - if (hoveredImage) { - const rect = event.currentTarget.getBoundingClientRect() - setLocalMousePosition({ - x: (event.clientX - rect.left) / rect.width, - y: (event.clientY - rect.top) / rect.height, - }) - } - } - - const handleMouseLeave = () => { - setHoveredImage(null) - } - if (generating) { return (
@@ -71,87 +85,17 @@ export default function ImageGrid() { return (
-
+
{images.map(image => ( - + /> ))}
- - {hoveredImage && ( -
- { - if (canvas && hoveredImage.canvas) { - const ctx = canvas.getContext('2d')! - canvas.width = 200 - canvas.height = 200 - - const zoomFactor = 1.5 - const sourceCanvas = hoveredImage.canvas - const sourceWidth = sourceCanvas.width - const sourceHeight = sourceCanvas.height - - const centerX = localMousePosition.x * sourceWidth - const centerY = localMousePosition.y * sourceHeight - - const cropSize = 200 / zoomFactor - const cropX = Math.max(0, Math.min(sourceWidth - cropSize, centerX - cropSize / 2)) - const cropY = Math.max(0, Math.min(sourceHeight - cropSize, centerY - cropSize / 2)) - - if (hoveredImage.generator === 'tixy') { - ctx.imageSmoothingEnabled = false - } else { - ctx.imageSmoothingEnabled = true - } - - ctx.drawImage(sourceCanvas, cropX, cropY, cropSize, cropSize, 0, 0, 200, 200) - } - }} - className="w-full h-full block" - style={{ imageRendering: hoveredImage.generator === 'tixy' ? 'pixelated' : 'auto' }} - /> -
- )}
) } diff --git a/src/stores/index.ts b/src/stores/index.ts index f9b882f..dd7cfa9 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -33,7 +33,7 @@ export interface AppSettings { export const appSettings = atom({ selectedGenerator: 'tixy', - gridSize: 25, + gridSize: 16, backgroundColor: '#000000', foregroundColor: '#ffffff', })