Created
May 7, 2023 00:11
-
-
Save jurijsk/1d4e269af48b1c241c9d7192ee0f242a to your computer and use it in GitHub Desktop.
Vuejs dynamic font size component aimed to scale the text to occupy all available space.
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
<script lang="ts"> | |
import { nextTick, onMounted, onUnmounted, ref } from 'vue' | |
export default { | |
expose: [], | |
async mounted() { | |
const firstEl = <Text>this.$el; | |
if (!firstEl.nextElementSibling) { | |
return; | |
} | |
target = <HTMLElement>firstEl.nextElementSibling; | |
if (!single) { | |
console.log("LineScaler: Expecting single element, got more then one"); | |
return; | |
} | |
if (!target || target.nodeType != Node.ELEMENT_NODE) { | |
console.log("LineScaler: Expecting HTML element to fit"); | |
return; | |
} | |
ruller = <HTMLElement>target.cloneNode(true); | |
setupRuller(ruller); | |
target.parentNode.insertBefore(ruller, target.nextSibling) | |
await nextTick(); | |
resizeObserver = new ResizeObserver(this.onResize) | |
resizeObserver.observe(target, { box: 'content-box' }); | |
}, | |
methods: { | |
isSingle(value: boolean) { | |
single = value; | |
}, | |
onResize(entries: ResizeObserverEntry[], observer: ResizeObserver) { | |
if (entries.length == 0) { | |
return; | |
} | |
const entry = entries[0]; | |
if (entry.target != target) { | |
//this is not normal | |
return; | |
} | |
try { | |
const targetWidth = entry.contentRect.width; | |
const rullerWidth = ruller.getBoundingClientRect().width; | |
let dif = targetWidth - rullerWidth; | |
if (dif >= tollerance[0] && dif <= tollerance[1]) { | |
//console.log(`returning. dif: ${dif} `) | |
return; | |
} | |
let targetFontSize = parseFloat(window.getComputedStyle(target).fontSize.slice(0, -2)); | |
let newSize = targetFontSize * Math.abs(targetWidth / rullerWidth) + 'px'; | |
//console.log(`target w: ${targetWidth} | dif: ${dif} | ${newSize}`) | |
target.style.setProperty("font-size", newSize) | |
ruller.style.setProperty("font-size", newSize) | |
} catch (error) { | |
//silently fail to resize | |
} | |
} | |
}, | |
unmounted() { | |
resizeObserver && resizeObserver.disconnect(); | |
} | |
} | |
function setupRuller(element: HTMLElement) { | |
element.style.setProperty("width", "unset") | |
element.style.setProperty("position", "absolute") /* makes width unset/auto */ | |
element.style.setProperty("opacity", "0") | |
element.style.setProperty("right", "100vw") //shove aside for convinience | |
element.style.setProperty("contain", "paint") //linits reflow | |
//not removing id cos it can change styling | |
element.setAttribute("aria-hidden", "true"); //hides from readers, to improve a13y | |
} | |
let target: HTMLElement; | |
let ruller: HTMLElement; | |
let single = true; | |
let tollerance = [-10, 2]; //allow 10px smaller but only 2 px bigger. | |
let resizeObserver: ResizeObserver; | |
</script> | |
<template> | |
<template v-if="$slots.default().length == 1"> | |
<slot /> | |
</template> | |
<template v-else> | |
<!-- LineScaler: we can only size then there is only one element in the template. Render as is peace off. --> | |
{{ isSingle(false) }} | |
<slot /> | |
</template> | |
</template> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment