/** * — before/after image comparison slider. * * The slider is always on. Left-click / drag positions the divider. Right-click * snaps the divider to whichever extreme it isn't currently nearer to (left * or right edge), so a single right-click flips between the before-only and * after-only views. */ import { morph } from 'lib/morph'; import { canvasToBlobUrl } from 'lib/canvas'; class CompareSlider extends HTMLElement { #dragging = false; #beforeSrc = ''; #afterSrc = ''; #positionFrac = 0.5; #downloadSrc = ''; #downloadName = ''; #beforeCanvas = null; #afterCanvas = null; #lazyBlobURL = ''; #onWindowMouseMove = (e) => { if (this.#dragging) this.#setPosition(this.#getFrac(e)); }; #onWindowTouchMove = (e) => { if (this.#dragging) this.#setPosition(this.#getFrac(e)); }; #onWindowMouseUp = () => { this.#dragging = false; this.classList.remove('dragging'); }; #onWindowTouchEnd = () => { this.#dragging = false; this.classList.remove('dragging'); }; #knobScheduled = false; #knobFullWidth = 0; #resizeObserver = null; #scheduleKnobUpdate = () => { if (this.#knobScheduled) return; this.#knobScheduled = true; requestAnimationFrame(() => { this.#knobScheduled = false; this.#updateKnobPosition(); }); }; #onResize = () => { // Knob's measured full width may change with font-metric / DPR shifts. this.#knobFullWidth = 0; this.#scheduleKnobUpdate(); }; connectedCallback() { this.classList.add('compare'); // Belt-and-suspenders: also set display:none inline so the slider is // definitely hidden before show() has been called. The class CSS rule // does this too, but it lives inside our own
${afterTag}
${beforeTag}
`); if (cm) this.#drawCanvasSources(); } } customElements.define('compare-slider', CompareSlider);