Created
August 23, 2023 14:50
-
-
Save cbratschi/a98c3b6270caa561c051af7cb17edb8b to your computer and use it in GitHub Desktop.
Blurhash to CSS (TypeScript example)
This file contains hidden or 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 { 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