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 = (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" : "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 (
{ 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" />
); }; export default Img;