ترنزیشنهای پیوستهی صفحه با WebGPU و جاوااسکریپت خام
خلاصهٔ کاملتر
نویسنده، بن پین، میگه هدفش اینه که حس «پاپ شدن» وضعیت DOM موقع رفتن از یه صفحه به صفحهی دیگه رو کاملاً حذف کنه. راهش اینه که بهجای اینکه هر صفحه المانهای خودشو بسازه و خراب کنه، یه صحنهی واحد و پیوسته روی WebGPU داشته باشه (همین با WebGL هم کار میکنه) که کاربر رو گول میزنه انگار جابهجایی یکپارچهست.
مدل ذهنیاش دو لایهست که روی هم چیده شدن: یه لایهی DOM که «اسلات» داره (یه خالی که فقط جا نگه میداره، هیچ توش نیست) و یه لایهی کانواس با یه صحنه که همهی پلنهای تصویر توشه. پلنها یه بار موقع شروع ساخته میشن و همیشه زنده میمونن؛ هر پلن با getBoundingClientRect() مستطیل اسلات متناظرشو دنبال میکنه. نویسنده تأکید میکنه که هیچوقت مختصات رو هاردکد نمیکنه: CSS چیدمان میکنه، getBoundingClientRect() میگه اسلات کجا نشسته و پلن با tween به همونجا میره.
قلب ماجرا اینه که پلنها هیچوقت ساخته یا نابود نمیشن. توی هر ترنزیشن، هر پلن دقیقاً یکی از سه کار رو میکنه: نگهداشتن (تصویری که تو هر دو صفحه هست، مستطیلشو از حالت قدیم به جدید tween میکنی)، حذف (تصویری که داره میره، opacityشو صفر میکنی ولی پلن زنده میمونه و بعداً دوباره استفاده میشه) و افزودن (تصویر جدید رو سرجاش میزنی و opacity رو از صفر به یک میبری).
همهی این حرکتها فقط با دو تابع کوچیک روی GSAP ساخته میشن: یکی برای جابهجا کردن مستطیل پلن و یکی برای تغییر شفافیتش:
export function tweenBounds(plane, target, opts = {}) {
return gsap.to(plane.bounds, { x: target.x, y: target.y, w: target.w, h: target.h });
}
export function tweenOpacity(plane, to, opts = {}) {
return gsap.to(plane, { opacity: to });
}هر ترنزیشن یه کلاس کوچیکه با دو متد async: out() که پلنهای صفحهی مبدأ رو مدیریت میکنه (مشترک رو مورف میکنه، بقیه رو محو) و in() که پلنهای مقصد رو ظاهر میکنه. کنترلر هر دو نیمه رو همزمان اجرا میکنه و با هم منتظرشون میمونه، برای همینه که بهجای «قدیمی میره بعد جدید میاد»، مثل یه حرکت پیوسته دیده میشه.
نویسنده میگه مهمترین نکتهی کل کار همینه: عادی وقتی روی یه صفحهای، حلقهی رندر هر فریم مستطیل اسلات رو تو bounds پلن کپی میکنه. ولی موقع ترنزیشن خودت داری همون bounds رو tween میکنی؛ اگه پلن هنوز اسلاتشو دنبال میکرد، حلقهی رندر ۶۰ بار در ثانیه tween تو رو بازنویسی میکرد و مورف نامرئی میشد. برای همین اولین کاری که کنترلر میکنه اینه که trackedEl هر پلن رو null میکنه تا کنترل bounds کامل دست GSAP بیفته و بعد از تموم شدن ترنزیشن دوباره وصلشون میکنه. چون tween دقیقاً روی مستطیل اسلات مقصد تموم شده، درز جابهجایی نامرئیه. یه قفل mutating هم کل عملیات رو اتمیک میکنه تا کلیکهای پشتسرهم دو ترنزیشن رو به جون هم نندازن.
نکات کلیدی:
- بهجای ساخت و خراب کردن المانها، یه استخر ثابت از پلنهای تصویر همیشه زنده میمونه
- هر ترنزیشن فقط سه نقش داره: نگهداشتن (مورف)، حذف (محو) و افزودن (ظاهر شدن)
- مختصات هاردکد نمیشه؛ CSS چیدمان میکنه و پلنها با tween به مستطیل getBoundingClientRect() میرسن
- موقع ترنزیشن trackedEl صفر میشه تا حلقهی رندر tween رو بازنویسی نکنه
- قفل mutating ترنزیشن رو اتمیک نگه میداره تا کلیکهای همزمان تداخل نکنن




