Changes
1 changed files (+115/-0)
-
-
@@ -11,6 +11,43 @@const SCALE_MIN = 0.1; const SCALE_MAX = 5.0; const TOUCH_IDLE = 0; const TOUCH_PANNING = 1; const TOUCH_SCALING = 2; function idle() { return { state: TOUCH_IDLE, touch: null, x: 0, y: 0, distance: 0, scale: 0.0, }; } function panning(touch, x, y) { return { state: TOUCH_PANNING, touch, x, y, distance: 0, scale: 0.0, }; } function scaling(distance, scale) { return { state: TOUCH_SCALING, touch: null, x: 0, y: 0, distance, scale, }; } export class XPreview extends HTMLElement { #slot = document.createElement("slot"); #gesturePlane = document.createElement("div");
-
@@ -27,6 +64,8 @@ #dx = 0;#dy = 0; #scale = 1.0; #touch = idle(); #isRenderScheduled = false; constructor() {
-
@@ -61,6 +100,10 @@ }connectedCallback() { this.#gesturePlane.addEventListener("wheel", this.#onWheel); this.#gesturePlane.addEventListener("touchstart", this.#onTouchChange); this.#gesturePlane.addEventListener("touchend", this.#onTouchChange); this.#gesturePlane.addEventListener("touchmove", this.#onTouchMove); this.#gesturePlane.addEventListener("touchcancel", this.#onTouchCancel); this.#render();
-
@@ -69,9 +112,61 @@ }disconnectedCallback() { this.#gesturePlane.removeEventListener("wheel", this.#onWheel); this.#gesturePlane.removeEventListener("touchstart", this.#onTouchChange); this.#gesturePlane.removeEventListener("touchend", this.#onTouchChange); this.#gesturePlane.removeEventListener("touchmove", this.#onTouchMove); this.#gesturePlane.removeEventListener("touchcancel", this.#onTouchCancel); this.#resizeObserver.disconnect(); } #onTouchChange = (event) => { switch (event.touches.length) { case 0: return; case 1: this.#touch = panning(event.touches.item(0), this.#dx, this.#dy); return; default: const distance = getPinchSize(event.touches); if (distance !== null) { this.#touch = scaling(distance, this.#scale); } return; } }; #onTouchCancel = (event) => { this.#touch = idle(); }; #onTouchMove = (event) => { switch (this.#touch.state) { case TOUCH_IDLE: return; case TOUCH_PANNING: { this.#dx = this.#touch.x + (event.touches[0].clientX - this.#touch.touch.clientX) / this.#scale; this.#dy = this.#touch.y + (event.touches[0].clientY - this.#touch.touch.clientY) / this.#scale; this.#render(); return; } case TOUCH_SCALING: { const distance = getPinchSize(event.touches); if (distance === null) { return; } this.#scale = Math.max( SCALE_MIN, Math.min(SCALE_MAX, this.#touch.scale * (distance / this.#touch.distance)), ); this.#render(); return; } } }; #onWheel = (event) => { event.preventDefault();
-
@@ -125,3 +220,23 @@ this.#isRenderScheduled = false;}); } } function getPinchSize(touches) { const firstTouch = touches.item(0); if (!firstTouch) { return null; } const px = firstTouch.clientX; const py = firstTouch.clientY; let tx = 0; let ty = 0; for (let i = 1, touch; (touch = touches.item(i)); i++) { tx += touch.clientX; ty += touch.clientY; } return Math.sqrt(Math.pow(px - tx / touches.length, 2) + Math.pow(py - ty / touches.length, 2)); }
-