This commit is contained in:
seb
2026-08-01 02:08:39 +02:00
parent 6d720d34ed
commit 4ea593a0c8
7 changed files with 211 additions and 33 deletions

View File

@@ -0,0 +1,69 @@
import { useEffect, useRef } from 'react';
import Box from '@mui/material/Box';
import { thumbHashToRGBA } from 'thumbhash';
function decodeThumbHash(thumbHash) {
const bytes = Uint8Array.from(atob(thumbHash), (c) => c.charCodeAt(0));
return thumbHashToRGBA(bytes);
}
function paintThumbHash(canvas, thumbHash) {
const parent = canvas.parentElement;
if (!parent) return;
const { width, height } = parent.getBoundingClientRect();
if (width === 0 || height === 0) return;
const { w, h, rgba } = decodeThumbHash(thumbHash);
const src = document.createElement('canvas');
src.width = w;
src.height = h;
src.getContext('2d').putImageData(
new ImageData(new Uint8ClampedArray(rgba), w, h),
0,
0,
);
canvas.width = Math.round(width);
canvas.height = Math.round(height);
const ctx = canvas.getContext('2d');
if (!ctx) return;
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(src, 0, 0, canvas.width, canvas.height);
}
export default function ThumbHashImage({ thumbHash, sx }) {
const canvasRef = useRef(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas || !thumbHash) return;
paintThumbHash(canvas, thumbHash);
const parent = canvas.parentElement;
if (!parent || typeof ResizeObserver === 'undefined') return undefined;
const observer = new ResizeObserver(() => {
paintThumbHash(canvas, thumbHash);
});
observer.observe(parent);
return () => observer.disconnect();
}, [thumbHash]);
return (
<Box
component="canvas"
ref={canvasRef}
aria-hidden
sx={{
display: 'block',
width: '100%',
height: '100%',
...sx,
}}
/>
);
}