diff --git a/js/game-datcards-improved.user.js b/js/game-datcards-improved.user.js index cb96723..4f1dc43 100644 --- a/js/game-datcards-improved.user.js +++ b/js/game-datcards-improved.user.js @@ -196,10 +196,35 @@ const FONT_STYLE_ID = 'gdc-card-font'; const FONT_PREF_KEY = 'gdc-card-font-enabled'; - // Sizes below are the official ones for a full size card. The app draws cards - // scaled by --card-scaling-factor, so every size is scaled along with it. + // The official sizes are point sizes on a printed card, and CSS pt units say + // nothing about how large the app happens to draw that card. So instead of + // using pt directly, measure each card and derive how many layout pixels one + // point of the printed card is worth; --gdc-pt below carries that value. + // Printed datacard width, 7in. Raising it shrinks all card text and lowering + // it grows it, so it doubles as the tuning knob for the overall type size: + // localStorage.setItem('gdc-card-width-pt', 560) + const CARD_WIDTH_PT = 504; + + function cardWidthPt() { + return Number(localStorage.getItem('gdc-card-width-pt')) || CARD_WIDTH_PT; + } + function pt(size) { - return `calc(${size}pt * var(--card-scaling-factor, 1))`; + return `calc(${size} * var(--gdc-pt, 1.333px))`; + } + + // offsetWidth is the untransformed layout width, so this stays correct + // whether the app zooms cards by scaling the layout or by a CSS transform. + function updateFontScale() { + const reference = cardWidthPt(); + document.querySelectorAll('.unit').forEach((unit) => { + const width = unit.offsetWidth; + if (!width) return; + const value = width / reference + 'px'; + if (unit.style.getPropertyValue('--gdc-pt') !== value) { + unit.style.setProperty('--gdc-pt', value); + } + }); } function cardFontCss() { @@ -262,16 +287,49 @@ return localStorage.getItem(FONT_PREF_KEY) === '1'; } + // Cards get re-rendered on every edit and resized by the zoom control, so the + // scale has to be recomputed rather than set once. + var domObserver = null; + var sizeObserver = null; + var scalePending = false; + + function scheduleFontScale() { + if (scalePending) return; + scalePending = true; + requestAnimationFrame(() => { + scalePending = false; + updateFontScale(); + document.querySelectorAll('.unit').forEach((unit) => sizeObserver.observe(unit)); + }); + } + function applyCardFont(enabled) { const existing = document.getElementById(FONT_STYLE_ID); + if (!enabled) { if (existing) existing.remove(); + if (domObserver) { + domObserver.disconnect(); + sizeObserver.disconnect(); + domObserver = null; + sizeObserver = null; + } return; } + const style = existing || document.createElement('style'); style.id = FONT_STYLE_ID; style.textContent = cardFontCss(); if (!existing) document.head.appendChild(style); + + if (!domObserver) { + // childList only: the scale is written to the cards as an inline custom + // property, and watching attributes here would retrigger on our own write. + sizeObserver = new ResizeObserver(scheduleFontScale); + domObserver = new MutationObserver(scheduleFontScale); + domObserver.observe(document.body, { childList: true, subtree: true }); + } + scheduleFontScale(); } function syncCardFontButton() {