Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | 12x 2x 2x 1x 7x 7x 7x 2x 2x 2x 1x 1x 1x 5x 5x 3x 3x 2x 2x 3x 3x 3x 7x 1x 2x 2x 2x | import { iterateDOMList } from '../utils.js';
export class Fingers {
/**
* Store touches that are occurring
* resets when count == 0
*
* Used to detect pinches etc
*/
touches = $state<Array<{ pointerId?: number; clientX: number; clientY: number }>>([]);
get count() {
return this.touches.length;
}
/**
* Drop the event from {@link Fingers.touch} by matching the pointerId
*/
#removeTouch(event: Event & { pointerId: number }) {
const i = this.touches.findIndex((touch) => touch.pointerId === event.pointerId);
if (i === -1) return;
this.touches.splice(i, 1);
}
#supportsTouchEvents() {
return 'ontouchstart' in this.inside;
}
get #handle() {
return (event: Event) => {
if (this.#supportsTouchEvents()) {
Iif (event instanceof TouchEvent) {
this.touches = [...iterateDOMList(event.touches)];
}
// Mouse support too, cuz touch{start,end} only works
// for touchscreens obviously
Eif (event instanceof MouseEvent) {
switch (event.type as `mouse${'up' | 'down'}`) {
case 'mousedown': {
this.touches = [event];
break;
}
case 'mouseup': {
this.touches = [];
}
}
}
} else {
// Do dumb manual tracking too cuz TouchEvent is not supported
// on Safari yet
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/touchstart_event#browser_compatibility
Eif (event instanceof PointerEvent) {
switch (event.type as `pointer${'up' | 'down'}`) {
case 'pointerdown': {
this.touches.push(event);
break;
}
case 'pointerup': {
this.#removeTouch(event);
break;
}
}
}
}
};
}
constructor(public inside: HTMLElement = document.body) {
$effect(() => {
const handler = this.#handle;
if (this.#supportsTouchEvents()) {
// Touchscreens
inside.addEventListener('touchstart', handler);
inside.addEventListener('touchend', handler);
// Mouses
inside.addEventListener('mousedown', handler);
inside.addEventListener('mouseup', handler);
return () => {
inside.removeEventListener('touchstart', handler);
inside.removeEventListener('touchend', handler);
inside.removeEventListener('mousedown', handler);
inside.removeEventListener('mouseup', handler);
};
} else {
// Drawing tablets
inside.addEventListener('pointerdown', handler);
inside.addEventListener('pointerup', handler);
return () => {
inside.removeEventListener('pointerdown', handler);
inside.removeEventListener('pointerup', handler);
};
}
});
// Pretty much sure that something
// has gone wrong in the finger count tracking
$effect(() => {
if (this.count < 0 || this.count > 4) {
console.warn(
`[touch::Fingers] Something has gone wrong with the finger count tracking! reset to 0 from ${this.count}`
);
this.touches = [];
}
});
}
/**
* Useful if a handler further down from {@link inside}
* will stop propagation of a pointer event.
* In that case, {@link Fingers}' own handlers wont be reached
* so we have to manually react
*/
register(e: Event) {
this.#handle(e);
}
reset() {
this.touches = [];
}
get any() {
return this.count > 0;
}
get none() {
return this.count <= 0;
}
get single() {
return this.count === 1;
}
get multiple() {
return this.count > 1;
}
}
|