Skip to content

Instantly share code, notes, and snippets.

@aleclarson
Created August 19, 2024 17:50
Show Gist options
  • Save aleclarson/a92f76f7bcd3391f7d19880246164abc to your computer and use it in GitHub Desktop.
Save aleclarson/a92f76f7bcd3391f7d19880246164abc to your computer and use it in GitHub Desktop.
// Adapted from: https://www.npmjs.com/package/to-px
const PIXELS_PER_INCH = measure('in', document.body) // 96
export function toPixels(input: number | string, element: HTMLElement = document.body): number {
if (typeof input === 'number') return input
input = input.toLowerCase()
// Support passing a unit with no amount prefix
let px = unitToPixels(input, element)
if (px !== undefined) {
return px
}
const [amount, unit] = parseUnit(input)
if (!unit) {
return amount
}
px = unitToPixels(unit, element)
if (px !== undefined) {
return amount * px
}
return measure(input, element, false)
}
function unitToPixels(unit: string, element: HTMLElement) {
switch (unit) {
case '%': //Ambiguous, not sure if we should use width or height
return element.clientHeight / 100.0
case 'ch':
case 'ex':
return measure(unit, element)
case 'em':
return computeProperty(element, 'font-size')
case 'rem':
return computeProperty(document.body, 'font-size')
case 'vw':
return window.innerWidth / 100
case 'vh':
return window.innerHeight / 100
case 'vmin':
return Math.min(window.innerWidth, window.innerHeight) / 100
case 'vmax':
return Math.max(window.innerWidth, window.innerHeight) / 100
case 'in':
return PIXELS_PER_INCH
case 'cm':
return PIXELS_PER_INCH / 2.54
case 'mm':
return PIXELS_PER_INCH / 25.4
case 'pt':
return PIXELS_PER_INCH / 72
case 'pc':
return PIXELS_PER_INCH / 6
case 'px':
return 1
}
}
function parseUnit(str: string) {
const unitMatch = str.match(/[\d.\-\+]*(.*)/)
return [parseFloat(str), unitMatch ? unitMatch[1] : ''] as const
}
function computeProperty(element: HTMLElement, prop: string) {
const [amount, unit] = parseUnit(getComputedStyle(element).getPropertyValue(prop))
return amount * toPixels(unit, element)
}
export function measure(length: string, element: HTMLElement, isUnit = true) {
const testDIV = document.createElement('div')
testDIV.style.height = isUnit ? 128 + length : length
element.appendChild(testDIV)
let size = computeProperty(testDIV, 'height')
if (isUnit) {
size /= 128
}
element.removeChild(testDIV)
return size
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment