متحرک سازی یک تاری

محو کردن یک راه عالی برای تغییر جهت تمرکز کاربر است. تار جلوه دادن برخی از عناصر بصری در حالی که سایر عناصر در فوکوس قرار دارند، به طور طبیعی تمرکز کاربر را هدایت می کند. کاربران محتوای تار را نادیده می گیرند و در عوض روی محتوایی که می توانند بخوانند تمرکز می کنند. یک مثال می تواند لیستی از نمادها باشد که جزئیات مربوط به موارد منفرد را با نگه داشتن ماوس روی آن نشان می دهد. در طی آن زمان، گزینه‌های باقی‌مانده می‌توانند برای هدایت کاربر به اطلاعات جدید نمایش داده شده محو شوند.

TL; DR

متحرک سازی یک تاری واقعاً یک گزینه نیست زیرا بسیار کند است. در عوض، یک سری از نسخه‌های تار شده را از قبل محاسبه کنید و بین آنها محو شوند. همکار من یی گو یک کتابخانه نوشت تا همه چیز را برای شما انجام دهد! به نسخه ی نمایشی ما نگاهی بیندازید.

با این حال، این تکنیک زمانی که بدون هیچ دوره انتقالی به کار می‌رود، می‌تواند بسیار آزاردهنده باشد. متحرک سازی یک تاری - انتقال از حالت محو نشده به محو - یک انتخاب معقول به نظر می رسد، اما اگر تا به حال سعی کرده اید این کار را در وب انجام دهید، احتمالا متوجه شده اید که انیمیشن ها چیزی جز نرم هستند، همانطور که این نسخه نمایشی نشان می دهد که اگر ندارید. یک ماشین قدرتمند آیا می توانیم بهتر عمل کنیم؟

مشکل

نشانه گذاری توسط CPU به بافت تبدیل می شود. بافت ها در GPU آپلود می شوند. GPU این بافت ها را با استفاده از سایه بان ها به فریم بافر می کشد. تاری در سایه زن اتفاق می افتد.

در حال حاضر، ما نمی‌توانیم انیمیشن تاری را به طور موثر انجام دهیم. با این حال، می‌توانیم کاری پیدا کنیم که به اندازه کافی خوب به نظر برسد، اما از نظر فنی، یک تاری انیمیشن نیست. برای شروع، ابتدا بیایید بفهمیم که چرا تاری متحرک کند است. برای محو کردن عناصر در وب، دو روش وجود دارد: ویژگی filter CSS و فیلترهای SVG. به لطف افزایش پشتیبانی و سهولت استفاده، معمولاً از فیلترهای CSS استفاده می شود. متأسفانه، اگر نیاز به پشتیبانی از اینترنت اکسپلورر دارید، چاره‌ای جز استفاده از فیلترهای SVG ندارید، زیرا IE 10 و 11 از فیلترهای CSS پشتیبانی می‌کنند اما نه. خبر خوب این است که راه حل ما برای متحرک سازی تاری با هر دو تکنیک کار می کند. پس بیایید با نگاهی به DevTools، گلوگاه را پیدا کنیم.

اگر «Paint Flashing» را در DevTools فعال کنید، اصلاً فلاش نمی‌بینید. به نظر می رسد هیچ رنگ آمیزی مجدد انجام نمی شود. و این از نظر فنی درست است زیرا "رنگ‌آمیزی مجدد" به این اشاره دارد که CPU مجبور است بافت یک عنصر ارتقا یافته را دوباره رنگ کند. هر زمان که یک عنصر هم ارتقا یابد و هم محو شود، تاری توسط GPU با استفاده از یک سایه زن اعمال می شود.

هم فیلترهای SVG و هم فیلترهای CSS از فیلترهای کانولوشن برای اعمال تاری استفاده می کنند. فیلترهای پیچشی نسبتاً گران هستند زیرا برای هر پیکسل خروجی باید تعدادی پیکسل ورودی در نظر گرفته شود. هرچه تصویر بزرگتر یا شعاع تاری بزرگتر باشد، اثر هزینه بیشتری دارد.

