Skip to content

Instantly share code, notes, and snippets.

@jurijsk
Created May 7, 2023 00:11
Show Gist options
  • Save jurijsk/1d4e269af48b1c241c9d7192ee0f242a to your computer and use it in GitHub Desktop.
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.
<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