Skip to content

Scratch

سطحی آزمایشی به سبک کارت اسکراچ که درون shadow DOM خود، یک <canvas> پوشاننده در اندازهٔ کامل روی لایهٔ آشکارشونده می‌کشد. کشیدن روی بوم، پوشش را با ترکیب destination-out در امتداد مسیری که نشانگر واقعاً پیموده پاک می‌کند، و با خراشیدن مساحت کافی، آنچه زیر آن است پدیدار می‌شود. میزبان display: block است، پس عرض و ارتفاع صریح به آن بدهید.

کجا به کارش ببرید: وقتی سطحی آزمایشی به سبک کارت اسکراچ می‌خواهید که کشیدن روی آن پوششِ بوم را پاک کند و هر محتوایی را که زیرش گذاشته‌اید آشکار سازد. به لطف Pointer Events API با ماوس و لمس و قلم یکسان کار می‌کند.

⚠️ آزمایشی: این کامپوننت هنوز در حال ساخت است. آن را یک تعامل سرگرم‌کننده بدانید، نه ابزارکی آبدیده برای محیط تولید.

شروع سریع

استفادهٔ پایه

هرچه درون <r-scratch> بگذارید همان محتوای آشکارشونده است (یک مبلغ، یک تصویر، یک <r-icon>، یا چند عنصر) که از راه اسلات پیش‌فرض به لایهٔ زیر پوشش بازتاب می‌یابد — درست مثل بازتاب محتوا در هر کامپوننت دیگر ranui.

۵۰ سکه بردی!
html
<r-scratch style="display: block; width: 240px; height: 120px;">۵۰ سکه بردی!</r-scratch>

مرجع API

خصیصه‌ها

خصیصهنوعپیش‌فرضتوضیح
disabledbooleanfalseتعامل خراشیدن را خاموش می‌کند (pointer-events: none روی بومِ پوشش، به‌علاوهٔ نگهبانی در هندلرها).
sheetstring''CSS تزریق‌شده به shadow DOM کامپوننت.

حالت غیرفعال disabled

۵۰ سکه بردی!
html
<r-scratch disabled style="display: block; width: 240px; height: 120px;">۵۰ سکه بردی!</r-scratch>

استایل بیرونی sheet

🎁
html
<r-scratch
  sheet=".ran-scratch-ticket-award { align-items: center; justify-content: center; display: flex; }"
  style="display: block; width: 240px; height: 120px;"
>
  🎁
</r-scratch>

تعامل

این کامپوننت هیچ رویداد سفارشی‌ای ارسال نمی‌کند: چیزی نیست که بشود شنونده‌ای به آن بست. خراشیدن یکسره با شنونده‌های درونی Pointer Events که روی بوم ثبت شده‌اند پیش می‌رود، پس ماوس و لمس و قلم همگی یک مسیر کد را می‌پیمایند:

  • pointerdown: خراشیدن را مسلح می‌کند و همان‌جا که نشانگر فرود آمده لکهٔ کوچکی را پاک می‌کند (پس حتی یک ضربهٔ بدون کشیدن هم چیزی را آشکار می‌کند).
  • pointermove: تا وقتی مسلح است، از نقطهٔ پیشین تا نقطهٔ کنونی یک خط پیوسته (نه لکه‌های جدا) با globalCompositeOperation = 'destination-out' می‌کشد، پس کشیدن سریع رد پیوسته‌ای را آشکار می‌کند نه نقطه‌چین، و در همان حال مساحت خراشیده‌شده را روی هم می‌ریزد.
  • pointerup / pointercancel: خراشیدن را خلع‌سلاح می‌کند؛ به‌محض اینکه مساحت انباشته از ۳۵٪ مساحت پیکسلی بوم بگذرد، کل پوشش با clearRect پاک می‌شود و لایهٔ زیرین به‌تمامی آشکار می‌گردد (آستانه‌ای عمداً دست‌ودل‌باز به معنای «کمی بخراش، بقیه‌اش خودش تمام می‌شود»؛ همان تجربهٔ رایج کارت اسکراچ، به‌جای واداشتن کاربر به پاک‌کردن دستی کل پوشش).

مختصات نشانگر از راه وضوح واقعی بافر ترسیم بوم (پایین‌تر) نگاشت می‌شود، پس خراشیدن بی‌توجه به اندازهٔ CSS عنصر و نسبت پیکسل دستگاه، درست زیر انگشت یا مکان‌نما را دنبال می‌کند. تا وقتی disabled باشد هیچ هندلری کاری نمی‌کند، و touch-action: none روی بوم مانع می‌شود کشیدن با انگشت صفحه را هم بپیمایاند.