و مشکل اینجاست، ما در هر فریم یک عملیات گرافیکی نسبتاً گران قیمت را اجرا می‌کنیم، بودجه فریم خود را 16 میلی‌ثانیه می‌رسانیم و بنابراین به زیر 60 فریم در ثانیه می‌رسیم.

پایین سوراخ خرگوش

بنابراین چه کاری می‌توانیم انجام دهیم تا این کار به خوبی اجرا شود؟ ما می توانیم از حیله گری استفاده کنیم! به جای متحرک کردن مقدار تاری واقعی (شعاع تاری)، چند کپی تار را از قبل محاسبه می‌کنیم که در آن مقدار تاری به صورت تصاعدی افزایش می‌یابد، سپس با استفاده از opacity بین آن‌ها محو می‌شود.

متقاطع محو شدن یک سری از همپوشانی تیرگی محو شدن و محو شدن است. برای مثال اگر چهار مرحله تاری داشته باشیم، مرحله اول را محو می کنیم در حالی که در مرحله دوم هم زمان محو می شویم. هنگامی که مرحله دوم به 100% کدورت رسید و مرحله اول به 0% رسید، مرحله دوم را محو می کنیم در حالی که در مرحله سوم محو می شویم. پس از انجام این کار، در نهایت مرحله سوم را محو می کنیم و در نسخه چهارم و نهایی محو می شویم. در این سناریو، هر مرحله ¼ از کل مدت زمان مورد نظر را می گیرد. از نظر بصری، این بسیار شبیه به یک تاری واقعی و متحرک است.

در آزمایشات ما، افزایش شعاع تاری به صورت تصاعدی در هر مرحله بهترین نتایج بصری را به همراه داشت. مثال: اگر چهار مرحله blur داشته باشیم، filter: blur(2^n) برای هر مرحله اعمال می کنیم، یعنی مرحله 0: 1px، مرحله 1: 2px، مرحله 2: 4px و مرحله 3: 8px. اگر هر یک از این کپی‌های محو شده را با استفاده از will-change: transform روی لایه خودشان (به نام «ترویج») روی لایه‌ی خودشان فشار دهیم، تغییر شفافیت در این عناصر باید بسیار سریع باشد. در تئوری، این به ما امکان می‌دهد تا کار گران‌قیمت محو کردن را از جلو بارگذاری کنیم. معلوم شد، منطق ناقص است. اگر این نسخه نمایشی را اجرا کنید، خواهید دید که نرخ فریم همچنان زیر 60 فریم در ثانیه است و تار شدن در واقع بدتر از قبل است.

ابزارهای توسعه‌دهنده ردی را نشان می‌دهند که در آن GPU دوره‌های زمانی طولانی مشغول است.

نگاهی گذرا به DevTools نشان می‌دهد که GPU هنوز به شدت شلوغ است و هر فریم را تا 90 میلی‌ثانیه افزایش می‌دهد. اما چرا؟ ما دیگر مقدار blur را تغییر نمی‌دهیم، فقط opacity را تغییر می‌دهیم، پس چه اتفاقی می‌افتد؟ مشکل بار دیگر در ماهیت افکت تاری نهفته است: همانطور که قبلا توضیح داده شد، اگر عنصر هم ارتقا یابد و هم محو شود، افکت توسط GPU اعمال می شود. بنابراین، اگرچه ما دیگر مقدار تاری را متحرک نمی‌کنیم، خود بافت هنوز محو نشده است و باید هر فریم توسط GPU دوباره محو شود. دلیل اینکه نرخ فریم حتی بدتر از قبل است از این واقعیت ناشی می شود که در مقایسه با پیاده سازی ساده، GPU در واقع کار بیشتری نسبت به قبل دارد، زیرا در بیشتر مواقع دو بافت قابل مشاهده هستند که باید به طور مستقل محو شوند.

