2 Commits
Author SHA1 Message Date
Markk116 5a7dfcc019 take aspect ratio into account when rasterizing 2026-08-08 21:19:25 +02:00
mark 0c2785ca3b fix: sliders in demo require reload to rerender
CSS custom property changes (--pxicon-ap-size, --pxicon-mode) aren't
observed by MutationObserver/ResizeObserver, so a live-mounted <px-icon>
never rechecked its config. Add a pxicon:refresh document event + exported
refreshAll() helper; demo calls it from its control handlers.
2026-08-08 15:56:58 +02:00
2 changed files with 34 additions and 11 deletions
+2 -1
View File
@@ -170,7 +170,7 @@
</div>
<script type="module">
import '../px-icon.js';
import { refreshAll } from '../px-icon.js';
const apSizeInput = document.getElementById('apSize');
const apSizeVal = document.getElementById('apSizeVal');
@@ -181,6 +181,7 @@ function applyConfig() {
apSizeVal.textContent = ap;
document.documentElement.style.setProperty('--pxicon-ap-size', ap + 'px');
document.documentElement.style.setProperty('--pxicon-mode', modeSelect.value);
refreshAll();
}
apSizeInput.addEventListener('input', applyConfig);
modeSelect.addEventListener('change', applyConfig);
+32 -10
View File
@@ -15,6 +15,14 @@ const DEFAULT_AP_SIZE = 8;
const DEFAULT_MODE = 'average';
const SUPERSAMPLE = 8; // per grid cell, for point-sample mode
// CSS custom properties (--pxicon-ap-size, --pxicon-mode, ...) aren't
// observable the way DOM mutations/resizes are: MutationObserver and
// ResizeObserver don't fire when a cascaded custom property changes.
// Instances listen for this document-level event so config changes (e.g.
// a page-level theme/control update) can trigger a re-check without every
// caller needing to touch each <px-icon>'s internals.
const REFRESH_EVENT = 'pxicon:refresh';
// key -> Promise<HTMLCanvasElement> (the small pre-upscale grid raster)
const rasterCache = new Map();
@@ -131,12 +139,15 @@ class PxIcon extends HTMLElement {
// e.g. a morph swapping in a different icon.
this._observer ??= new MutationObserver(() => this._scheduleRender());
this._observer.observe(this, { childList: true, subtree: true, attributes: true, characterData: true });
this._refreshHandler ??= () => this._scheduleRender();
document.addEventListener(REFRESH_EVENT, this._refreshHandler);
this._scheduleRender();
}
disconnectedCallback() {
this._observer?.disconnect();
this._resizeObserver?.disconnect();
document.removeEventListener(REFRESH_EVENT, this._refreshHandler);
}
_upgrade() {
@@ -181,10 +192,12 @@ class PxIcon extends HTMLElement {
const apSize = parseFloat(cs.getPropertyValue('--pxicon-ap-size')) || DEFAULT_AP_SIZE;
const mode = (cs.getPropertyValue('--pxicon-mode') || DEFAULT_MODE).trim();
const displaySize = Math.max(this.clientWidth, this.clientHeight) || 24;
const snapped = snapToGrid(displaySize, apSize);
const gridW = Math.max(1, Math.round(snapped / apSize));
const gridH = gridW;
const displayW = this.clientWidth || 24;
const displayH = this.clientHeight || 24;
const snappedW = snapToGrid(displayW, apSize);
const snappedH = snapToGrid(displayH, apSize);
const gridW = Math.max(1, Math.round(snappedW / apSize));
const gridH = Math.max(1, Math.round(snappedH / apSize));
const svgMarkup = svgEl.outerHTML;
const key = `${svgMarkup}|${mode}|${gridW}x${gridH}`;
@@ -200,13 +213,14 @@ class PxIcon extends HTMLElement {
const gridCanvas = await getGridRaster(svgMarkup, mode, gridW, gridH);
const dpr = window.devicePixelRatio || 1;
const backingSize = Math.round(snapped * dpr);
this._canvas.width = backingSize;
this._canvas.height = backingSize;
const backingW = Math.round(snappedW * dpr);
const backingH = Math.round(snappedH * dpr);
this._canvas.width = backingW;
this._canvas.height = backingH;
const ctx = this._canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
ctx.clearRect(0, 0, backingSize, backingSize);
ctx.drawImage(gridCanvas, 0, 0, backingSize, backingSize);
ctx.clearRect(0, 0, backingW, backingH);
ctx.drawImage(gridCanvas, 0, 0, backingW, backingH);
if (this._slot.isConnected) {
// first successful raster: swap the placeholder slot for the
@@ -219,4 +233,12 @@ class PxIcon extends HTMLElement {
customElements.define('px-icon', PxIcon);
export { PxIcon, rasterCache };
// Call after changing a --pxicon-* custom property (e.g. on :root, or any
// ancestor of a group of icons) to make every connected <px-icon> re-check
// its computed config and re-render if it actually changed. Cheap to call
// broadly: each instance's own key check skips the work if nothing did.
function refreshAll() {
document.dispatchEvent(new CustomEvent(REFRESH_EVENT));
}
export { PxIcon, rasterCache, refreshAll };