EventManager / createDoubleTapDetector
دفتری برای ثبت شنوندههای رویداد که به چرخهٔ عمر گره خورده و بر AbortController سوار است، بههمراه یک تشخیصدهندهٔ کوچک دو-ضربه برای حرکتهای لمسی که به نوع اشارهگر کاری ندارد.
مسئلهای که حل میکند برداشتن دوبارهٔ شنوندهها است. removeEventListener تنها وقتی کار میکند که دقیقاً همان ارجاع تابع و همان گزینههایی را به آن بدهی که با آن ثبت کرده بودی. اگر سر راه، دستگیره را در یک تابع پیکانی بپیچی، دیگر هرگز نمیتوانی برش داری. آنگاه مؤلفههایی که پیدرپی سوار و پیاده میشوند، هر دور یک شنونده نشت میدهند. AbortController همهٔ اینها را به یک abort() تبدیل میکند.
کاربرد
درون یک وبکامپوننت
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(); // همهٔ شنوندهها را برمیدارد و برای اتصال بعدی از نو آماده میشود
}
}در کد معمولی صفحه
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(); // هنگام برچیدن این بخش صدایش بزن
}API
on
یک شنونده ثبت میکند که به همین مدیر گره خورده است. زنجیرهپذیر است.
پارامترها
| پارامتر | توضیح | نوع | پیشفرض |
|---|---|---|---|
target | هدف رویداد | EventTarget | الزامی |
type | نام رویداد | string | الزامی |
handler | تابع دستگیره | EventListener | الزامی |
options | گزینههای addEventListener منهای signal | Omit<AddEventListenerOptions,'signal'> | - |
بازگشت
| آرگومان | توضیح | نوع |
|---|---|---|
this | خودِ مدیر، برای زنجیره کردن | EventManager |
delegate
واگذاری رویداد: یک شنونده به parent میچسباند و handler را تنها زمانی صدا میزند که رویداد از فرزندی برخاسته باشد که با selector همخوانی دارد. زنجیرهپذیر است.
دستگیره خودِ رویداد اصلی و عنصر همخوان را دریافت میکند.
scope.delegate(list, '.item', 'click', (ev, item) => {
console.log(item.getAttribute('data-id'));
});پارامترها
| پارامتر | توضیح | نوع | پیشفرض |
|---|---|---|---|
parent | عنصری که آن تکشنونده به آن بسته میشود | HTMLElement | الزامی |
selector | گزینشگری که فرزند باید با آن همخوان باشد | string | الزامی |
type | نام رویداد | string | الزامی |
handler | (event, matchedElement) => void | Function | الزامی |
options | گزینههای addEventListener منهای signal | Omit<AddEventListenerOptions,'signal'> | - |
بازگشت
| آرگومان | توضیح | نوع |
|---|---|---|
this | خودِ مدیر، برای زنجیره کردن | EventManager |
abort
همهٔ شنوندههای ثبتشده را برمیدارد و AbortController درونی را از نو میسازد. چند بار صدا زدنش بیخطر است؛ فراخوانیهای بعدی on() و delegate() از دامنهای تمیز آغاز میشوند.
بازگشت
بدون مقدار بازگشتی (void)
signal
همان AbortSignal زیرین، برای وقتی که بخواهی خودت آن را به addEventListener بدهی.
| آرگومان | توضیح | نوع |
|---|---|---|
signal | سیگنال لغو این مدیر | AbortSignal |
createDoubleTapDetector
تشخیص دو-ضربه فقط از روی نمونههای خام (x, y, زمان). به نوع اشارهگر کاری ندارد، پس چه از رویدادهای Pointer تغذیهاش کنی چه Touch چه Mouse، یکسان کار میکند. برای حرکتهای لمسی ساخته شده است: دو ضربه برای جابهجایی، برای بزرگنمایی، برای پسندیدن. در چنین جاهایی اگر منطق آستانهٔ زمان و فاصله را در هر نقطه از نو بنویسی، بهآسانی و بهشکلی نامحسوس اشتباه از آب درمیآید: مقایسهٔ تنها یک محور، یا فراموش کردن بازنشانی پس از یک تشخیص، که آنگاه سه ضربهٔ سریع بهجای یکی، دو دو-ضربهٔ همپوشان شمرده میشود.
import { createDoubleTapDetector } from 'ranuts/utils';
const detector = createDoubleTapDetector();
el.addEventListener('pointerup', (e) => {
if (detector.check(e.clientX, e.clientY)) seek();
});createDoubleTapDetector(options?)
پارامترها (DoubleTapDetectorOptions)
| گزینه | توضیح | نوع | پیشفرض |
|---|---|---|---|
windowMs | بیشترین فاصلهٔ مجاز میان دو ضربه، بر حسب میلیثانیه | number | 300 |
maxDistancePx | بیشترین فاصلهٔ مجاز میان دو ضربه روی صفحه، بر حسب پیکسل | number | 60 |
DoubleTapDetector
| عضو | توضیح | نوع |
|---|---|---|
check | ضربهای در (x, y) را ثبت میکند و میگوید آیا با ضربهٔ بلافاصله پیش از خود یک دو-ضربه میسازد یا نه. با تشخیص یک دو-ضربه، ردگیری از نو آغاز میشود، پس ضربهٔ سریع سوم جفتی تازه را شروع میکند و جزئی از همان دو-ضربه به شمار نمیآید | (x: number, y: number, now?: number) => boolean |
reset | آخرین ضربهٔ ثبتشده را از یاد میبرد؛ وقتی حرکتی جز ضربه (مثلاً کشیدن) آغاز میشود صدایش بزن | () => void |