feat: blurhash placeholder while image loads

- Add BlurhashCanvas component (decodes hash → canvas pixel data)
- DrawCanvas: expose onImageLoad callback prop
- DrawIt + GenerateIt: show blurhash layer until real image is ready,
  reset imageLoaded state on picture navigation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-10 08:09:09 +02:00
parent 7c983a7460
commit f4b082329e
6 changed files with 89 additions and 4 deletions

View File

@@ -1,5 +1,6 @@
import { useState, useEffect, useCallback, useRef } from 'react'
import DrawCanvas, { type DrawCanvasHandle } from '../components/DrawCanvas'
import BlurhashCanvas from '../components/BlurhashCanvas'
import Topbar from '../components/Topbar'
import {
getDbPictures,
@@ -57,6 +58,7 @@ export default function DrawIt() {
const [finishing, setFinishing] = useState(false)
const [statusMsg, setStatusMsg] = useState('')
const [statusError, setStatusError] = useState(false)
const [imageLoaded, setImageLoaded] = useState(false)
const canvasRef = useRef<DrawCanvasHandle>(null)
@@ -94,6 +96,7 @@ export default function DrawIt() {
if (!currentPicture || !token) {
setObjects([]); setSelectedObjectId(null)
setPictureWords([]); setPendingWords([])
setImageLoaded(false)
return
}
getDbObjects(currentPicture.id, token)
@@ -315,8 +318,17 @@ export default function DrawIt() {
<main className="canvas-area">
<div
className="canvas-frame"
style={currentPicture ? { background: 'var(--surface-2)' } : undefined}
style={{ position: 'relative', background: 'var(--surface-2)' }}
>
{/* Blurhash-Platzhalter: sichtbar solange das echte Bild noch lädt */}
{currentPicture?.blurhash && !imageLoaded && (
<BlurhashCanvas
hash={currentPicture.blurhash}
width={32}
height={32}
style={{ zIndex: 1 }}
/>
)}
<DrawCanvas
ref={canvasRef}
imageSrc={currentPicture && token ? directusAssetUrl(currentPicture.picture, token) : null}
@@ -324,6 +336,7 @@ export default function DrawIt() {
selectedObjectId={selectedObjectId}
mode={mode}
onHasSelection={handleHasSelection}
onImageLoad={() => setImageLoaded(true)}
/>
</div>
</main>