EventManager / createDoubleTapDetector
Um registro de eventos preso ao ciclo de vida e apoiado no AbortController, mais um pequeno detector de toque duplo, indiferente ao tipo de ponteiro, para gestos de toque.
O problema que ele resolve é tirar os ouvintes de volta. O removeEventListener só funciona se você entregar a ele exatamente a mesma referência de função e as mesmas opções com que registrou. Embrulhe o manipulador numa arrow function na hora de passar e nunca mais conseguirá removê-lo. Componentes que montam e desmontam repetidas vezes passam então a vazar um ouvinte por ciclo. O AbortController transforma tudo isso num único abort().
Uso
Dentro de um Web Component
import { EventManager } from 'ranuts/utils';
class MyElement extends HTMLElement {
private _events = new EventManager();
connectedCallback() {
this._events.on(this._input, 'input', this.handleInput).on(this, 'click', this.handleClick, { capture: true });
}
disconnectedCallback() {
this._events.abort(); // tira todos os ouvintes e deixa tudo limpo para a próxima conexão
}
}No código comum de uma página
function initSection(container: HTMLElement) {
const scope = new EventManager();
scope.on(input, 'input', handleSearch).delegate(container, '[data-action]', 'click', (ev, target) => {
handleAction(target.getAttribute('data-action'));
});
return () => scope.abort(); // chame quando a seção for desmontada
}API
on
Registra um ouvinte preso a este gerenciador. Dá para encadear.
Parâmetros
| Parâmetro | Descrição | Tipo | Padrão |
|---|---|---|---|
target | O alvo do evento | EventTarget | Obrigatório |
type | Nome do evento | string | Obrigatório |
handler | A função manipuladora | EventListener | Obrigatório |
options | As opções do addEventListener menos signal | Omit<AddEventListenerOptions,'signal'> | - |
Retorna
| Argumento | Descrição | Tipo |
|---|---|---|
this | O próprio gerenciador, para encadear | EventManager |
delegate
Delegação de eventos: põe um só ouvinte no parent e chama o handler apenas quando o evento nasceu de um descendente que casa com selector. Dá para encadear.
O manipulador recebe o evento original e o elemento que casou.
scope.delegate(list, '.item', 'click', (ev, item) => {
console.log(item.getAttribute('data-id'));
});Parâmetros
| Parâmetro | Descrição | Tipo | Padrão |
|---|---|---|---|
parent | O elemento ao qual esse único ouvinte é preso | HTMLElement | Obrigatório |
selector | O seletor com que o descendente precisa casar | string | Obrigatório |
type | Nome do evento | string | Obrigatório |
handler | (event, matchedElement) => void | Function | Obrigatório |
options | As opções do addEventListener menos signal | Omit<AddEventListenerOptions,'signal'> | - |
Retorna
| Argumento | Descrição | Tipo |
|---|---|---|
this | O próprio gerenciador, para encadear | EventManager |
abort
Tira todos os ouvintes registrados e renova o AbortController interno. Dá para chamar mais de uma vez sem problema; as chamadas seguintes a on() ou delegate() partem de um escopo limpo.
Retorna
Sem valor de retorno (void)
signal
O AbortSignal de baixo, caso você queira passá-lo você mesmo ao addEventListener.
| Argumento | Descrição | Tipo |
|---|---|---|
signal | O sinal de cancelamento do gerenciador | AbortSignal |
createDoubleTapDetector
Detecção de toque duplo a partir de amostras cruas de (x, y, tempo). É indiferente ao tipo de ponteiro, então funciona igual sendo alimentada por eventos Pointer, Touch ou Mouse. Foi feita para gestos de toque (toque duplo para avançar, para dar zoom, para curtir), onde refazer em cada lugar a lógica do limiar de tempo e distância é fácil de errar de um jeito sutil: comparar só um eixo, ou esquecer de reiniciar depois de um acerto e fazer três toques rápidos contarem como dois toques duplos sobrepostos.
import { createDoubleTapDetector } from 'ranuts/utils';
const detector = createDoubleTapDetector();
el.addEventListener('pointerup', (e) => {
if (detector.check(e.clientX, e.clientY)) seek();
});createDoubleTapDetector(options?)
Parâmetros (DoubleTapDetectorOptions)
| Opção | Descrição | Tipo | Padrão |
|---|---|---|---|
windowMs | Intervalo máximo entre os dois toques, em ms | number | 300 |
maxDistancePx | Distância máxima no plano entre os dois toques, em px | number | 60 |
DoubleTapDetector
| Membro | Descrição | Tipo |
|---|---|---|
check | Anota um toque em (x, y) e diz se ele forma um toque duplo com o imediatamente anterior. Ao detectar um, o rastreio é reiniciado, de modo que um terceiro toque rápido começa um par novo em vez de contar como parte do mesmo toque duplo | (x: number, y: number, now?: number) => boolean |
reset | Esquece o último toque anotado; chame quando começar um gesto que não seja um toque (um arrasto) | () => void |