چند حالت مرزیِ مربوط به دستگاه‌ها به‌جای واگذاشتن به آنچه «یکسان‌سازی» ماوس/لمس/قلم به‌طور پیش‌فرض می‌کند، صریح مدیریت شده‌اند:

  • ماوس: تنها دکمهٔ اصلی (چپ) خراشیدن را آغاز می‌کند؛ کشیدن با کلیک راست یا کلیک وسط نه.
  • چندلمسی: نخستین انگشتی که پایین می‌آید خط را هدایت می‌کند؛ انگشت دومی که وسط خراشیدن تماس بگیرد تا بلندشدن انگشت اول نادیده گرفته می‌شود، به‌جای اینکه هر دو هم‌زمان روی یک وضعیت ترسیم بنویسند.
  • حرکت‌های نیمه‌کاره: اگر سیستم‌عامل وسط کشیدن، تصاحب نشانگر را پس بگیرد و هرگز pointerup نفرستد (در برخی WebViewهای اندروید هنگام قطع‌شدن خراشیدن با اشارهٔ بازگشت سیستم دیده می‌شود)، شنوندهٔ lostpointercapture باز هم وضعیت درونی را بازنشانی می‌کند؛ وگرنه وضعیت درونی مسلح می‌ماند و حرکت بعدیِ کاملاً بی‌ربط نشانگر بی‌صدا به ترسیم ادامه می‌دهد.

وضوح بوم

وضوح درونی بوم به‌جای ماندن روی ۳۰۰×۱۵۰ پیش‌فرض مرورگر، با اندازهٔ CSSِ واقعاً رندرشده × devicePixelRatio همگام می‌شود (هنگام اتصال و دوباره در هر resize پنجره). این کار پوشش را روی نمایشگرهای HiDPI شفاف نگه می‌دارد و نگاشت مختصات نشانگر به بوم را در هر اندازه‌ای دقیق می‌کند؛ تغییر اندازه، خراشیدنِ در جریان را بازنشانی می‌کند (بافر با تغییر ابعاد ناگزیر پاک می‌شود).

اسلات‌ها

اسلاتتوضیح
(پیش‌فرض)محتوای آشکارشونده، که به لایهٔ زیر پوشش بازتاب می‌یابد.

استایل

این کامپوننت هیچ دستگیرهٔ ()::part ندارد، اما رنگ دو لایه‌اش متغیرهای CSS‌ای‌اند که توکن‌های پوسته آن‌ها را می‌رانند. shadow DOM آن سه لایهٔ ثابت است:

کلاسنقش
.ran-scratch-ticketنگه‌دارندهٔ نسبی در اندازهٔ کامل (width: 100%; height: 100%)
.ran-scratch-ticket-awardلایهٔ آشکارشونده، z-index: 1، background: var(--ran-scratch-award-background, var(--ran-color-bg-elevated, #fff))؛ اسلات پیش‌فرض را نگه می‌دارد
.ran-scratch-ticket-canvasبوم پوشاننده، z-index: 2؛ با --ran-scratch-cover-background پر می‌شود (پیش‌فرض var(--ran-color-text-secondary, #6b6b6b)) که روی میزبان تعیین می‌شود

هر دو رنگ از توکن‌های پوسته با یک مقدار پشتیبانِ عینی می‌گذرند، پس به‌طور پیش‌فرض با حالت روشن و تیره جور می‌شوند و می‌توان با --ran-scratch-award-background / --ran-scratch-cover-background بازنویسی‌شان کرد. اندازهٔ میزبان را با width / height معمولی تعیین کنید.

بهترین شیوه‌ها

  • همیشه به میزبان اندازه بدهید: display: block است و اندازهٔ ذاتی ندارد؛ width و height صریح بدهید وگرنه لایه‌های درونیِ 100% تا صفر جمع می‌شوند.
  • هر محتوای آشکارشونده‌ای کار می‌کند: متن، تصویر، <r-icon>، چند عنصر. هرچه جایزه واقعاً هست در اسلات بگذارید؛ هیچ API ثابت «آیکون + اندازه»ای نیست که ناچار شوید دورش بزنید.
  • با ماوس و لمس و قلم کار می‌کند: Pointer Events هر سه را یکسان می‌کند، پس روی رومیزی و موبایل یکسان پاسخ می‌دهد.
  • آزمایشی بدانیدش: هنوز در حال ساخت است؛ برای رفتار محیط تولید به آن تکیه نکنید.

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