Skip to content

Instantly share code, notes, and snippets.

@prowlee
Forked from ngbrown/BlurImg.tsx
Created August 9, 2022 02:48
Show Gist options
  • Select an option

  • Save prowlee/b00cee6e56cbc4333f84d583f67ed733 to your computer and use it in GitHub Desktop.

Select an option

Save prowlee/b00cee6e56cbc4333f84d583f67ed733 to your computer and use it in GitHub Desktop.

Revisions

  1. @ngbrown ngbrown revised this gist Mar 16, 2020. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions BlurImg.tsx
    Original file line number Diff line number Diff line change
    @@ -12,16 +12,16 @@ type Props = React.DetailedHTMLProps<
    // 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 { loading = "lazy", blurhash, style, ...props } = allProps;

    const blurUrl = useBlurhash(!imgLoaded && inView ? blurhash : null);

    const newStyle = blurUrl
    ? {
    ...style,
  2. @ngbrown ngbrown revised this gist Mar 16, 2020. 2 changed files with 81 additions and 27 deletions.
    45 changes: 45 additions & 0 deletions BlurImg.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,45 @@
    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 [imgLoaded, setImgLoaded] = useState(false);
    const [ref, inView] = useInView({ rootMargin: "110%" });
    const handleOnLoad = useCallback(() => {
    setImgLoaded(true);
    }, []);

    const { loading = "lazy", blurhash, style, ...props } = allProps;

    const blurUrl = useBlurhash(!imgLoaded && inView ? blurhash : null);

    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}
    />
    );
    }
    63 changes: 36 additions & 27 deletions use-blurhash.ts
    Original file line number Diff line number Diff line change
    @@ -1,46 +1,55 @@
    import { decode } from 'blurhash'
    import { useEffect, useState } from 'react'
    import { useEffect, useState } from "react";
    import { decode } from "blurhash";

    function useBlurhash (blurhash: string, width: number, height: number, punch: number = 1) {
    punch = punch || 1
    // 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)
    const [url, setUrl] = useState(null as string | null);

    useEffect(() => {
    let isCancelled = false
    let isCancelled = false;

    if (!blurhash) return;

    // decode hash
    const pixels = decode(blurhash, width, height, punch)
    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)
    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)
    URL.revokeObjectURL(oldUrl);
    }
    return URL.createObjectURL(blob)
    })
    return URL.createObjectURL(blob);
    });
    }
    })
    });

    return function cleanupBlurhash () {
    isCancelled = true
    return function cleanupBlurhash() {
    isCancelled = true;
    setUrl(oldUrl => {
    if (oldUrl) {
    URL.revokeObjectURL(oldUrl)
    URL.revokeObjectURL(oldUrl);
    }
    return null
    })
    }
    }, [blurhash, height, width, punch])
    return null;
    });
    };
    }, [blurhash, height, width, punch]);

    return url
    }
    return url;
    }
  3. @WorldMaker WorldMaker revised this gist Dec 6, 2019. 1 changed file with 17 additions and 8 deletions.
    25 changes: 17 additions & 8 deletions use-blurhash.ts
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,8 @@ function useBlurhash (blurhash: string, width: number, height: number, punch: nu
    const [url, setUrl] = useState(null as string | null)

    useEffect(() => {
    let isCancelled = false

    // decode hash
    const pixels = decode(blurhash, width, height, punch)

    @@ -19,19 +21,26 @@ function useBlurhash (blurhash: string, width: number, height: number, punch: nu
    imageData.data.set(pixels)
    context!.putImageData(imageData, 0, 0)
    canvas.toBlob(blob => {
    if (url) {
    URL.revokeObjectURL(url)
    if (!isCancelled) {
    setUrl(oldUrl => {
    if (oldUrl) {
    URL.revokeObjectURL(oldUrl)
    }
    return URL.createObjectURL(blob)
    })
    }
    setUrl(URL.createObjectURL(blob))
    })

    return function cleanupBlurhash () {
    if (url) {
    URL.revokeObjectURL(url)
    setUrl(null)
    }
    isCancelled = true
    setUrl(oldUrl => {
    if (oldUrl) {
    URL.revokeObjectURL(oldUrl)
    }
    return null
    })
    }
    }, [blurhash, height, width, punch])

    return url
    }
    }
  4. @WorldMaker WorldMaker revised this gist Dec 6, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion use-blurhash.ts
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@ function useBlurhash (blurhash: string, width: number, height: number, punch: nu
    // decode hash
    const pixels = decode(blurhash, width, height, punch)

    // temporary canvas to create a blog from decoded ImageData
    // temporary canvas to create a blob from decoded ImageData
    const canvas = document.createElement('canvas')
    canvas.width = width
    canvas.height = height
  5. @WorldMaker WorldMaker created this gist Dec 6, 2019.
    37 changes: 37 additions & 0 deletions use-blurhash.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    import { decode } from 'blurhash'
    import { useEffect, useState } from 'react'

    function useBlurhash (blurhash: string, width: number, height: number, punch: number = 1) {
    punch = punch || 1

    const [url, setUrl] = useState(null as string | null)

    useEffect(() => {
    // decode hash
    const pixels = decode(blurhash, width, height, punch)

    // temporary canvas to create a blog 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 (url) {
    URL.revokeObjectURL(url)
    }
    setUrl(URL.createObjectURL(blob))
    })

    return function cleanupBlurhash () {
    if (url) {
    URL.revokeObjectURL(url)
    setUrl(null)
    }
    }
    }, [blurhash, height, width, punch])

    return url
    }