tutu/src/material/Img.tsx
thislight 1c8a3f0bbb
All checks were successful
/ depoly (push) Successful in 1m22s
remove utils
* add ~platform/DocumentTitle
* add titles for some pages
2025-01-02 22:43:37 +08:00

127 lines
2.8 KiB
TypeScript

import {
JSX,
splitProps,
Component,
createSignal,
onMount,
createRenderEffect,
Show,
} from "solid-js";
import { css } from "solid-styled";
import { decode } from "blurhash";
type ImgProps = {
blurhash?: string;
keepBlur?: boolean;
} & JSX.HTMLElementTags["img"];
const Img: Component<ImgProps> = (props) => {
let canvas: HTMLCanvasElement;
let imgE: HTMLImageElement;
const [managed, passthough] = splitProps(props, [
"blurhash",
"keepBlur",
"class",
"classList",
"style",
]);
const [isImgLoaded, setIsImgLoaded] = createSignal(false);
const [imgSize, setImgSize] = createSignal<{
width: number;
height: number;
}>();
const isBlurEnabled = () => managed.keepBlur || !isImgLoaded();
css`
:where(.img-root) {
display: inline-block;
position: relative;
> img:first-of-type {
object-fit: contain;
object-position: center;
width: 100%;
height: 100%;
visibility: ${isBlurEnabled() ? "hidden" : "initial"};
}
}
:where(.cover) {
display: ${isBlurEnabled() ? "block" : "none"};
position: absolute;
left: 0;
top: 0;
height: ${`${imgSize()?.height ?? 0}px`};
width: ${`${imgSize()?.width ?? 0}px`};
}
`;
const onImgLoaded = () => {
setIsImgLoaded(true);
setImgSize({
width: imgE!.width,
height: imgE!.height,
});
};
const onMetadataLoaded = () => {
setImgSize({
width: imgE!.width,
height: imgE!.height,
});
};
onMount(() => {
setImgSize((x) => {
const parent = imgE!.parentElement;
if (!parent) return x;
return x
? x
: {
width: parent.clientWidth,
height: parent.clientHeight,
};
});
});
return (
<div
classList={{
...managed.classList,
[managed.class ?? ""]: true,
"img-root": true,
}}
style={managed.style}
>
<Show when={managed.blurhash}>
<canvas
ref={(canvas) => {
createRenderEffect(() => {
if (!managed.blurhash) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const size = imgSize();
if (!size) return;
const imgd = ctx?.createImageData(size.width, size.height);
const pixels = decode(managed.blurhash, size.width, size.height);
imgd.data.set(pixels);
ctx.putImageData(imgd, 0, 0);
});
}}
class="cover"
role="presentation"
/>
</Show>
<img
ref={imgE!}
{...passthough}
onLoad={onImgLoaded}
onLoadedMetadata={onMetadataLoaded}
/>
</div>
);
};
export default Img;