-
-
Save ngbrown/d62eb518753378eb0a9bf02bb4723235 to your computer and use it in GitHub Desktop.
useBlurhash hook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useCallback } from "react"; | |
import { useBlurhash } from "./use-blurhash"; | |
import { useInView } from "react-intersection-observer"; | |
type Props = React.DetailedHTMLProps< | |
React.ImgHTMLAttributes<HTMLImageElement>, | |
HTMLImageElement | |
> & { blurhash?: string | null }; | |
// Uses browser-native `loading="lazy"` to lazy load images | |
// Renders a blurhash value to a blob when it about to appear on screen. | |
// Only renders the blurhash when the image hasn't loaded yet. | |
// Removes the blob once the image has finished loading. | |
export function BlurImg(allProps: Props) { | |
const { loading = "lazy", blurhash, style, ...props } = allProps; | |
const [imgLoaded, setImgLoaded] = useState(false); | |
const [ref, inView] = useInView({ rootMargin: "110%" }); | |
const blurUrl = useBlurhash(!imgLoaded && inView ? blurhash : null); | |
const handleOnLoad = useCallback(() => { | |
setImgLoaded(true); | |
}, []); | |
const newStyle = blurUrl | |
? { | |
...style, | |
backgroundImage: `url("${blurUrl}")`, | |
backgroundSize: | |
props.width && props.height | |
? `${props.width}px ${props.height}px` | |
: "100% 100%" | |
} | |
: style; | |
return ( | |
<img | |
ref={ref} | |
{...props} | |
loading={loading} | |
onLoad={handleOnLoad} | |
style={newStyle} | |
/> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useState } from "react"; | |
import { decode } from "blurhash"; | |
// modified from https://gist.github.com/WorldMaker/a3cbe0059acd827edee568198376b95a | |
// https://github.com/woltapp/react-blurhash/issues/3 | |
export function useBlurhash( | |
blurhash: string | undefined | null, | |
width: number = 32, | |
height: number = 32, | |
punch: number = 1 | |
) { | |
punch = punch || 1; | |
const [url, setUrl] = useState(null as string | null); | |
useEffect(() => { | |
let isCancelled = false; | |
if (!blurhash) return; | |
// decode hash | |
const pixels = decode(blurhash, width, height, punch); | |
// temporary canvas to create a blob from decoded ImageData | |
const canvas = document.createElement("canvas"); | |
canvas.width = width; | |
canvas.height = height; | |
const context = canvas.getContext("2d"); | |
const imageData = context!.createImageData(width, height); | |
imageData.data.set(pixels); | |
context!.putImageData(imageData, 0, 0); | |
canvas.toBlob(blob => { | |
if (!isCancelled) { | |
setUrl(oldUrl => { | |
if (oldUrl) { | |
URL.revokeObjectURL(oldUrl); | |
} | |
return URL.createObjectURL(blob); | |
}); | |
} | |
}); | |
return function cleanupBlurhash() { | |
isCancelled = true; | |
setUrl(oldUrl => { | |
if (oldUrl) { | |
URL.revokeObjectURL(oldUrl); | |
} | |
return null; | |
}); | |
}; | |
}, [blurhash, height, width, punch]); | |
return url; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment