This commit is contained in:
seb
2026-10-04 09:21:45 +02:00
parent 4edd417dab
commit da318223c1
8 changed files with 315 additions and 166 deletions

View File

@@ -6,7 +6,7 @@ import Typography from '@mui/material/Typography';
import { Tile, tileBaseSx } from 'dashgui/tiles';
import { formatPrice } from '../../utils/price';
import { articleSku } from '../article/articleRoute';
import { decodeThumbHash } from '../article/ThumbHashImage';
import { getThumbHashDataUrl, getCachedImage, imageCache } from '../article/ThumbHashImage';
import { BrandLabel } from '../brand';
import { thumb } from '../../api';
@@ -100,12 +100,22 @@ class ArticleTileBackdrop extends PureComponent {
img = null;
observer = null;
constructor(props) {
super(props);
this.thumbUrl = getThumbHashDataUrl(props.thumbHash);
this.img = getCachedImage(props.fullHash);
}
componentDidMount() {
this.loadImage();
}
componentDidUpdate(prevProps) {
if (prevProps.thumbHash !== this.props.thumbHash) {
this.thumbUrl = getThumbHashDataUrl(this.props.thumbHash);
}
if (prevProps.fullHash !== this.props.fullHash) {
this.img = getCachedImage(this.props.fullHash);
this.loadImage();
}
}
@@ -118,8 +128,15 @@ class ArticleTileBackdrop extends PureComponent {
loadImage() {
const { fullHash } = this.props;
if (fullHash) {
const cached = getCachedImage(fullHash);
if (cached) {
this.img = cached;
this.paint();
return;
}
const img = new Image();
img.onload = () => {
imageCache.set(fullHash, img);
if (this.img === img) {
this.paint();
}
@@ -127,15 +144,18 @@ class ArticleTileBackdrop extends PureComponent {
img.src = thumb.url(fullHash);
this.img = img;
if (img.complete && img.naturalWidth) {
imageCache.set(fullHash, img);
this.paint();
return;
}
}
this.paint();
}
setCanvas = (canvas) => {
this.canvasRef = canvas;
if (!this.img && this.props.fullHash) {
this.img = getCachedImage(this.props.fullHash);
}
if (canvas && typeof ResizeObserver !== 'undefined') {
this.observer?.disconnect();
this.observer = new ResizeObserver(() => this.paint());
@@ -157,8 +177,9 @@ class ArticleTileBackdrop extends PureComponent {
const ctx = canvas.getContext('2d');
if (!ctx) return;
const img = this.img;
const img = this.img || getCachedImage(this.props.fullHash);
if (img && img.complete && img.naturalWidth && img.naturalHeight) {
this.img = img;
drawContainedWithStretchedEdges(
ctx,
img,
@@ -167,41 +188,49 @@ class ArticleTileBackdrop extends PureComponent {
dstW,
dstH,
);
return;
}
const { thumbHash } = this.props;
if (thumbHash) {
const decoded = decodeThumbHash(thumbHash);
if (decoded) {
const { w, h, rgba } = decoded;
const temp = document.createElement('canvas');
temp.width = w;
temp.height = h;
temp.getContext('2d').putImageData(
new ImageData(new Uint8ClampedArray(rgba), w, h),
0,
0,
);
drawContainedWithStretchedEdges(ctx, temp, w, h, dstW, dstH);
}
}
};
render() {
const hasCachedImage = Boolean(this.img && this.img.complete && this.img.naturalWidth);
return (
<Box
component="canvas"
ref={this.setCanvas}
aria-hidden
sx={{
position: 'absolute',
inset: 0,
width: '100%',
height: '100%',
display: 'block',
overflow: 'hidden',
}}
/>
>
{!hasCachedImage && this.thumbUrl ? (
<Box
component="img"
src={this.thumbUrl}
alt=""
aria-hidden
sx={{
position: 'absolute',
inset: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
}}
/>
) : null}
<Box
component="canvas"
ref={this.setCanvas}
aria-hidden
sx={{
position: 'absolute',
inset: 0,
width: '100%',
height: '100%',
display: 'block',
}}
/>
</Box>
);
}
}