Skip to content

EventManager / createDoubleTapDetector

دفتری برای ثبت شنونده‌های رویداد که به چرخهٔ عمر گره خورده و بر AbortController سوار است، به‌همراه یک تشخیص‌دهندهٔ کوچک دو-ضربه برای حرکت‌های لمسی که به نوع اشاره‌گر کاری ندارد.

مسئله‌ای که حل می‌کند برداشتن دوبارهٔ شنونده‌ها است. removeEventListener تنها وقتی کار می‌کند که دقیقاً همان ارجاع تابع و همان گزینه‌هایی را به آن بدهی که با آن ثبت کرده بودی. اگر سر راه، دستگیره را در یک تابع پیکانی بپیچی، دیگر هرگز نمی‌توانی برش داری. آن‌گاه مؤلفه‌هایی که پی‌درپی سوار و پیاده می‌شوند، هر دور یک شنونده نشت می‌دهند. AbortController همهٔ این‌ها را به یک abort() تبدیل می‌کند.

کاربرد

درون یک وب‌کامپوننت

ts
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(); // همهٔ شنونده‌ها را برمی‌دارد و برای اتصال بعدی از نو آماده می‌شود
  }
}

در کد معمولی صفحه

ts
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 منهای signalOmit<AddEventListenerOptions,'signal'>-

بازگشت

آرگومانتوضیحنوع
thisخودِ مدیر، برای زنجیره کردنEventManager

delegate

واگذاری رویداد: یک شنونده به parent می‌چسباند و handler را تنها زمانی صدا می‌زند که رویداد از فرزندی برخاسته باشد که با selector هم‌خوانی دارد. زنجیره‌پذیر است.

دستگیره خودِ رویداد اصلی و عنصر هم‌خوان را دریافت می‌کند.

ts
scope.delegate(list, '.item', 'click', (ev, item) => {
  console.log(item.getAttribute('data-id'));
});

پارامترها

پارامترتوضیحنوعپیش‌فرض
parentعنصری که آن تک‌شنونده به آن بسته می‌شودHTMLElementالزامی
selectorگزینشگری که فرزند باید با آن هم‌خوان باشدstringالزامی
typeنام رویدادstringالزامی
handler(event, matchedElement) => voidFunctionالزامی
optionsگزینه‌های addEventListener منهای signalOmit<AddEventListenerOptions,'signal'>-

بازگشت

آرگومانتوضیحنوع
thisخودِ مدیر، برای زنجیره کردنEventManager

abort

همهٔ شنونده‌های ثبت‌شده را برمی‌دارد و AbortController درونی را از نو می‌سازد. چند بار صدا زدنش بی‌خطر است؛ فراخوانی‌های بعدی on() و delegate() از دامنه‌ای تمیز آغاز می‌شوند.

بازگشت

بدون مقدار بازگشتی (void)

signal

همان AbortSignal زیرین، برای وقتی که بخواهی خودت آن را به addEventListener بدهی.

آرگومانتوضیحنوع
signalسیگنال لغو این مدیرAbortSignal

createDoubleTapDetector

تشخیص دو-ضربه فقط از روی نمونه‌های خام (x, y, زمان). به نوع اشاره‌گر کاری ندارد، پس چه از رویدادهای Pointer تغذیه‌اش کنی چه Touch چه Mouse، یکسان کار می‌کند. برای حرکت‌های لمسی ساخته شده است: دو ضربه برای جابه‌جایی، برای بزرگ‌نمایی، برای پسندیدن. در چنین جاهایی اگر منطق آستانهٔ زمان و فاصله را در هر نقطه از نو بنویسی، به‌آسانی و به‌شکلی نامحسوس اشتباه از آب درمی‌آید: مقایسهٔ تنها یک محور، یا فراموش کردن بازنشانی پس از یک تشخیص، که آن‌گاه سه ضربهٔ سریع به‌جای یکی، دو دو-ضربهٔ هم‌پوشان شمرده می‌شود.

ts
import { createDoubleTapDetector } from 'ranuts/utils';

const detector = createDoubleTapDetector();
el.addEventListener('pointerup', (e) => {
  if (detector.check(e.clientX, e.clientY)) seek();
});

createDoubleTapDetector(options?)

پارامترها (DoubleTapDetectorOptions)

گزینهتوضیحنوعپیش‌فرض
windowMsبیشترین فاصلهٔ مجاز میان دو ضربه، بر حسب میلی‌ثانیهnumber300
maxDistancePxبیشترین فاصلهٔ مجاز میان دو ضربه روی صفحه، بر حسب پیکسلnumber60

DoubleTapDetector

عضوتوضیحنوع
checkضربه‌ای در (x, y) را ثبت می‌کند و می‌گوید آیا با ضربهٔ بلافاصله پیش از خود یک دو-ضربه می‌سازد یا نه. با تشخیص یک دو-ضربه، ردگیری از نو آغاز می‌شود، پس ضربهٔ سریع سوم جفتی تازه را شروع می‌کند و جزئی از همان دو-ضربه به شمار نمی‌آید(x: number, y: number, now?: number) => boolean
resetآخرین ضربهٔ ثبت‌شده را از یاد می‌برد؛ وقتی حرکتی جز ضربه (مثلاً کشیدن) آغاز می‌شود صدایش بزن() => void

منتشرشده تحت مجوز MIT.