انتقال نمای CSS را با Velvette افزایش دهید

نوشته دیوید اوموتایو✏️
انتقال صفحه در شکل دادن به تجربه کاربر در طراحی وب مدرن و برنامه های کاربردی بسیار مهم است. ابزارهایی مانند انتقال CSS و Web Animations API به ایجاد نشانه های بصری برای ناوبری و نشان دادن جریان ناوبری کمک می کنند.
انتقال موثر صفحه همچنین با کمک به کاربران در حفظ زمینه و درک زمان بارگذاری سریعتر به کاهش بار شناختی کمک می کند. با این حال، به دلیل وجود کدهای CSS و جاوا اسکریپت مورد نیاز، مدیریت وضعیت عناصر و اطمینان از دسترسی زمانی که هر دو حالت در DOM وجود دارند، اجرای این موارد از ابتدا میتواند بسیار پیچیده باشد.
CSS View Transitions API با اکثر این چالشها مقابله میکند، اما کار کردن با آن به دلایل خاص خود دشوار است – برای مثال، این واقعیت که یک API جدید با کاربردهای متنوع است. اینجاست که ابزارهایی مانند Velvette وارد عمل می شوند.
Velvette کتابخانه ای است که پیاده سازی های انتقال نمایش را ساده می کند و به کاهش این چالش ها کمک می کند. در این مقاله، Velvette را معرفی میکنیم، ویژگیهای آن را بررسی میکنیم و نحوه ادغام آن را در پروژههای موجود توضیح میدهیم.
یک پرایمر سریع در CSS View Transitions
CSS View Transitions API راهی را برای تغییر هموار DOM معرفی میکند و همزمان درونیابی بین دو حالت غیرمرتبط را بدون همپوشانی بین آنها متحرک میکند.
منطق اساسی که این کار را انجام می دهد این است که مرورگر یک عنصر را می گیرد و دو عکس فوری می گیرد: یکی از حالت قدیمی قبل از تغییر و دیگری از حالت جدید بعد از آن. برای ساخت این کار به دو قسمت نیاز دارید.
اول، شما نیاز دارید view-transition-name
ویژگی اختصاص داده شده به انتخابگر عنصر در شیوه نامه شما:
.element{
view-timeline-name: transition-name;
}
دوم، شما به روشی نیاز دارید که DOM پیچیده شده در آن را به روز می کند document.startViewTransition()
تابع:
document.startViewTransition(() =>
updateTheDOMSomehow();
);
این اعلان به مرورگر دستور میدهد که عکسهای فوری را بگیرد، آنها را روی هم قرار دهد و با استفاده از یک انیمیشن محو شدن، یک انتقال ایجاد کند.
چالش های CSS View Transitions API
توسعهدهندگانی که از زمان انتشار View Transitions API کار کردهاند، احتمالاً با یک یا چند مورد از چالشهای زیر مواجه شدهاند:
- نسل نام منحصر به فرد: CSS View Transitions نیاز دارد که هر عنصر یک نام منحصر به فرد داشته باشد که بین حالت های قدیمی و جدید به اشتراک گذاشته شود. تعریف این نام ها برای چندین عنصر می تواند به سرعت خسته کننده شود
- انتقال های محدوده: مشاهده انتقالها بر کل سند تأثیر میگذارد، که میتواند منجر به عکسهای بیهوده زیادی در صفحهای با چندین انتقال شود.
- مدیریت ناوبری: پیاده سازی انتقال ها بر اساس الگوهای ناوبری خاص می تواند دشوار باشد و به کد جاوا اسکریپت قابل توجهی نیاز دارد.
حال، بیایید ببینیم که Velvette چگونه می تواند این چالش ها را کاهش دهد.
معرفی Velvette
Velvette یک کتابخانه ابزاری است که برای آسانتر کردن کار با انتقال دید ایجاد شده است. این برنامه با مسائلی مانند دیگ بخار اضافی و تولید یکنواخت نامهای منحصربهفرد مقابله میکند و به توسعهدهندگان اجازه میدهد تا روی ساخت انیمیشنهای روان تمرکز کنند.
کتابخانه روشی برای مدیریت رفتار انتقال در برنامه شما ارائه می دهد. میتوانید انتقالهایی را برای عناصر جدا شده تعریف کنید – عناصری که به طور مستقل عمل میکنند – یا در پاسخ به رویدادهای ناوبری. سپس این اعلانها بهطور یکپارچه با View Transitions API یکپارچه میشوند.
ویژگی های کلیدی Velvette عبارتند از:
- اضافه کردن کلاس های موقت: Velvette به صورت پویا کلاس های موقت را در طول فرآیند انتقال به عنصر سند اضافه می کند. این کلاسها بهعنوان نشانگرهایی برای ثبت حالتهای مختلف انتقال عمل میکنند. به عنوان مثال، هنگام انتقال از نمای لیست به نمای جزئیات، Velvette ممکن است یک کلاس مانند اضافه کند
morph
در طول انتقال - سبک های ساخت: در حالی که انتقال متحرک است، Velvette سبک های اضافی می سازد. این سبک ها نحوه ظاهر شدن عناصر را در طول انتقال مشخص می کنند. به عنوان مثال، اگر نمای فهرستی را محو میکنید و در نمای جزئیات محو میشوید، Velvette کلاسهای لازم را برای کنترل کدورت، تغییرات زمانبندی انیمیشن و سایر تنظیمات بصری اختصاص میدهد.
- اختصاص دادن
view-transition-name
خواص:view-transition-name
ویژگی برای تعیین اینکه کدام عناصر در انتقال شرکت می کنند بسیار مهم است. Velvette این ویژگی ها را بر اساس قوانین از پیش تعریف شده تولید و تنظیم می کند. این تضمین می کند که عناصر صحیح در طول انتقال متحرک شوند
در بخشهای آینده، بیشتر در مورد نحوه کار Velvette و نحوه شروع کار با آن در پروژه بعدی خود خواهیم دید.
بلوک های ساختمانی مخملی
مخمل دو عملکرد کلیدی را ارائه می دهد: الف Velvette
سازنده و الف startViewTransition
روش. این توابع روشهای سادهشدهای را برای گسترش انتقالهای نمایش در پاسخ به بهروزرسانی DOM ارائه میکنند که نیازهای خاص را برآورده میکند.
این startViewTransition
روش
این startViewTransition
روش ایده آل برای ادغام انیمیشن های انتقال مستقیم، مانند مرتب سازی انیمیشن ها، به یک یا چند عنصر در یک صفحه استفاده می شود. نیاز به اعلام دستی نام های انتقال را از بین می برد و از گرفتن غیر ضروری جلوگیری می کند.
متد یک شی حاوی گزینه های پیکربندی را به عنوان آرگومان می پذیرد:
startViewTransition({
update: () => {...},
captures: {...},
classes: ...,
});
در اینجا یک تفکیک شیء آرگومان آمده است:
-
update
: این یک تابع callback است که نحوه به روز رسانی DOM را در طول انتقال تعریف می کند. به عنوان مثال، اگر یک تابع جداگانه به نام داریدupdateTheDOM
که دستکاری DOM را کنترل می کند، آن تابع را به عنوان آرگومان به روز رسانی منتقل می کنید -
captures
: این شی به شما امکان می دهد عناصری را برای انتقال تعریف کنید. از ساختار کلید-مقدار استفاده می کند. کلیدها انتخابگرهایی برای عناصری هستند که می خواهید ضبط کنید (به عنوان مثال، نام کلاس ها، شناسه ها)، و مقادیر نحوه ایجاد ویژگی های view-transition-name منحصر به فرد را تعیین می کنند (اغلب با استفاده از شناسه عنصر یا سایر شناسه های منحصر به فرد) -
classes
: این یک آرایه اختیاری از نام کلاس های CSS است که به طور موقت در طول انتقال به عنصر سند اضافه می شود. افزودن این کلاس ها می تواند برای اعمال سبک های خاص در طول انیمیشن مفید باشد
این Velvette
سازنده
این Velvette
سازنده برای ایجاد انیمیشن های پیچیده انتقال نمای در مسیر ناوبری صفحه طراحی شده است. یک مثال معمولی یک انتقال صاف تصویر است – مانند بزرگ شدن یا کوچک شدن – زمانی که کاربر بین یک لیست و یک صفحه جزئیات حرکت می کند.
شبیه به startViewTransition
، سازنده یک شی پیکربندی با گزینه های مختلف را به عنوان آرگومان خود می پذیرد:
const velvette = new Velvette({
routes: {
details: ""..."
list: "..."
},
rules: [{
with: ["list", "details"], class: ...
}, ],
captures: {
...
}
});
در اینجا به تفکیک گزینه های پیکربندی آمده است:
-
routes
: این شی مسیرهای نامگذاری شده را برای نماهای مختلف در برنامه شما تعریف می کند. از جفتهای کلید-مقدار استفاده میکند، جایی که کلیدها نام مسیرها هستند و مقادیر میتوانند URLهایی باشند که به طور منحصربهفرد نما را شناسایی کنند. -
rules
: این مجموعه ای از قوانین است که با الگوهای ناوبری خاص مطابقت دارد. هر قانون تعیین میکند که کدام پیمایشها یک انتقال view را راهاندازی میکنند و کلاس و پارامتری را برای مرتبط شدن با انتقال مشخص میکند. -
captures
: مشابهstartViewTransition
روش، این گزینه به شما امکان می دهد عناصری را برای ضبط در طول انتقال ناوبری تعریف کنید. این امر کنترل دقیق تری بر عناصر درگیر در انیمیشن فراهم می کند
نصب و راه اندازی Velvette
Velvette به عنوان یک افزونه ساخته شده است، به این معنی که ما می توانیم آن را به پروژه های موجود اضافه کنیم و فقط تگ اسکریپت زیر را در آن قرار دهیم. index.html
فایل:
>"https://www.unpkg.com/velvette@0.1.10-pre/dist/browser/velvette.js">
همچنین می توانیم آن را با npm با استفاده از دستور زیر اضافه کنیم:
>npm install velvette
ادغام Velvette با پروژه های موجود
هنگامی که Velvette در پروژه شما ادغام شد، می توانید با وارد کردن آن شروع به استفاده از کتابخانه کنید startViewTransition
یا Velvette
سازنده در کامپوننت ها یا صفحات مورد نیاز:
import {Velvette, startViewTransition} from "velvette";
از طرف دیگر، اگر Velvette را با استفاده از پیوند CDN اضافه کرده اید، می توانید به سادگی سازنده Velvette را به این صورت فراخوانی کنید:
const velvette = new Velvette({...});
این امکان پذیر است زیرا پیوند CDN به طور خودکار یک سراسری را تزریق می کند Velvette
کلاس را مستقیماً بر روی شی پنجره خود قرار دهید، که می تواند در سراسر سند قابل دسترسی باشد.
انیمیشن مورد لیست
اکنون که با موفقیت Velvette را به پروژه خود اضافه کرده اید، می توانید هر پیاده سازی Vanilla View Transitions را در پروژه خود با Velvette جایگزین کنید.
به عنوان مثال، فرض کنید یک برنامه کاری با یک پیاده سازی پایه View Transitions مانند مثال زیر دارید:
document.addEventListener("DOMContentLoaded", () => {
const items = document.querySelectorAll(".item");
items.forEach((item, index) => {
item.id = `item-${index}`;
item.addEventListener("click", (e) => {
document.startViewTransition(() => moveItem(e));
});
});
});
const moveItem = (e) => {
const item = e.target;
var targetBoxId = item.closest(".box").id === "box1" ? "box2" : "box1";
var targetList = document.getElementById(targetBoxId).querySelector("ul");
item.parentNode.removeChild(item);
targetList.appendChild(item);
};
این پیاده سازی پایه به این صورت خواهد بود:
انتقال لیست Pen Todo توسط david omotayo (@david4473) را در CodePen ببینید.
در چنین شرایطی می توانیم جایگزین کنیم document.startViewTransition()
اعلام:
document.startViewTransition(() => moveItem(e));
با بیانیه مخملی به شرح زیر:
>Velvette.startViewTransition({
update: () => moveItem(e)
});
این Velvette را فراخوانی می کند moveItem()
روی هر کلیک روی آیتم عمل کنید، و انیمیشن محو پیشفرض را برای هر آیتم از فهرست، زمانی که حذف یا به یکی از آنها اضافه میشوند، اعمال کنید. Tasks
یا Completed Tasks
عناصر والد
با این حال، برای اینکه هر آیتم به آرامی متحرک شود، به یک ویژگی منحصر به فرد نیاز دارد view-transition-name
ارزش.
فرض کنید یک نام انتقال را فقط به اولین مورد در لیست اختصاص می دهیم:
#item-0{
view-transition-name: item;
}
همانطور که انتظار می رود، فقط اولین مورد متحرک است: برای دستیابی به اثر یکسان برای همه موارد، ما به طور سنتی نیاز به اختصاص یک منحصر به فرد داریم
view-transition-name
ارزش برای هر یک، که می تواند بسیار خسته کننده باشد. این جایی است که مخملی است captures
به جای تخصیص دستی، میتوانید از عکسها برای نگاشت پویا بین انتخابگرهای آیتم و اختصاص موقت استفاده کنید. view-transition-name
مقادیر در طول انتقال:
Velvette.startViewTransition({
update: () => moveItem(e),
captures: {
"ul#list li[:id]": "$(id)",
},
});
در اینجا، ما هر کودکی را اسیر می کنیم li
عنصر در #list
انتخاب کنید و از عنصر استفاده کنید id
برای تولید الف view-transition-name
ویژگی.
این ممکن است کمی سخت به نظر برسد، بنابراین اجازه دهید آن را تجزیه کنیم. به یاد داشته باشید، یک id
به هر مورد در لیست اختصاص داده می شود:
const items = document.querySelectorAll(".item");
items.forEach((item, index) => {
item.id = `item-${index}`;
...
});
و عناصر والد آنها a اختصاص داده می شود list
انتخابگر شناسه:
این captures
شی به دنبال ul
عنصر با list
انتخابگرهای کلاس در کد بالا، از طریق آن نگاشت می شوند li
عناصر فرزند، شناسه ای را که در کد قبلی اختصاص داده بودیم، می گیرد و به آنها اختصاص می دهد view-transition-name
اعلامیه ها:
captures: {
"ul#list li[:id]": "$(id)",
},
این view-transition-name
اعلان برای هر مورد در لیست چیزی شبیه به این خواهد بود:
>#item-0{
view-transition-name: item-0;
}
#item-1{
view-transition-name: item-1;
}
#item-2{
view-transition-name: item-2;
}
...
و نتیجه: همانطور که می بینید، انیمیشن اکنون برای هر آیتم لیست به درستی کار می کند.
انیمیشن ناوبری
یک مورد معمول برای View Transitions API مدیریت انیمیشن ها در طول پیمایش صفحه است که اساسا بین صفحات خروجی و ورودی منتقل می شود. همانطور که قبلا ذکر شد، یک مثال محبوب شامل متحرک سازی پیمایش بین نمای لیست و صفحه جزئیات است: اجرای این اثر گذار از ابتدا می تواند چالش برانگیز باشد. معمولاً هنگام پیمایش بین صفحات فهرست و جزئیات، باعث ایجاد یک انتقال دید می شود.
یکی از راههای دستیابی به این هدف، رهگیری رویداد ناوبری و کپسولهسازی تابع بهروزرسانی DOM – تابعی که محتوای صفحه را تغییر میدهد – در View Transitions API است. startViewTransition
روش.
در اینجا یک مثال است:
async function init() {
const data = await fetch("products.json");
const results = await data.json();
function render() {
const title = document.getElementById("title");
const product_list = document.querySelector("#product-list ul");
product_list.innerHTML = "";
for (const product of results) {
const li = document.createElement("li");
li.id = `product-${product.id}`;
li.innerHTML = `
<a href="?product=${product.id}">
<img class="product-img" src="${product.image}" />
<span class="title">${product.title}span>
a>
`;
product_list.append(li);
}
const searchParams = new URL(location.href).searchParams;
if (searchParams.has("product")) {
const productId = +searchParams.get("product");
const product = results.find((product) => product.id === productId);
if (product) {
const details = document.querySelector("#product-details");
details.querySelector(".title").innerText = product.title;
details.querySelector("img").src = `${product.image}`;
}
}
if (searchParams.has("product")) {
title.innerText = "Product Details";
} else {
title.innerText = "Product List";
}
document.documentElement.classList.toggle(
"details",
searchParams.has("product")
);
}
render();
navigation.addEventListener("navigate", (e) => {
e.intercept({
handler() {
document.startViewTransition(() => {
render();
});
},
});
});
}
init();
در این مثال کد، ما از Navigation API برای رهگیری پیمایش بین صفحه فهرست و جزئیات و راه اندازی یک انتقال view که به آن اعمال می شود استفاده کردیم. render()
تابع: کد کامل این مثال را می توانید در این مخزن GitHub بیابید.
توجه داشته باشید که Navigation API در حال حاضر پشتیبانی محدودی از مرورگر دارد — این فقط در مرورگرهای مبتنی بر Chromium در دسترس است. برای اطمینان از UX خوب برای طیف وسیع تری از کاربران، پیاده سازی مکانیسم های بازگشتی را برای مرورگرهای پشتیبانی نشده در نظر بگیرید.
این پیاده سازی اساسی یک نقطه شروع را فراهم می کند، اما دستیابی به یک اثر پیچیده تر به مراحل اضافی نیاز دارد.
به عنوان مثال، برای تغییر شکل تصاویر کوچک بین صفحات لیست و جزئیات، باید یکسان را اختصاص دهیم. view-transition-name
مقادیر به ریز عکسهای مربوطه در هر دو صفحه جزئیات و لیست. با این حال، این تکلیف باید به صورت استراتژیک انجام شود:
- برای جلوگیری از رد شدن از انتقال، نباید به طور همزمان اتفاق بیفتد
- باید به آیتم خاصی درگیر در انتقال در لیست اختصاص داده شود
در حالی که قطعه کد زیر ممکن است به برخی تنظیمات نیاز داشته باشد، مفهوم اصلی را نشان می دهد:
.details-thumbnail {
view-transition-name: morph;
}
//Javascript
list-thumbnail.addEventListener(“click”, async () => {
list-thumbnail.style.viewTransitionName = "morph";
document.startViewTransition(() => {
thumbnail.style.viewTransitionName = "";
updateTheDOMSomehow();
});
};
بزرگترین اشکال این روش، پیچیدگی غیر ضروری و کد دیگ بخاری است که به پروژه شما اضافه می کند.
Velvette این فرآیند را با ارائه یک سیستم پیکربندی متمرکز ساده می کند. این پیکربندی با انجام کارهای سنگین در پشت صحنه، نیاز به پیاده سازی دستی را از بین می برد و در زمان و تلاش شما صرفه جویی می کند:
const velvette = new Velvette({
routes: {
details: "?product=:product_id",
list: "?products",
},
rules: [
{
with: ["list", "details"],
class: "morph",
},
],
captures: {
":root.vt-morph.vt-route-details #details-img": "morph-img",
":root.vt-morph.vt-route-list #product-$(product_id) img": "morph-img",
},
});
این پیکربندی مخملی یک کپی از انتقال ناوبری است که قبلاً با استفاده از انتقال دید سعی کردیم به صورت دستی پیاده سازی کنیم.
در این پیکربندی، ما از routes
و rules
ویژگی ها برای تعیین اینکه کدام ناوبری باعث ایجاد یک انتقال نمای می شود. در این مورد، هر ناوبری بین list
و details
مسیرها یک انتقال view را آغاز می کنند و a اضافه می کنند morph
کلاس به انتقال:
routes: {
details: "?product=:product_id",
list: "?products",
},
rules: [
{
with: ["list", "details"],
class: "morph",
},
],
...
این captures
ویژگی با چالش ذکر شده قبلی یعنی اختصاص منحصر به فرد مقابله می کند view-transition-name
خواص در حین انتقال:
captures: {
":root.vt-morph.vt-route-details #details-img": "morph-img",
":root.vt-morph.vt-route-list #product-$(product_id) img": "morph-img",
},
در اینجا، ما از یک جفت انتخابگر و مقادیر کلید-مقدار برای تخصیص نامهای انتقال یکسان استفاده میکنیم. morph-img
، برای تولید الف view-transition-name
هم برای تصویر کوچک صفحه جزئیات و هم برای تصویر مورد محصول کلیک شده.
این ":root.vt-morph.vt-route-details #details-img"
انتخابگر ترکیبی از:
- کلاس انتقال –
vt-morph
از شی قوانین - مسیری که میخواهیم آن را بگیریم
morph
انتقال –vt-route-details
- انتخابگر تصویر –
#details-img
توجه داشته باشید که vt
برای اینکه Velvette انتخابگرها را بشناسد، پیشوند لازم است.
انتخابگر دوم، ":root.vt-morph.vt-route-list #product-$(product_id) img"
، از همین روش برای اضافه کردن استفاده می کند morph-img
انتقال نام به محصول انتخاب شده در طول morph
انتقال تنها تفاوت این است که فقط زمانی اعمال می شود که در list
مسیر و ${product_id}
عبارت با شناسه مورد محصول جایگزین می شود، مانند:
:root.vt-morph.vt-route-list #product-1 img: ...,
در نهایت، میتوانیم از Velvette برای رهگیری مسیریابی و اعمال تنظیمات تعریفشده در بالا استفاده کنیم. برای رسیدن به این هدف، اعلامیه ناوبری قبلی را به صورت زیر به روز می کنیم:
navigation.addEventListener("navigate", (e) => {
velvette.intercept(e, {
handler() {
render();
},
});
});
نتیجه این است:
نتیجه
در این مقاله، Velvette را معرفی کردیم و بلوکهای سازنده آن و نحوه عملکرد آنها برای دستیابی به انتقال روانتر و جذابتر بین نماها را بررسی کردیم. ما همچنین چگونگی ادغام Velvette را در پروژههای موجود بدون بازنگری کامل کد موجود بررسی کردیم.
در حالی که Velvette قابلیتهای انتقال قدرتمندی را ارائه میکند، اما بر اساس View Transitions API ساخته شده است، که در حال حاضر پشتیبانی محدودی از مرورگر دارد، بنابراین مکانیسمهای بازگشتی را برای مرورگرهایی که از API پشتیبانی نمیکنند، در نظر بگیرید.
ما فقط سطح آنچه را که می توانید با Velvette به دست آورید در این مقاله خراشیده ایم. اگر مشتاق کسب اطلاعات بیشتر در مورد کتابخانه هستید، مستندات Velvette نمونه های جامعی را ارائه می دهد که به شما در شروع کار کمک می کند.
آیا ظاهر شما به CPU کاربران شما توجه دارد؟
همانطور که صفحات وب به طور فزاینده ای پیچیده می شوند، ویژگی های حریص منابع بیشتر و بیشتر از مرورگر طلب می کنند. اگر علاقه مند به نظارت و ردیابی استفاده از CPU سمت مشتری، استفاده از حافظه و موارد دیگر برای همه کاربران خود در تولید هستید، LogRocket را امتحان کنید.
LogRocket مانند یک DVR برای برنامه های وب و تلفن همراه است که هر چیزی را که در برنامه وب، برنامه تلفن همراه یا وب سایت شما اتفاق می افتد ضبط می کند. به جای حدس زدن چرایی مشکلات، میتوانید معیارهای کلیدی عملکرد frontend را جمعآوری کرده و گزارش دهید، جلسات کاربر را به همراه وضعیت برنامهها دوباره پخش کنید، درخواستهای شبکه را ثبت کنید و بهطور خودکار همه خطاها را نشان دهید.
نحوه اشکال زدایی برنامه های وب و تلفن همراه را مدرن کنید — نظارت را به صورت رایگان شروع کنید.