Skip to content

Instantly share code, notes, and snippets.

@cbratschi
Created August 23, 2023 14:50
Show Gist options
  • Save cbratschi/a98c3b6270caa561c051af7cb17edb8b to your computer and use it in GitHub Desktop.
Save cbratschi/a98c3b6270caa561c051af7cb17edb8b to your computer and use it in GitHub Desktop.
Blurhash to CSS (TypeScript example)
import { decode } from 'blurhash';
import { CSSProperties } from 'react';
/**
* Get a pixel value.
*
* @param pixelBytes
* @param x
* @param y
* @param width
* @param index
* @returns
*/
function getPixel(pixelBytes: Uint8ClampedArray, x: number, y: number, width: number, index: 0 | 1 | 2): number {
const numberOfChannels = 4; //RGBa
const bytesPerRow = width * numberOfChannels;
return pixelBytes[(numberOfChannels * x + index + y * bytesPerRow)];
}
/**
* Convert to percent value and round value.
*
* @param part
* @param whole
* @returns
*/
function getRoundedPercentageOf(part: number, whole: number): number {
const value = part / whole * 100;
return Math.round(value);
}
/**
* Convert a blurhash to CSS background image data.
*
* @param blurhash
* @param width
* @param height
* @returns
*/
export function convertBlurhashToCss(blurhash: string, width: number, height: number): CSSProperties {
const pixelBytes = decode(blurhash, width, height, 1);
const rIndex = 0;
const gIndex = 1;
const bIndex = 2;
//const backgroundSize = `100% ${100.0 / height}%`; //FIXME needed? did not work
const backgroundSize = '100%';
const backgroundPosition: Array<string> = [];
const linearGradients: Array<string> = [];
for (let y = 0; y < height; y++) {
const rowLinearGradients: Array<string> = [];
for (let x = 0; x < width; x++) {
//get pixels
const r = getPixel(pixelBytes, x, y, width, rIndex);
const g = getPixel(pixelBytes, x, y, width, gIndex);
const b = getPixel(pixelBytes, x, y, width, bIndex);
//calc
const startPercent = x === 0 ? '':`${getRoundedPercentageOf(x, width)}%`;
const endPercent = x === width - 1 ? '':`${getRoundedPercentageOf(x + 1, width)}%`;
const linearGradient = `rgb(${r},${g},${b}) ${startPercent} ${endPercent}`;
rowLinearGradients.push(linearGradient);
}
linearGradients.push(`linear-gradient(90deg,${rowLinearGradients.join(',')})`);
if (y === 0) {
backgroundPosition.push('0 0');
} else {
backgroundPosition.push(`0 ${getRoundedPercentageOf(y, height - 1)}`);
}
}
return {
backgroundImage: linearGradients.join(','),
backgroundPosition: backgroundPosition.join(','),
backgroundRepeat: 'no-repeat',
backgroundSize: backgroundSize,
//filter: 'blur(24px)', //FIXME affects img element itself
//transform: 'scale(1.2)' //FIXME why?
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment