initial commit
This commit is contained in:
commit
5449e361d5
46 changed files with 8309 additions and 0 deletions
121
src/material/Img.tsx
Normal file
121
src/material/Img.tsx
Normal file
|
@ -0,0 +1,121 @@
|
|||
import {
|
||||
JSX,
|
||||
splitProps,
|
||||
Component,
|
||||
createSignal,
|
||||
createEffect,
|
||||
onMount,
|
||||
createRenderEffect,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { css } from "solid-styled";
|
||||
import { decode } from "blurhash";
|
||||
import { mergeClass } from "../utils";
|
||||
|
||||
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",
|
||||
"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" : "visible"};
|
||||
}
|
||||
}
|
||||
|
||||
: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 class={mergeClass(managed.class, "img-root")} 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;
|
Loading…
Add table
Add a link
Reference in a new issue