چیزی که ما به آن رسیدیم زیبا نیست، اما انیمیشن را به طرز شگفت انگیزی سریع می کند. ما به عدم تبلیغ عنصر محو شدن برمی‌گردیم، بلکه در عوض یک لفاف اصلی را تبلیغ می‌کنیم. اگر یک عنصر هم محو و هم تبلیغ شود، افکت توسط GPU اعمال می شود. این چیزی است که دموی ما را کند کرده است. اگر عنصر تار باشد اما ارتقاء نیابد، به جای آن تاری به نزدیکترین بافت والد شطرنجی می شود. در مورد ما این عنصر بسته بندی والد ارتقا یافته است. تصویر تار اکنون بافت عنصر والد است و می‌تواند برای همه فریم‌های آینده دوباره استفاده شود. این فقط به این دلیل کار می کند که می دانیم عناصر تار متحرک نیستند و ذخیره آنها در حافظه پنهان واقعاً مفید است. در اینجا نسخه ی نمایشی است که این تکنیک را پیاده سازی می کند. من نمی دانم که Moto G4 در مورد این رویکرد چه فکر می کند؟ هشدار اسپویلر: فکر می کند عالی است:

DevTools ردی را نشان می دهد که در آن GPU زمان بیکاری زیادی دارد.

اکنون فضای زیادی برای سر در پردازنده گرافیکی و 60 فریم بر ثانیه ابریشمی و صاف داریم. ما آن را انجام دادیم!

تولید کردن

در نسخه ی نمایشی خود، یک ساختار DOM را چندین بار کپی کردیم تا کپی هایی از محتوا در نقاط قوت مختلف محو شود. ممکن است تعجب کنید که چگونه این کار در یک محیط تولید کار می کند زیرا ممکن است برخی از عوارض جانبی ناخواسته با سبک های CSS نویسنده یا حتی جاوا اسکریپت آنها داشته باشد. حق با شماست. Shadow DOM را وارد کنید!

در حالی که اکثر مردم در مورد Shadow DOM به عنوان راهی برای الصاق عناصر "داخلی" به عناصر سفارشی خود فکر می کنند، این نیز یک انزوا و عملکرد اولیه است! جاوا اسکریپت و CSS نمی توانند مرزهای Shadow DOM را سوراخ کنند که به ما امکان می دهد بدون تداخل با سبک های توسعه دهنده یا منطق برنامه، محتوا را کپی کنیم. ما قبلاً یک عنصر <div> برای هر کپی داریم که می‌توان آن را به صورت شطرنجی درآورد و اکنون از این <div> ‌ها به عنوان میزبان سایه استفاده می‌کنیم. ما با استفاده از attachShadow({mode: 'closed'}) یک ShadowRoot ایجاد می کنیم و یک کپی از محتوا را به جای خود <div> به ShadowRoot متصل می کنیم. ما باید مطمئن شویم که تمام شیوه نامه ها را نیز در ShadowRoot کپی کنیم تا تضمین کنیم که کپی های ما به همان شیوه اصلی استایل بندی شده اند.

برخی از مرورگرها Shadow DOM v1 را پشتیبانی نمی‌کنند، و برای آن‌ها، ما فقط به تکثیر محتوا و امید به بهترین چیزی که چیزی خراب نمی‌شود، بازمی‌گردیم. ما می‌توانیم از Shadow DOM polyfill با ShadyCSS استفاده کنیم، اما این را در کتابخانه خود پیاده‌سازی نکردیم.

و شما بروید. پس از سفر به خط لوله رندر کروم، متوجه شدیم که چگونه می‌توانیم تاری‌ها را به طور موثر در مرورگرها متحرک کنیم!

نتیجه

این نوع افکت نباید به راحتی مورد استفاده قرار گیرد. با توجه به این واقعیت که ما عناصر DOM را کپی می کنیم و آنها را به لایه خود فشار می دهیم، می توانیم محدودیت های دستگاه های پایین تر را افزایش دهیم. کپی کردن تمام شیوه نامه ها در هر ShadowRoot نیز یک خطر بالقوه عملکرد است، بنابراین باید تصمیم بگیرید که آیا ترجیح می دهید منطق و سبک های خود را طوری تنظیم کنید که تحت تأثیر کپی ها در LightDOM قرار نگیرید یا از تکنیک ShadowDOM ما استفاده کنید. اما گاهی اوقات تکنیک ما ممکن است یک سرمایه گذاری ارزشمند باشد. به کد موجود در مخزن GitHub ما و همچنین نسخه ی نمایشی نگاهی بیندازید و اگر سوالی داشتید من را در توییتر معرفی کنید!