Last active
June 2, 2023 10:27
-
-
Save usame-algan/d7adfe4be1008568274714cb8d22b216 to your computer and use it in GitHub Desktop.
Obfuscate/Blur element
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
type Color = { r: number, g: number, b: number, a: number } | |
// Helpers | |
function getTextBoundingBox(ctx: CanvasRenderingContext2D, text: string) { | |
const metrics = ctx.measureText(text) | |
const top = metrics.actualBoundingBoxAscent * -1 | |
const bottom = metrics.actualBoundingBoxDescent | |
const height = bottom - top | |
return { top, height } | |
} | |
function getAverageColor(imageData: Uint8ClampedArray): Color { | |
let r = 0, | |
g = 0, | |
b = 0, | |
a = 0, | |
count = 0 | |
for (let i = 0; i < imageData.length; i += 4) { | |
r += imageData[i] | |
g += imageData[i + 1] | |
b += imageData[i + 2] | |
a += imageData[i + 3] | |
count++ | |
} | |
r /= count | |
g /= count | |
b /= count | |
a /= count | |
a = a / 100 | |
return {r,g,b,a} | |
} | |
function createMosaicBlock(x: number, y: number, cellSize: number, color: Color) { | |
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect') | |
rect.setAttribute('x', x.toString()) | |
rect.setAttribute('y', y.toString()) | |
rect.setAttribute('width', cellSize.toString()) | |
rect.setAttribute('height', cellSize.toString()) | |
rect.setAttribute('fill', `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a.toFixed(2)})`) | |
return rect | |
} | |
function createCanvas(element: HTMLElement) { | |
const canvas = document.createElement('canvas') | |
canvas.width = element.offsetWidth | |
canvas.height = element.offsetHeight | |
const ctx = canvas.getContext('2d', {willReadFrequently: true}) | |
if (!ctx) return {} | |
const textStyle = getComputedStyle(element) | |
ctx.textBaseline = 'top' | |
ctx.font = textStyle.font | |
ctx.fillStyle = textStyle.color | |
const { top, height } = getTextBoundingBox(ctx, element.innerText) | |
const middle_y = top + height / 2 | |
const offsetTop = canvas.height / 2 - middle_y | |
// Draw the text onto the canvas | |
ctx.fillText(element.innerText, 0, offsetTop) | |
return { canvas, ctx } | |
} | |
function createSVG(element: HTMLElement) { | |
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') | |
svg.setAttribute('width', element.offsetWidth.toString()) | |
svg.setAttribute('height', element.offsetHeight.toString()) | |
return svg | |
} | |
/** | |
* Visually blur/obfuscate the contents of an element | |
* 123$ -> ◼◽◾ | |
*/ | |
function obfuscateElement(element: HTMLElement) { | |
const { canvas, ctx } = createCanvas(element) | |
if (!canvas || !ctx) throw Error("Could not create canvas") | |
const svg = createSVG(element) | |
const cellSize = element.offsetHeight / 2 | |
for (let x = 0; x < canvas.width; x += cellSize) { | |
for (let y = 0; y < canvas.height; y += cellSize) { | |
const imageData = ctx.getImageData(x, y, cellSize, cellSize).data | |
const color = getAverageColor(imageData) | |
const mosaic = createMosaicBlock(x, y, cellSize, color) | |
svg.appendChild(mosaic) | |
} | |
} | |
return svg | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment