প্রকাশিত: আগস্ট 17, 2021, শেষ আপডেট: সেপ্টেম্বর 25, 2024
যখন একটি ভিউ ট্রানজিশন একটি একক নথিতে চলে তখন একে একই-ডকুমেন্ট ভিউ ট্রানজিশন বলে। এটি সাধারণত একক-পৃষ্ঠা অ্যাপ্লিকেশন (এসপিএ) এর ক্ষেত্রে হয় যেখানে জাভাস্ক্রিপ্ট DOM আপডেট করতে ব্যবহৃত হয়। একই-ডকুমেন্ট ভিউ ট্রানজিশনগুলি Chrome 111-এর মতো Chrome-এ সমর্থিত।
একই-ডকুমেন্ট ভিউ ট্রানজিশন ট্রিগার করতে, document.startViewTransition
কল করুন:
function handleClick(e) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow();
return;
}
// With a View Transition:
document.startViewTransition(() => updateTheDOMSomehow());
}
যখন আহ্বান করা হয়, ব্রাউজার স্বয়ংক্রিয়ভাবে সমস্ত উপাদানের স্ন্যাপশট ক্যাপচার করে যেগুলির উপর একটি view-transition-name
CSS সম্পত্তি রয়েছে।
এটি তারপরে পাস করা কলব্যাকে কার্যকর করে যা DOM আপডেট করে, তারপরে এটি নতুন অবস্থার স্ন্যাপশট নেয়।
এই স্ন্যাপশটগুলিকে সিউডো-এলিমেন্টের গাছে সাজানো হয় এবং CSS অ্যানিমেশনের শক্তি ব্যবহার করে অ্যানিমেটেড করা হয়। পুরানো এবং নতুন অবস্থা থেকে স্ন্যাপশটগুলির জোড়া তাদের পুরানো অবস্থান এবং আকার থেকে তাদের নতুন অবস্থানে মসৃণভাবে স্থানান্তরিত হয়, যখন তাদের বিষয়বস্তু ক্রসফেড হয়। আপনি চাইলে অ্যানিমেশন কাস্টমাইজ করতে CSS ব্যবহার করতে পারেন।
ডিফল্ট রূপান্তর: ক্রস-ফেইড
ডিফল্ট ভিউ ট্রানজিশন একটি ক্রস-ফেড, তাই এটি API-এর একটি সুন্দর ভূমিকা হিসেবে কাজ করে:
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// With a transition:
document.startViewTransition(() => updateTheDOMSomehow(data));
}
যেখানে updateTheDOMSomehow
DOM কে নতুন অবস্থায় পরিবর্তন করে। যেটা আপনি চাইলেই করা যেতে পারে। উদাহরণস্বরূপ, আপনি উপাদান যোগ করতে বা সরাতে পারেন, শ্রেণির নাম পরিবর্তন করতে পারেন বা শৈলী পরিবর্তন করতে পারেন।
এবং ঠিক সেই মত, পৃষ্ঠাগুলি ক্রস-ফেড:
ঠিক আছে, একটি ক্রস-ফেড যে চিত্তাকর্ষক নয়। সৌভাগ্যক্রমে, রূপান্তরগুলি কাস্টমাইজ করা যেতে পারে, তবে প্রথমে, আপনাকে বুঝতে হবে কিভাবে এই মৌলিক ক্রস-ফেড কাজ করে।
কিভাবে এই রূপান্তর কাজ
আগের কোড নমুনা আপডেট করা যাক.
document.startViewTransition(() => updateTheDOMSomehow(data));
যখন .startViewTransition()
কল করা হয়, API পৃষ্ঠার বর্তমান অবস্থা ক্যাপচার করে। এর মধ্যে একটি স্ন্যাপশট নেওয়া অন্তর্ভুক্ত।
একবার সম্পূর্ণ হলে, .startViewTransition()
এ পাস করা কলব্যাক বলা হয়। যে যেখানে DOM পরিবর্তন করা হয়. তারপর, API পৃষ্ঠার নতুন অবস্থা ক্যাপচার করে।
একবার নতুন অবস্থা ক্যাপচার করা হলে, API এই মত একটি ছদ্ম-উপাদান গাছ তৈরি করে:
::view-transition
└─ ::view-transition-group(root)
└─ ::view-transition-image-pair(root)
├─ ::view-transition-old(root)
└─ ::view-transition-new(root)
::view-transition
পৃষ্ঠার অন্য সব কিছুর উপরে একটি ওভারলেতে বসে। আপনি যদি রূপান্তরের জন্য একটি পটভূমির রঙ সেট করতে চান তবে এটি কার্যকর।
::view-transition-old(root)
হল পুরানো ভিউ এর একটি স্ক্রিনশট, এবং ::view-transition-new(root)
হল নতুন ভিউ এর একটি লাইভ উপস্থাপনা৷ উভয়ই CSS 'প্রতিস্থাপিত সামগ্রী' হিসাবে রেন্ডার করে (যেমন একটি <img>
)।
পুরানো ভিউ opacity: 1
থেকে opacity: 0
, যখন নতুন ভিউ opacity: 0
থেকে opacity: 1
, একটি ক্রস-ফেড তৈরি করে।
সমস্ত অ্যানিমেশন সিএসএস অ্যানিমেশন ব্যবহার করে সঞ্চালিত হয়, তাই সেগুলি সিএসএস দিয়ে কাস্টমাইজ করা যায়।
রূপান্তর কাস্টমাইজ করুন
সমস্ত ভিউ ট্রানজিশন সিউডো-এলিমেন্ট CSS দিয়ে টার্গেট করা যেতে পারে এবং যেহেতু অ্যানিমেশনগুলি CSS ব্যবহার করে সংজ্ঞায়িত করা হয়েছে, আপনি বিদ্যমান CSS অ্যানিমেশন বৈশিষ্ট্যগুলি ব্যবহার করে সেগুলি পরিবর্তন করতে পারেন। যেমন:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 5s;
}
সেই একটি পরিবর্তনের সাথে, বিবর্ণ এখন সত্যিই ধীর:
ঠিক আছে, এটি এখনও চিত্তাকর্ষক নয়। পরিবর্তে, নিম্নলিখিত কোডটি মেটেরিয়াল ডিজাইনের ভাগ করা অক্ষ পরিবর্তনকে প্রয়োগ করে:
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(30px); }
}
@keyframes slide-to-left {
to { transform: translateX(-30px); }
}
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
এবং এখানে ফলাফল:
রূপান্তর একাধিক উপাদান
পূর্ববর্তী ডেমোতে, পুরো পৃষ্ঠাটি ভাগ করা অক্ষ পরিবর্তনের সাথে জড়িত। এটি বেশিরভাগ পৃষ্ঠার জন্য কাজ করে, তবে শিরোনামের জন্য এটি পুরোপুরি সঠিক বলে মনে হচ্ছে না, কারণ এটি আবার স্লাইড করার জন্য স্লাইড করে বেরিয়ে যায়।
এটি এড়াতে, আপনি বাকি পৃষ্ঠা থেকে শিরোনামটি বের করতে পারেন যাতে এটি আলাদাভাবে অ্যানিমেট করা যায়। এটি উপাদানটিতে একটি view-transition-name
বরাদ্দ করে করা হয়।
.main-header {
view-transition-name: main-header;
}
view-transition-name
মান আপনি যা চান তা হতে পারে ( none
ছাড়া, যার মানে কোনও পরিবর্তনের নাম নেই)। এটি রূপান্তর জুড়ে উপাদানটিকে অনন্যভাবে সনাক্ত করতে ব্যবহৃত হয়।
এবং এর ফলাফল:
এখন হেডার জায়গায় থাকে এবং ক্রস-বিবর্ণ হয়।
সেই CSS ঘোষণার ফলে ছদ্ম-উপাদান গাছটি পরিবর্তন হয়েছে:
::view-transition
├─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
└─ ::view-transition-group(main-header)
└─ ::view-transition-image-pair(main-header)
├─ ::view-transition-old(main-header)
└─ ::view-transition-new(main-header)
এখন দুটি ট্রানজিশন গ্রুপ আছে। শিরোনাম জন্য একটি, এবং বাকি জন্য অন্য. এগুলোকে CSS দিয়ে স্বাধীনভাবে টার্গেট করা যায় এবং বিভিন্ন ট্রানজিশন দেওয়া যায়। যদিও, এই ক্ষেত্রে main-header
ডিফল্ট রূপান্তর সহ বাকি ছিল, যা একটি ক্রস-ফেড।
আচ্ছা, ঠিক আছে, ডিফল্ট ট্রানজিশন শুধু ক্রস ফেইড নয়, ::view-transition-group
ও ট্রানজিশন করে:
- অবস্থান এবং রূপান্তর (একটি
transform
ব্যবহার করে) - প্রস্থ
- উচ্চতা
এটি এখন পর্যন্ত গুরুত্বপূর্ণ নয়, কারণ শিরোনামটি একই আকার এবং DOM পরিবর্তনের উভয় দিকে অবস্থান। তবে আপনি শিরোনামে পাঠ্যটিও বের করতে পারেন:
.main-header-text {
view-transition-name: main-header-text;
width: fit-content;
}
fit-content
ব্যবহার করা হয় তাই উপাদানটি বাকি প্রস্থে প্রসারিত না হয়ে পাঠ্যের আকার। এটি ছাড়া, পিছনের তীরটি উভয় পৃষ্ঠায় একই আকারের পরিবর্তে হেডার পাঠ্য উপাদানের আকার হ্রাস করে।
সুতরাং এখন আমাদের সাথে খেলতে তিনটি অংশ রয়েছে:
::view-transition
├─ ::view-transition-group(root)
│ └─ …
├─ ::view-transition-group(main-header)
│ └─ …
└─ ::view-transition-group(main-header-text)
└─ …
কিন্তু আবার, শুধু ডিফল্টের সাথে যাচ্ছে:
এখন হেডিং টেক্সট পিছনের বোতামের জন্য জায়গা তৈরি করতে একটু সন্তোষজনক স্লাইড করে।
view-transition-class
সাথে একইভাবে একাধিক সিউডো-এলিমেন্ট অ্যানিমেট করুন
ব্রাউজার সমর্থন
বলুন আপনার কাছে একগুচ্ছ কার্ডের সাথে একটি ভিউ ট্রানজিশন আছে কিন্তু পৃষ্ঠায় একটি শিরোনামও রয়েছে। শিরোনাম ছাড়া সমস্ত কার্ড অ্যানিমেট করতে, আপনাকে একটি নির্বাচক লিখতে হবে যা প্রতিটি পৃথক কার্ডকে লক্ষ্য করে।
h1 {
view-transition-name: title;
}
::view-transition-group(title) {
animation-timing-function: ease-in-out;
}
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
…
#card20 { view-transition-name: card20; }
::view-transition-group(card1),
::view-transition-group(card2),
::view-transition-group(card3),
::view-transition-group(card4),
…
::view-transition-group(card20) {
animation-timing-function: var(--bounce);
}
20টি উপাদান পেয়েছেন? যে 20 নির্বাচক আপনার লিখতে হবে. একটি নতুন উপাদান যোগ করা হচ্ছে? তারপরে আপনাকে নির্বাচক বাড়াতে হবে যা অ্যানিমেশন শৈলীগুলি প্রয়োগ করে। ঠিক মাপযোগ্য নয়।
একই শৈলীর নিয়ম প্রয়োগ করতে view-transition-class
ভিউ ট্রানজিশন সিউডো-এলিমেন্টগুলিতে ব্যবহার করা যেতে পারে।
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
#card5 { view-transition-name: card5; }
…
#card20 { view-transition-name: card20; }
#cards-wrapper > div {
view-transition-class: card;
}
html::view-transition-group(.card) {
animation-timing-function: var(--bounce);
}
নিম্নলিখিত কার্ডের উদাহরণ পূর্ববর্তী সিএসএস স্নিপেটটি ব্যবহার করে। সমস্ত কার্ড-সদ্য যোগ করাগুলি সহ-একটি নির্বাচকের সাথে একই সময় প্রয়োগ করুন: html::view-transition-group(.card)
।
ডিবাগ ট্রানজিশন
যেহেতু ভিউ ট্রানজিশনগুলি CSS অ্যানিমেশনগুলির উপরে তৈরি করা হয়, তাই Chrome DevTools-এর অ্যানিমেশন প্যানেলটি ডিবাগিং ট্রানজিশনের জন্য দুর্দান্ত৷
অ্যানিমেশন প্যানেল ব্যবহার করে, আপনি পরবর্তী অ্যানিমেশন থামাতে পারেন, তারপর অ্যানিমেশনের মাধ্যমে সামনে পিছনে স্ক্রাব করতে পারেন। এই সময়, রূপান্তর ছদ্ম-উপাদান উপাদান প্যানেলে পাওয়া যাবে.
রূপান্তর উপাদান একই DOM উপাদান হতে হবে না
এখন পর্যন্ত আমরা হেডার এবং হেডারের পাঠ্যের জন্য পৃথক রূপান্তর উপাদান তৈরি করতে view-transition-name
ব্যবহার করেছি। এগুলি ধারণাগতভাবে DOM পরিবর্তনের আগে এবং পরে একই উপাদান, তবে আপনি পরিবর্তনগুলি তৈরি করতে পারেন যেখানে এটি হয় না।
উদাহরণস্বরূপ, প্রধান ভিডিও এম্বেডকে একটি view-transition-name
দেওয়া যেতে পারে:
.full-embed {
view-transition-name: full-embed;
}
তারপর, থাম্বনেইলে ক্লিক করা হলে, এটিকে একই view-transition-name
দেওয়া যেতে পারে, শুধুমাত্র ট্রানজিশনের সময়কালের জন্য:
thumbnail.onclick = async () => {
thumbnail.style.viewTransitionName = 'full-embed';
document.startViewTransition(() => {
thumbnail.style.viewTransitionName = '';
updateTheDOMSomehow();
});
};
এবং ফলাফল:
থাম্বনেইল এখন মূল ছবিতে রূপান্তরিত হয়। যদিও তারা ধারণাগতভাবে (এবং আক্ষরিক অর্থে) ভিন্ন উপাদান, ট্রানজিশন API তাদের একই জিনিস হিসাবে বিবেচনা করে কারণ তারা একই view-transition-name
ভাগ করেছে।
এই রূপান্তরের আসল কোডটি পূর্ববর্তী উদাহরণের তুলনায় একটু বেশি জটিল, কারণ এটি থাম্বনেইল পৃষ্ঠায় রূপান্তরটি পরিচালনা করে। সম্পূর্ণ বাস্তবায়নের জন্য উৎস দেখুন ।
কাস্টম এন্ট্রি এবং প্রস্থান ট্রানজিশন
এই উদাহরণ দেখুন:
সাইডবারটি রূপান্তরের অংশ:
.sidebar {
view-transition-name: sidebar;
}
কিন্তু, পূর্ববর্তী উদাহরণের শিরোনাম থেকে ভিন্ন, সাইডবারটি সমস্ত পৃষ্ঠায় প্রদর্শিত হয় না। উভয় রাজ্যের সাইডবার থাকলে, রূপান্তর ছদ্ম-উপাদানগুলি এইরকম দেখায়:
::view-transition
├─ …other transition groups…
└─ ::view-transition-group(sidebar)
└─ ::view-transition-image-pair(sidebar)
├─ ::view-transition-old(sidebar)
└─ ::view-transition-new(sidebar)
যাইহোক, যদি সাইডবার শুধুমাত্র নতুন পৃষ্ঠায় থাকে, তাহলে ::view-transition-old(sidebar)
ছদ্ম-উপাদান সেখানে থাকবে না। যেহেতু সাইডবারের জন্য কোনো 'পুরানো' ছবি নেই, তাই ইমেজ-পেয়ারে শুধুমাত্র একটি ::view-transition-new(sidebar)
থাকবে। একইভাবে, যদি সাইডবার শুধুমাত্র পুরানো পৃষ্ঠায় থাকে, তাহলে ইমেজ-পেয়ারে শুধুমাত্র একটি ::view-transition-old(sidebar)
থাকবে।
পূর্ববর্তী ডেমোতে, সাইডবারটি ভিন্নভাবে রূপান্তরিত হয় তা নির্ভর করে যে এটি উভয় অবস্থায় প্রবেশ করছে, প্রস্থান করছে বা উপস্থিত রয়েছে। এটি ডান দিক থেকে স্লাইড করে প্রবেশ করে এবং ভিতরে বিবর্ণ হয়ে যায়, এটি ডানদিকে স্লাইড করে এবং বিবর্ণ হয়ে প্রস্থান করে এবং উভয় অবস্থায় উপস্থিত থাকলে এটি অবস্থানে থাকে।
নির্দিষ্ট এন্ট্রি এবং এক্সিট ট্রানজিশন তৈরি করতে, আপনি :only-child
pseudo-class ব্যবহার করতে পারেন পুরনো বা নতুন ছদ্ম-উপাদানগুলিকে লক্ষ্য করার জন্য যখন এটি ইমেজ-জোড়ার একমাত্র সন্তান হয়:
/* Entry transition */
::view-transition-new(sidebar):only-child {
animation: 300ms cubic-bezier(0, 0, 0.2, 1) both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Exit transition */
::view-transition-old(sidebar):only-child {
animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
}
এই ক্ষেত্রে, উভয় অবস্থায় সাইডবার উপস্থিত থাকার জন্য কোন নির্দিষ্ট পরিবর্তন নেই, যেহেতু ডিফল্টটি নিখুঁত।
Async DOM আপডেট, এবং বিষয়বস্তুর জন্য অপেক্ষা করছে
.startViewTransition()
এ পাস করা কলব্যাক একটি প্রতিশ্রুতি ফিরিয়ে দিতে পারে, যা DOM আপডেটগুলিকে অ্যাসিঙ্ক করার অনুমতি দেয় এবং গুরুত্বপূর্ণ বিষয়বস্তু প্রস্তুত হওয়ার জন্য অপেক্ষা করে৷
document.startViewTransition(async () => {
await something;
await updateTheDOMSomehow();
await somethingElse;
});
প্রতিশ্রুতি পূরণ না হওয়া পর্যন্ত স্থানান্তর শুরু হবে না। এই সময়ের মধ্যে, পৃষ্ঠাটি হিমায়িত হয়, তাই এখানে বিলম্ব একটি সর্বনিম্ন রাখা উচিত। বিশেষত, .startViewTransition()
.startViewTransition()
কলব্যাকের অংশ হিসাবে না করে কল করার আগে করা উচিত৷
আপনি যদি ছবি বা ফন্ট প্রস্তুত হওয়ার জন্য অপেক্ষা করার সিদ্ধান্ত নেন, তাহলে একটি আক্রমনাত্মক টাইমআউট ব্যবহার করতে ভুলবেন না:
const wait = ms => new Promise(r => setTimeout(r, ms));
document.startViewTransition(async () => {
updateTheDOMSomehow();
// Pause for up to 100ms for fonts to be ready:
await Promise.race([document.fonts.ready, wait(100)]);
});
যাইহোক, কিছু ক্ষেত্রে বিলম্বকে সম্পূর্ণভাবে এড়িয়ে যাওয়া এবং আপনার ইতিমধ্যেই থাকা সামগ্রী ব্যবহার করা ভাল।
আপনার ইতিমধ্যেই রয়েছে এমন সামগ্রীর সর্বাধিক ব্যবহার করুন৷
যে ক্ষেত্রে থাম্বনেইল একটি বড় ছবিতে রূপান্তরিত হয়:
ডিফল্ট ট্রানজিশন হল ক্রস-ফেড, যার মানে থাম্বনেইলটি এখনও লোড করা হয়নি এমন পূর্ণ চিত্রের সাথে ক্রস-বিবর্ণ হতে পারে।
এটি পরিচালনা করার একটি উপায় হল রূপান্তর শুরু করার আগে সম্পূর্ণ চিত্রটি লোড হওয়ার জন্য অপেক্ষা করা। আদর্শভাবে এটি .startViewTransition()
কল করার আগে করা হবে, যাতে পৃষ্ঠাটি ইন্টারেক্টিভ থাকে এবং একটি স্পিনার ব্যবহারকারীকে বোঝাতে দেখাতে পারে যে জিনিসগুলি লোড হচ্ছে৷ কিন্তু এই ক্ষেত্রে একটি ভাল উপায় আছে:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
}
এখন থাম্বনেইলটি বিবর্ণ হয় না, এটি সম্পূর্ণ চিত্রের নীচে বসে থাকে। এর মানে হল যদি নতুন ভিউ লোড না হয়, থাম্বনেইলটি ট্রানজিশন জুড়ে দৃশ্যমান। এর অর্থ হল রূপান্তরটি সরাসরি শুরু হতে পারে এবং সম্পূর্ণ চিত্রটি তার নিজস্ব সময়ে লোড হতে পারে।
নতুন ভিউতে স্বচ্ছতা থাকলে এটি কাজ করবে না, কিন্তু এই ক্ষেত্রে আমরা জানি এটি নেই, তাই আমরা এই অপ্টিমাইজেশানটি করতে পারি।
আকৃতির অনুপাতের পরিবর্তনগুলি পরিচালনা করুন
সুবিধাজনকভাবে, এখন পর্যন্ত সমস্ত রূপান্তর একই অনুপাতের উপাদানগুলিতে হয়েছে, তবে এটি সর্বদা হবে না। থাম্বনেইল 1:1 হলে এবং প্রধান চিত্রটি 16:9 হলে কী হবে?
ডিফল্ট ট্রানজিশনে, গ্রুপটি আগের সাইজ থেকে পরের সাইজে অ্যানিমেট করে। পুরানো এবং নতুন ভিউ হল গ্রুপের প্রস্থ 100%, এবং স্বয়ংক্রিয় উচ্চতা, মানে তারা গ্রুপের আকার নির্বিশেষে তাদের আকৃতির অনুপাত বজায় রাখে।
এটি একটি ভাল ডিফল্ট, কিন্তু এই ক্ষেত্রে যা চাই তা নয়। তাই:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
/* Make the height the same as the group,
meaning the view size might not match its aspect-ratio. */
height: 100%;
/* Clip any overflow of the view */
overflow: clip;
}
/* The old view is the thumbnail */
::view-transition-old(full-embed) {
/* Maintain the aspect ratio of the view,
by shrinking it to fit within the bounds of the element */
object-fit: contain;
}
/* The new view is the full image */
::view-transition-new(full-embed) {
/* Maintain the aspect ratio of the view,
by growing it to cover the bounds of the element */
object-fit: cover;
}
এর অর্থ হল থাম্বনেইলটি উপাদানটির কেন্দ্রে থাকে প্রস্থ বাড়ার সাথে সাথে, কিন্তু 1:1 থেকে 16:9 পর্যন্ত রূপান্তরিত হওয়ার সাথে সাথে সম্পূর্ণ চিত্রটি 'আন-ক্রপ' হয়।
আরও বিশদ তথ্যের জন্য, রূপান্তরগুলি দেখুন: দৃষ্টিভঙ্গি অনুপাত পরিবর্তনগুলি পরিচালনা করুন
বিভিন্ন ডিভাইসের অবস্থার জন্য ট্রানজিশন পরিবর্তন করতে মিডিয়া কোয়েরি ব্যবহার করুন
আপনি মোবাইল বনাম ডেস্কটপে বিভিন্ন ট্রানজিশন ব্যবহার করতে চাইতে পারেন, যেমন এই উদাহরণটি যা মোবাইলের পাশ থেকে একটি সম্পূর্ণ স্লাইড সম্পাদন করে, কিন্তু ডেস্কটপে আরও সূক্ষ্ম স্লাইড:
এটি নিয়মিত মিডিয়া প্রশ্ন ব্যবহার করে অর্জন করা যেতে পারে:
/* Transitions for mobile */
::view-transition-old(root) {
animation: 300ms ease-out both full-slide-to-left;
}
::view-transition-new(root) {
animation: 300ms ease-out both full-slide-from-right;
}
@media (min-width: 500px) {
/* Overrides for larger displays.
This is the shared axis transition from earlier in the article. */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
}
আপনি কোন উপাদানগুলিকে view-transition-name
বরাদ্দ করবেন তা পরিবর্তন করতে চাইতে পারেন মিডিয়া ক্যোয়ারীগুলির উপর নির্ভর করে।
'হ্রাস গতি' পছন্দ প্রতিক্রিয়া
ব্যবহারকারীরা নির্দেশ করতে পারে যে তারা তাদের অপারেটিং সিস্টেমের মাধ্যমে কম গতি পছন্দ করে এবং সেই পছন্দটি CSS-এ প্রকাশ করা হয়।
আপনি এই ব্যবহারকারীদের জন্য কোনো রূপান্তর প্রতিরোধ করতে বেছে নিতে পারেন:
@media (prefers-reduced-motion) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
যাইহোক, 'কমানো গতি' এর জন্য একটি পছন্দের মানে এই নয় যে ব্যবহারকারী কোনো গতি চায় না। পূর্ববর্তী স্নিপেটের পরিবর্তে, আপনি একটি আরও সূক্ষ্ম অ্যানিমেশন বেছে নিতে পারেন, তবে একটি যা এখনও উপাদান এবং ডেটা প্রবাহের মধ্যে সম্পর্ক প্রকাশ করে।
ভিউ ট্রানজিশন প্রকার সহ একাধিক ভিউ ট্রানজিশন শৈলী পরিচালনা করুন
ব্রাউজার সমর্থন
কখনও কখনও একটি নির্দিষ্ট দৃশ্য থেকে অন্য একটি রূপান্তর একটি বিশেষভাবে উপযোগী রূপান্তর থাকা উচিত। উদাহরণস্বরূপ, একটি পৃষ্ঠা সংখ্যা ক্রমানুসারে পরবর্তী বা পূর্ববর্তী পৃষ্ঠায় যাওয়ার সময়, আপনি অনুক্রম থেকে একটি উচ্চ পৃষ্ঠা বা নিম্ন পৃষ্ঠায় যাচ্ছেন কিনা তার উপর নির্ভর করে বিষয়বস্তুগুলিকে ভিন্ন দিকে স্লাইড করতে চাইতে পারেন৷
এর জন্য আপনি ভিউ ট্রানজিশনের ধরন ব্যবহার করতে পারেন, যা আপনাকে একটি সক্রিয় ভিউ ট্রানজিশনে এক বা একাধিক প্রকার বরাদ্দ করতে দেয়। উদাহরণস্বরূপ, একটি পৃষ্ঠা সংখ্যা ক্রমানুসারে একটি উচ্চ পৃষ্ঠায় স্থানান্তর করার সময় forwards
টাইপ ব্যবহার করুন এবং একটি নিম্ন পৃষ্ঠায় যাওয়ার সময় backwards
টাইপ ব্যবহার করুন৷ এই প্রকারগুলি শুধুমাত্র একটি ট্রানজিশন ক্যাপচার বা সম্পাদন করার সময় সক্রিয় থাকে এবং প্রতিটি প্রকার বিভিন্ন অ্যানিমেশন ব্যবহার করার জন্য CSS এর মাধ্যমে কাস্টমাইজ করা যেতে পারে।
একই-ডকুমেন্ট ভিউ ট্রানজিশনে টাইপ ব্যবহার করতে, আপনি startViewTransition
পদ্ধতিতে types
পাস করেন। এটিকে অনুমতি দেওয়ার জন্য, document.startViewTransition
একটি অবজেক্টও গ্রহণ করে: update
হল কলব্যাক ফাংশন যা DOM আপডেট করে এবং types
হল প্রকারগুলির সাথে একটি অ্যারে।
const direction = determineBackwardsOrForwards();
const t = document.startViewTransition({
update: updateTheDOMSomehow,
types: ['slide', direction],
});
এই ধরনের প্রতিক্রিয়া জানাতে, :active-view-transition-type()
নির্বাচক ব্যবহার করুন। নির্বাচক মধ্যে আপনি লক্ষ্য করতে চান type
পাস. এটি আপনাকে একাধিক ভিউ ট্রানজিশনের স্টাইলগুলিকে একে অপরের থেকে আলাদা রাখতে দেয়, একটির ঘোষণা অন্যটির ঘোষণায় হস্তক্ষেপ না করে।
যেহেতু ধরনগুলি শুধুমাত্র ট্রানজিশন ক্যাপচার করার সময় বা সম্পাদন করার সময় প্রযোজ্য হয়, আপনি শুধুমাত্র সেই টাইপের সাথে ভিউ ট্রানজিশনের জন্য একটি এলিমেন্টে একটি view-transition-name
সেট–বা আনসেট করতে সিলেক্টর ব্যবহার করতে পারেন৷
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only (using the default root snapshot) */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
নিম্নলিখিত পেজিনেশন ডেমোতে , আপনি যে পৃষ্ঠা নম্বরে নেভিগেট করছেন তার উপর ভিত্তি করে পৃষ্ঠার বিষয়বস্তু সামনের দিকে বা পিছনে স্লাইড করে। প্রকারগুলি নির্ধারণ করা হয় ক্লিকের উপর যা তারা document.startViewTransition
এ পাস হয়।
যেকোনো সক্রিয় ভিউ ট্রানজিশন টার্গেট করতে, ধরন নির্বিশেষে, আপনি এর পরিবর্তে :active-view-transition
pseudo-class সিলেক্টর ব্যবহার করতে পারেন।
html:active-view-transition {
…
}
ভিউ ট্রানজিশন রুটে একটি ক্লাস নাম সহ একাধিক ভিউ ট্রানজিশন শৈলী পরিচালনা করুন
কখনও কখনও একটি বিশেষ ধরনের দৃশ্য থেকে অন্য একটি রূপান্তর একটি বিশেষভাবে উপযোগী রূপান্তর থাকা উচিত। অথবা, একটি 'ব্যাক' নেভিগেশন একটি 'ফরোয়ার্ড' নেভিগেশন থেকে ভিন্ন হওয়া উচিত।
ট্রানজিশন টাইপ করার আগে এই কেসগুলি পরিচালনা করার উপায় ছিল অস্থায়ীভাবে ট্রানজিশন রুটে একটি ক্লাস নাম সেট করা। document.startViewTransition
কল করার সময়, এই ট্রানজিশন রুট হল <html>
উপাদান, JavaScript এ document.documentElement
ব্যবহার করে অ্যাক্সেসযোগ্য:
if (isBackNavigation) {
document.documentElement.classList.add('back-transition');
}
const transition = document.startViewTransition(() =>
updateTheDOMSomehow(data)
);
try {
await transition.finished;
} finally {
document.documentElement.classList.remove('back-transition');
}
ট্রানজিশন শেষ হওয়ার পরে ক্লাসগুলি সরাতে, এই উদাহরণটি transition.finished
ব্যবহার করে, একটি প্রতিশ্রুতি যা একবার রূপান্তর শেষ অবস্থায় পৌঁছে গেলে সমাধান করে। এই বস্তুর অন্যান্য বৈশিষ্ট্য API রেফারেন্সে আচ্ছাদিত করা হয়েছে।
এখন আপনি রূপান্তর পরিবর্তন করতে আপনার CSS-এ সেই শ্রেণীর নাম ব্যবহার করতে পারেন:
/* 'Forward' transitions */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms
cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Overrides for 'back' transitions */
.back-transition::view-transition-old(root) {
animation-name: fade-out, slide-to-right;
}
.back-transition::view-transition-new(root) {
animation-name: fade-in, slide-from-left;
}
মিডিয়া ক্যোয়ারীগুলির মতো, এই ক্লাসগুলির উপস্থিতি কোন উপাদানগুলি একটি view-transition-name
পায় তা পরিবর্তন করতেও ব্যবহার করা যেতে পারে।
অন্যান্য অ্যানিমেশন জমা না করে ট্রানজিশন চালান
একটি ভিডিও রূপান্তর অবস্থানের এই ডেমোটি দেখুন:
আপনি এটা সঙ্গে কিছু ভুল দেখেছেন? আপনি না হলে চিন্তা করবেন না. এখানে এটিকে ধীর করা হয়েছে:
ট্রানজিশনের সময়, ভিডিওটি হিমায়িত হতে দেখা যায়, তারপর ভিডিওটির প্লে ভার্সন ফিকে হয়ে যায়। এর কারণ হল ::view-transition-old(video)
হল পুরানো ভিউ এর একটি স্ক্রিনশট, যেখানে ::view-transition-new(video)
হল নতুন ভিউ এর একটি লাইভ ইমেজ।
আপনি এটি ঠিক করতে পারেন, তবে প্রথমে নিজেকে জিজ্ঞাসা করুন যে এটি ঠিক করা উপযুক্ত কিনা। আপনি যদি 'সমস্যা' দেখতে না পান যখন ট্রানজিশনটি তার স্বাভাবিক গতিতে বাজছিল, আমি এটি পরিবর্তন করতে বিরক্ত করব না।
আপনি যদি সত্যিই এটি ঠিক করতে চান, তাহলে ::view-transition-old(video)
দেখাবেন না; সরাসরি ::view-transition-new(video)
এ স্যুইচ করুন। আপনি ডিফল্ট শৈলী এবং অ্যানিমেশন ওভাররাইড করে এটি করতে পারেন:
::view-transition-old(video) {
/* Don't show the frozen old view */
display: none;
}
::view-transition-new(video) {
/* Don't fade the new view in */
animation: none;
}
আর এটাই!
এখন ভিডিওটি ট্রানজিশন জুড়ে চলে।
নেভিগেশন এপিআই (এবং অন্যান্য ফ্রেমওয়ার্ক) এর সাথে ইন্টিগ্রেশন
ভিউ ট্রানজিশনগুলি এমনভাবে নির্দিষ্ট করা হয়েছে যাতে সেগুলি অন্যান্য ফ্রেমওয়ার্ক বা লাইব্রেরির সাথে একত্রিত করা যায়। উদাহরণস্বরূপ, যদি আপনার একক-পৃষ্ঠা অ্যাপ্লিকেশন (SPA) একটি রাউটার ব্যবহার করে, আপনি একটি ভিউ ট্রানজিশন ব্যবহার করে বিষয়বস্তু আপডেট করতে রাউটারের আপডেট প্রক্রিয়া সামঞ্জস্য করতে পারেন।
এই পেজিনেশন ডেমো থেকে নেওয়া নিম্নলিখিত কোড স্নিপেটে ন্যাভিগেশন API-এর ইন্টারসেপশন হ্যান্ডলারকে document.startViewTransition
startViewTransition কলে সামঞ্জস্য করা হয় যখন ভিউ ট্রানজিশন সমর্থিত হয়।
navigation.addEventListener("navigate", (e) => {
// Don't intercept if not needed
if (shouldNotIntercept(e)) return;
// Intercept the navigation
e.intercept({
handler: async () => {
// Fetch the new content
const newContent = await fetchNewContent(e.destination.url, {
signal: e.signal,
});
// The UA does not support View Transitions, or the UA
// already provided a Visual Transition by itself (e.g. swipe back).
// In either case, update the DOM directly
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
// Update the content using a View Transition
const t = document.startViewTransition(() => {
setContent(newContent);
});
}
});
});
কিছু, কিন্তু সব নয়, যখন ব্যবহারকারী নেভিগেট করার জন্য একটি সোয়াইপ অঙ্গভঙ্গি সম্পাদন করে তখন ব্রাউজার তাদের নিজস্ব ট্রানজিশন প্রদান করে। সেই ক্ষেত্রে আপনার নিজের ভিউ ট্রানজিশন ট্রিগার করা উচিত নয় কারণ এটি একটি দুর্বল বা বিভ্রান্তিকর ব্যবহারকারীর অভিজ্ঞতার দিকে নিয়ে যাবে। ব্যবহারকারী দুটি ট্রানজিশন দেখতে পাবেন—একটি ব্রাউজার দ্বারা প্রদত্ত এবং অন্যটি আপনার দ্বারা- পরপর চলছে।
তাই, যখন ব্রাউজারটি তার নিজস্ব ভিজ্যুয়াল ট্রানজিশন প্রদান করে তখন একটি ভিউ ট্রানজিশন শুরু হতে বাধা দেওয়ার পরামর্শ দেওয়া হয়। এটি অর্জন করতে, NavigateEvent
উদাহরণের hasUAVisualTransition
বৈশিষ্ট্যের মান পরীক্ষা করুন। যখন ব্রাউজার একটি ভিজ্যুয়াল ট্রানজিশন প্রদান করে তখন প্রপার্টিটি true
হিসেবে সেট করা হয়। এই hasUIVisualTransition
সম্পত্তি PopStateEvent
দৃষ্টান্তেও বিদ্যমান।
পূর্ববর্তী স্নিপেটে যে চেকটি নির্ধারণ করে যে ভিউ ট্রানজিশন চালানো হবে কিনা তা এই সম্পত্তিটিকে বিবেচনা করে। যখন একই-ডকুমেন্ট ভিউ ট্রানজিশনের জন্য কোন সমর্থন না থাকে বা যখন ব্রাউজার ইতিমধ্যেই নিজস্ব ট্রানজিশন প্রদান করে, তখন ভিউ ট্রানজিশন এড়িয়ে যায়।
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
নিম্নলিখিত রেকর্ডিংয়ে, ব্যবহারকারী পূর্ববর্তী পৃষ্ঠায় ফিরে যেতে সোয়াইপ করে। বাম দিকে ক্যাপচার hasUAVisualTransition
পতাকার জন্য একটি চেক অন্তর্ভুক্ত করে না। ডানদিকে রেকর্ডিং চেক অন্তর্ভুক্ত করে, যার ফলে ম্যানুয়াল ভিউ ট্রানজিশন এড়িয়ে যায় কারণ ব্রাউজার একটি ভিজ্যুয়াল ট্রানজিশন প্রদান করে।
জাভাস্ক্রিপ্ট দিয়ে অ্যানিমেটিং
এখন পর্যন্ত, সমস্ত রূপান্তর CSS ব্যবহার করে সংজ্ঞায়িত করা হয়েছে, কিন্তু কখনও কখনও CSS যথেষ্ট নয়:
এই পরিবর্তনের কয়েকটি অংশ একা সিএসএস দিয়ে অর্জন করা যাবে না:
- অ্যানিমেশন ক্লিক অবস্থান থেকে শুরু হয়.
- অ্যানিমেশনটি বৃত্তের দূরতম কোণে একটি ব্যাসার্ধের সাথে শেষ হয়। যদিও, আশা করি এটি ভবিষ্যতে CSS দিয়ে সম্ভব হবে।
সৌভাগ্যক্রমে, আপনি ওয়েব অ্যানিমেশন API ব্যবহার করে রূপান্তর তৈরি করতে পারেন!
let lastClick;
addEventListener('click', event => (lastClick = event));
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// Get the click position, or fallback to the middle of the screen
const x = lastClick?.clientX ?? innerWidth / 2;
const y = lastClick?.clientY ?? innerHeight / 2;
// Get the distance to the furthest corner
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);
// With a transition:
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
});
// Wait for the pseudo-elements to be created:
transition.ready.then(() => {
// Animate the root's new view
document.documentElement.animate(
{
clipPath: [
`circle(0 at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
],
},
{
duration: 500,
easing: 'ease-in',
// Specify which pseudo-element to animate
pseudoElement: '::view-transition-new(root)',
}
);
});
}
এই উদাহরণটি transition.ready
ব্যবহার করে, একটি প্রতিশ্রুতি যা ট্রানজিশন ছদ্ম-উপাদান সফলভাবে তৈরি হয়ে গেলে সমাধান করে। এই বস্তুর অন্যান্য বৈশিষ্ট্য API রেফারেন্সে আচ্ছাদিত করা হয়েছে।
একটি বর্ধন হিসাবে রূপান্তর
ভিউ ট্রানজিশন API একটি DOM পরিবর্তনকে 'র্যাপ' করার জন্য এবং এটির জন্য একটি রূপান্তর তৈরি করার জন্য ডিজাইন করা হয়েছে। যাইহোক, রূপান্তরটিকে একটি বর্ধিতকরণ হিসাবে বিবেচনা করা উচিত, যেমন, DOM পরিবর্তন সফল হলে আপনার অ্যাপটি 'ত্রুটি' অবস্থায় প্রবেশ করা উচিত নয়, কিন্তু পরিবর্তন ব্যর্থ হয়। আদর্শভাবে রূপান্তরটি ব্যর্থ হওয়া উচিত নয়, তবে যদি এটি হয়ে থাকে, তবে এটি ব্যবহারকারীর বাকি অভিজ্ঞতাকে ভাঙবে না।
ট্রানজিশনকে একটি বর্ধিতকরণ হিসাবে বিবেচনা করার জন্য, ট্রানজিশনের প্রতিশ্রুতিগুলি এমনভাবে ব্যবহার না করার বিষয়ে যত্ন নিন যাতে আপনার অ্যাপটি ট্রানজিশন ব্যর্থ হলে নিক্ষেপ করতে পারে।
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); }
এই উদাহরণের সমস্যা হল যে পরিবর্তনটি একটি ready
অবস্থায় পৌঁছাতে না পারলে switchView()
প্রত্যাখ্যান করবে, কিন্তু এর মানে এই নয় যে ভিউটি স্যুইচ করতে ব্যর্থ হয়েছে। DOM সফলভাবে আপডেট হতে পারে, কিন্তু সেখানে ডুপ্লিকেট view-transition-name
ছিল, তাই ট্রানজিশনটি এড়িয়ে গেছে।
পরিবর্তে:
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); animateFromMiddle(transition); await transition.updateCallbackDone; } async function animateFromMiddle(transition) { try { await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); } catch (err) { // You might want to log this error, but it shouldn't break the app } }
এই উদাহরণটি DOM আপডেটের জন্য অপেক্ষা করতে transition.updateCallbackDone
ব্যবহার করে এবং এটি ব্যর্থ হলে প্রত্যাখ্যান করতে। রূপান্তর ব্যর্থ হলে switchView
আর প্রত্যাখ্যান করে না, DOM আপডেট সম্পূর্ণ হলে এটি সমাধান করে এবং ব্যর্থ হলে প্রত্যাখ্যান করে।
আপনি যদি চান যে switchView
নতুন ভিউ 'সেটেল' হয়ে গেলে সমাধান করতে, যেমন যেকোন অ্যানিমেটেড ট্রানজিশন শেষ হয়ে গেছে বা শেষ পর্যন্ত এড়িয়ে গেছে, transition.updateCallbackDone
transition.finished
দিয়ে প্রতিস্থাপন করুন।
পলিফিল নয়, তবে…
এটি পলিফিল করার জন্য একটি সহজ বৈশিষ্ট্য নয়। যাইহোক, এই সহায়ক ফাংশনটি এমন ব্রাউজারগুলিতে অনেক সহজ করে তোলে যা ভিউ ট্রানজিশন সমর্থন করে না:
function transitionHelper({
skipTransition = false,
types = [],
update,
}) {
const unsupported = (error) => {
const updateCallbackDone = Promise.resolve(update()).then(() => {});
return {
ready: Promise.reject(Error(error)),
updateCallbackDone,
finished: updateCallbackDone,
skipTransition: () => {},
types,
};
}
if (skipTransition || !document.startViewTransition) {
return unsupported('View Transitions are not supported in this browser');
}
try {
const transition = document.startViewTransition({
update,
types,
});
return transition;
} catch (e) {
return unsupported('View Transitions with types are not supported in this browser');
}
}
এবং এটি এই মত ব্যবহার করা যেতে পারে:
function spaNavigate(data) {
const types = isBackNavigation ? ['back-transition'] : [];
const transition = transitionHelper({
update() {
updateTheDOMSomehow(data);
},
types,
});
// …
}
যেসব ব্রাউজারে ভিউ ট্রানজিশন সমর্থন করে না, updateDOM
এখনও কল করা হবে, কিন্তু অ্যানিমেটেড ট্রানজিশন হবে না।
ট্রানজিশনের সময় <html>
এ যোগ করার জন্য আপনি কিছু classNames
প্রদান করতে পারেন, যা নেভিগেশনের ধরনের উপর নির্ভর করে ট্রানজিশন পরিবর্তন করা সহজ করে তোলে।
আপনি যদি কোনো অ্যানিমেশন না চান, এমন কি ভিউ ট্রানজিশন সমর্থন করে এমন ব্রাউজারেও আপনি skipTransition
true
পাস করতে পারেন। ট্রানজিশন অক্ষম করার জন্য আপনার সাইটে ব্যবহারকারীর পছন্দ থাকলে এটি কার্যকর।
ফ্রেমওয়ার্ক নিয়ে কাজ করা
আপনি যদি এমন একটি লাইব্রেরি বা ফ্রেমওয়ার্ক নিয়ে কাজ করেন যা DOM পরিবর্তনগুলিকে বিমূর্ত করে দেয়, তবে কঠিন অংশটি হল DOM পরিবর্তন সম্পূর্ণ হলে তা জানা। এখানে বিভিন্ন ফ্রেমওয়ার্কে, উপরে সাহায্যকারী ব্যবহার করে উদাহরণের একটি সেট রয়েছে।
- প্রতিক্রিয়া — এখানে কী হল
flushSync
, যা সিঙ্ক্রোনাসভাবে স্টেট পরিবর্তনের একটি সেট প্রয়োগ করে। হ্যাঁ, সেই API ব্যবহার করার বিষয়ে একটি বড় সতর্কতা রয়েছে, কিন্তু ড্যান আব্রামভ আমাকে আশ্বস্ত করেছেন যে এটি এই ক্ষেত্রে উপযুক্ত। যথারীতি প্রতিক্রিয়া এবং অ্যাসিঙ্ক কোডের সাথে,startViewTransition
দ্বারা প্রত্যাবর্তিত বিভিন্ন প্রতিশ্রুতি ব্যবহার করার সময়, আপনার কোডটি সঠিক অবস্থার সাথে চলছে কিনা সেদিকে খেয়াল রাখুন। - Vue.js —এখানে কী হল
nextTick
, যা একবার DOM আপডেট হয়ে গেলে পূরণ করে। - Svelte — Vue-এর মতোই, কিন্তু পরবর্তী পরিবর্তনের জন্য অপেক্ষা করার পদ্ধতি হল
tick
। - লিট —এখানে মূল বিষয় হল উপাদানগুলির মধ্যে
this.updateComplete
প্রতিশ্রুতি, যা একবার DOM আপডেট করার পরে পূরণ করে। - কৌণিক — এখানে কী হল
applicationRef.tick
, যা মুলতুবি থাকা DOM পরিবর্তনগুলিকে ফ্লাশ করে। কৌণিক সংস্করণ 17 হিসাবে আপনিwithViewTransitions
ব্যবহার করতে পারেন যা@angular/router
এর সাথে আসে ।
API রেফারেন্স
-
const viewTransition = document.startViewTransition(update)
একটি নতুন
ViewTransition
শুরু করুন।update
হল একটি ফাংশন যা একবার ডকুমেন্টের বর্তমান অবস্থা ক্যাপচার করা হলে বলা হয়।তারপর, যখন
updateCallback
দ্বারা প্রত্যাবর্তিত প্রতিশ্রুতি পূরণ হয়, তখন পরবর্তী ফ্রেমে রূপান্তর শুরু হয়।updateCallback
দ্বারা প্রত্যাবর্তিত প্রতিশ্রুতি প্রত্যাখ্যান করলে, রূপান্তর পরিত্যক্ত হয়।-
const viewTransition = document.startViewTransition({ update, types })
নির্দিষ্ট প্রকারের সাথে একটি নতুন
ViewTransition
শুরু করুননথির বর্তমান অবস্থা ক্যাপচার করা হলে
update
বলা হয়।রূপান্তর ক্যাপচার বা সঞ্চালন করার সময়
types
পরিবর্তনের জন্য সক্রিয় প্রকারগুলি সেট করে। এটি প্রাথমিকভাবে খালি। আরও তথ্যের জন্য আরও নীচেviewTransition.types
দেখুন।
ViewTransition
ট্রানজিশনের সদস্যদের উদাহরণ:
-
viewTransition.updateCallbackDone
একটি প্রতিশ্রুতি যা পরিপূর্ণ হয় যখন
updateCallback
দ্বারা প্রত্যাবর্তিত প্রতিশ্রুতি পূর্ণ হয়, বা প্রত্যাখ্যান করলে তা প্রত্যাখ্যান করে।ভিউ ট্রানজিশন এপিআই একটি DOM পরিবর্তন মোড়ানো এবং একটি ট্রানজিশন তৈরি করে। যাইহোক, কখনও কখনও আপনি ট্রানজিশন অ্যানিমেশনের সাফল্য বা ব্যর্থতার বিষয়ে চিন্তা করেন না, আপনি শুধু জানতে চান যে DOM পরিবর্তন কখন ঘটবে।
updateCallbackDone
সেই ব্যবহারের ক্ষেত্রে।-
viewTransition.ready
একটি প্রতিশ্রুতি যা পূর্ণ হয় একবার রূপান্তরের জন্য ছদ্ম-উপাদানগুলি তৈরি হয়ে গেলে এবং অ্যানিমেশন শুরু হতে চলেছে৷
রূপান্তর শুরু করতে না পারলে এটি প্রত্যাখ্যান করে। এটি ভুল কনফিগারেশনের কারণে হতে পারে, যেমন ডুপ্লিকেট
view-transition-name
s, অথবা যদিupdateCallback
একটি প্রত্যাখ্যাত প্রতিশ্রুতি প্রদান করে।এটি জাভাস্ক্রিপ্টের সাথে ট্রানজিশন সিউডো-এলিমেন্ট অ্যানিমেট করার জন্য দরকারী।
-
viewTransition.finished
একটি প্রতিশ্রুতি যা ব্যবহারকারীর কাছে শেষ অবস্থা সম্পূর্ণরূপে দৃশ্যমান এবং ইন্টারেক্টিভ হওয়ার পরে পূরণ করে।
এটি শুধুমাত্র প্রত্যাখ্যান করে যদি
updateCallback
একটি প্রত্যাখ্যাত প্রতিশ্রুতি প্রদান করে, কারণ এটি ইঙ্গিত করে যে শেষ অবস্থা তৈরি করা হয়নি।অন্যথায়, যদি একটি ট্রানজিশন শুরু হতে ব্যর্থ হয়, বা ট্রানজিশনের সময় এড়িয়ে যাওয়া হয়, তাহলেও শেষ অবস্থায় পৌঁছে যায়, তাই
finished
হয়।-
viewTransition.types
একটি
Set
-সদৃশ বস্তু যা সক্রিয় ভিউ ট্রানজিশনের ধরন ধারণ করে। এন্ট্রি ম্যানিপুলেট করতে, এর ইনস্ট্যান্স পদ্ধতি ব্যবহার করুনclear()
,add()
, এবংdelete()
।CSS-এ একটি নির্দিষ্ট ধরনের প্রতিক্রিয়া জানাতে, ট্রানজিশন রুটে
:active-view-transition-type(type)
pseudo-class সিলেক্টর ব্যবহার করুন।ভিউ ট্রানজিশন শেষ হলে প্রকারগুলি স্বয়ংক্রিয়ভাবে পরিষ্কার হয়ে যায়।
-
viewTransition.skipTransition()
ট্রানজিশনের অ্যানিমেশন অংশটি এড়িয়ে যান।
এটি
updateCallback
কলিং এড়িয়ে যাবে না, কারণ DOM পরিবর্তনটি ট্রানজিশনের জন্য আলাদা।
ডিফল্ট শৈলী এবং ট্রানজিশন রেফারেন্স
-
::view-transition
- রুট ছদ্ম-উপাদান যা ভিউপোর্ট পূরণ করে এবং প্রতিটি
::view-transition-group
ধারণ করে। -
::view-transition-group
একেবারে অবস্থান.
'আগে' এবং 'পরে' অবস্থার মধ্যে
width
এবংheight
পরিবর্তন করে।রূপান্তরগুলি 'আগে' এবং 'পরে' ভিউপোর্ট-স্পেস কোয়াডের মধ্যে
transform
।-
::view-transition-image-pair
গ্রুপ পূরণ করার জন্য একেবারে অবস্থান.
isolation: isolate
পুরানো এবং নতুন ভিউতেmix-blend-mode
প্রভাব সীমিত করতে আইসোলেট করুন।-
::view-transition-new
এবং::view-transition-old
একেবারে র্যাপারের উপরের-বামে অবস্থান।
গোষ্ঠীর প্রস্থের 100% পূরণ করে, কিন্তু একটি স্বয়ংক্রিয় উচ্চতা রয়েছে, তাই এটি গোষ্ঠীটি পূরণ করার পরিবর্তে তার আকৃতির অনুপাত বজায় রাখবে।
mix-blend-mode: plus-lighter
।পুরানো ভিউ
opacity: 1
opacity: 0
তে। নতুন ভিউopacity: 0
থেকেopacity: 1
।
প্রতিক্রিয়া
বিকাশকারী প্রতিক্রিয়া সবসময় প্রশংসা করা হয়. এটি করার জন্য, পরামর্শ এবং প্রশ্ন সহ GitHub-এ CSS ওয়ার্কিং গ্রুপের সাথে একটি সমস্যা ফাইল করুন । [css-view-transitions]
এর সাথে আপনার সমস্যাটি উপসর্গ করুন।
আপনি একটি বাগ চালাতে হবে, তারপর পরিবর্তে একটি Chromium বাগ ফাইল করুন .
,প্রকাশিত: আগস্ট 17, 2021, শেষ আপডেট: সেপ্টেম্বর 25, 2024
যখন একটি ভিউ ট্রানজিশন একটি একক নথিতে চলে তখন একে একই-ডকুমেন্ট ভিউ ট্রানজিশন বলে। এটি সাধারণত একক-পৃষ্ঠা অ্যাপ্লিকেশন (এসপিএ) এর ক্ষেত্রে হয় যেখানে জাভাস্ক্রিপ্ট DOM আপডেট করতে ব্যবহৃত হয়। একই-ডকুমেন্ট ভিউ ট্রানজিশনগুলি Chrome 111-এর মতো Chrome-এ সমর্থিত।
একই-ডকুমেন্ট ভিউ ট্রানজিশন ট্রিগার করতে, document.startViewTransition
কল করুন:
function handleClick(e) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow();
return;
}
// With a View Transition:
document.startViewTransition(() => updateTheDOMSomehow());
}
যখন আহ্বান করা হয়, ব্রাউজার স্বয়ংক্রিয়ভাবে সমস্ত উপাদানের স্ন্যাপশট ক্যাপচার করে যেগুলির উপর একটি view-transition-name
CSS সম্পত্তি রয়েছে।
এটি তারপরে পাস করা কলব্যাকে কার্যকর করে যা DOM আপডেট করে, তারপরে এটি নতুন অবস্থার স্ন্যাপশট নেয়।
এই স্ন্যাপশটগুলিকে সিউডো-এলিমেন্টের গাছে সাজানো হয় এবং CSS অ্যানিমেশনের শক্তি ব্যবহার করে অ্যানিমেটেড করা হয়। পুরানো এবং নতুন অবস্থা থেকে স্ন্যাপশটগুলির জোড়া তাদের পুরানো অবস্থান এবং আকার থেকে তাদের নতুন অবস্থানে মসৃণভাবে স্থানান্তরিত হয়, যখন তাদের বিষয়বস্তু ক্রসফেড হয়। আপনি চাইলে অ্যানিমেশন কাস্টমাইজ করতে CSS ব্যবহার করতে পারেন।
ডিফল্ট রূপান্তর: ক্রস-ফেইড
ডিফল্ট ভিউ ট্রানজিশন একটি ক্রস-ফেড, তাই এটি API-এর একটি সুন্দর ভূমিকা হিসেবে কাজ করে:
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// With a transition:
document.startViewTransition(() => updateTheDOMSomehow(data));
}
যেখানে updateTheDOMSomehow
DOM কে নতুন অবস্থায় পরিবর্তন করে। যেটা আপনি চাইলেই করা যেতে পারে। উদাহরণস্বরূপ, আপনি উপাদান যোগ করতে বা সরাতে পারেন, শ্রেণির নাম পরিবর্তন করতে পারেন বা শৈলী পরিবর্তন করতে পারেন।
এবং ঠিক সেই মত, পৃষ্ঠাগুলি ক্রস-ফেড:
ঠিক আছে, একটি ক্রস-ফেড যে চিত্তাকর্ষক নয়। সৌভাগ্যক্রমে, রূপান্তরগুলি কাস্টমাইজ করা যেতে পারে, তবে প্রথমে, আপনাকে বুঝতে হবে কিভাবে এই মৌলিক ক্রস-ফেড কাজ করে।
কিভাবে এই রূপান্তর কাজ
আগের কোড নমুনা আপডেট করা যাক.
document.startViewTransition(() => updateTheDOMSomehow(data));
যখন .startViewTransition()
কল করা হয়, API পৃষ্ঠার বর্তমান অবস্থা ক্যাপচার করে। এর মধ্যে একটি স্ন্যাপশট নেওয়া অন্তর্ভুক্ত।
একবার সম্পূর্ণ হলে, .startViewTransition()
এ পাস করা কলব্যাক বলা হয়। যে যেখানে DOM পরিবর্তন করা হয়. তারপর, API পৃষ্ঠার নতুন অবস্থা ক্যাপচার করে।
একবার নতুন অবস্থা ক্যাপচার করা হলে, API এই মত একটি ছদ্ম-উপাদান গাছ তৈরি করে:
::view-transition
└─ ::view-transition-group(root)
└─ ::view-transition-image-pair(root)
├─ ::view-transition-old(root)
└─ ::view-transition-new(root)
::view-transition
পৃষ্ঠার অন্য সব কিছুর উপরে একটি ওভারলেতে বসে। আপনি যদি রূপান্তরের জন্য একটি পটভূমির রঙ সেট করতে চান তবে এটি কার্যকর।
::view-transition-old(root)
হল পুরানো ভিউ এর একটি স্ক্রিনশট, এবং ::view-transition-new(root)
হল নতুন ভিউ এর একটি লাইভ উপস্থাপনা৷ উভয়ই CSS 'প্রতিস্থাপিত সামগ্রী' হিসাবে রেন্ডার করে (যেমন একটি <img>
)।
পুরানো ভিউ opacity: 1
থেকে opacity: 0
, যখন নতুন ভিউ opacity: 0
থেকে opacity: 1
, একটি ক্রস-ফেড তৈরি করে।
সমস্ত অ্যানিমেশন সিএসএস অ্যানিমেশন ব্যবহার করে সঞ্চালিত হয়, তাই সেগুলি সিএসএস দিয়ে কাস্টমাইজ করা যায়।
রূপান্তর কাস্টমাইজ করুন
সমস্ত ভিউ ট্রানজিশন সিউডো-এলিমেন্ট CSS দিয়ে টার্গেট করা যেতে পারে এবং যেহেতু অ্যানিমেশনগুলি CSS ব্যবহার করে সংজ্ঞায়িত করা হয়েছে, আপনি বিদ্যমান CSS অ্যানিমেশন বৈশিষ্ট্যগুলি ব্যবহার করে সেগুলি পরিবর্তন করতে পারেন। যেমন:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 5s;
}
সেই একটি পরিবর্তনের সাথে, বিবর্ণ এখন সত্যিই ধীর:
ঠিক আছে, এটি এখনও চিত্তাকর্ষক নয়। পরিবর্তে, নিম্নলিখিত কোডটি উপাদান ডিজাইনের ভাগ করা অক্ষ রূপান্তর প্রয়োগ করে:
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(30px); }
}
@keyframes slide-to-left {
to { transform: translateX(-30px); }
}
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
এবং এখানে ফলাফল:
একাধিক উপাদান রূপান্তর
পূর্ববর্তী ডেমোতে, পুরো পৃষ্ঠাটি ভাগ করা অক্ষের রূপান্তরটিতে জড়িত। এটি বেশিরভাগ পৃষ্ঠার জন্য কাজ করে তবে এটি শিরোনামগুলির পক্ষে একেবারেই ঠিক মনে হয় না, কারণ এটি কেবল আবার পিছলে যেতে স্লাইড হয়ে যায়।
এটি এড়াতে, আপনি বাকী পৃষ্ঠাটি থেকে শিরোনামটি বের করতে পারেন যাতে এটি আলাদাভাবে অ্যানিমেটেড করা যায়। এটি উপাদানটির জন্য একটি view-transition-name
নির্ধারণ করে করা হয়।
.main-header {
view-transition-name: main-header;
}
view-transition-name
মান আপনি যা চান তা হতে পারে ( none
ব্যতীত, যার অর্থ কোনও রূপান্তর নাম নেই)। এটি রূপান্তর জুড়ে উপাদানটি অনন্যভাবে সনাক্ত করতে ব্যবহৃত হয়।
এবং এর ফলাফল:
এখন শিরোনামটি জায়গায় এবং ক্রস-ফেডে থাকে।
এই সিএসএস ঘোষণার ফলে সিউডো-এলিমেন্ট ট্রি পরিবর্তন হয়েছিল:
::view-transition
├─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
└─ ::view-transition-group(main-header)
└─ ::view-transition-image-pair(main-header)
├─ ::view-transition-old(main-header)
└─ ::view-transition-new(main-header)
এখন দুটি ট্রানজিশন গ্রুপ রয়েছে। একটি শিরোনামের জন্য, এবং অন্যটি বাকি জন্য। এগুলি সিএসএস দিয়ে স্বাধীনভাবে লক্ষ্য করা যায় এবং বিভিন্ন রূপান্তর দেওয়া যেতে পারে। যদিও, এক্ষেত্রে main-header
ডিফল্ট ট্রানজিশনের সাথে রেখে দেওয়া হয়েছিল, যা ক্রস-ফেড।
ঠিক আছে, ঠিক আছে, ডিফল্ট ট্রানজিশনটি কেবল একটি ক্রস বিবর্ণ নয়, ::view-transition-group
ট্রানজিশন:
- অবস্থান এবং রূপান্তর (একটি
transform
ব্যবহার করে) - প্রস্থ
- উচ্চতা
এটি এখনও অবধি গুরুত্বপূর্ণ হয়নি, কারণ শিরোনামটি একই আকার এবং ডিওএম পরিবর্তনের উভয় পক্ষের অবস্থান। তবে আপনি শিরোনামে পাঠ্যটিও বের করতে পারেন:
.main-header-text {
view-transition-name: main-header-text;
width: fit-content;
}
fit-content
ব্যবহৃত হয় তাই উপাদানটি বাকী প্রস্থ পর্যন্ত প্রসারিত না করে পাঠ্যের আকার। এটি ব্যতীত, পিছনের তীরটি উভয় পৃষ্ঠায় একই আকারের চেয়ে শিরোনাম পাঠ্য উপাদানটির আকার হ্রাস করে।
সুতরাং এখন আমাদের সাথে খেলতে তিনটি অংশ রয়েছে:
::view-transition
├─ ::view-transition-group(root)
│ └─ …
├─ ::view-transition-group(main-header)
│ └─ …
└─ ::view-transition-group(main-header-text)
└─ …
তবে আবার, কেবল ডিফল্টগুলির সাথে চলছে:
এখন শিরোনাম পাঠ্যটি ব্যাক বোতামের জন্য স্থান তৈরি করতে কিছুটা সন্তোষজনক স্লাইড করে।
view-transition-class
সহ একইভাবে একাধিক সিউডো-উপাদানগুলি অ্যানিমেট করুন
ব্রাউজার সমর্থন
বলুন আপনার একগুচ্ছ কার্ডের সাথে একটি ভিউ ট্রানজিশন রয়েছে তবে পৃষ্ঠায় একটি শিরোনামও রয়েছে। শিরোনাম ব্যতীত সমস্ত কার্ড অ্যানিমেট করতে, আপনাকে এমন একটি নির্বাচক লিখতে হবে যা প্রতিটি পৃথক কার্ডকে লক্ষ্য করে।
h1 {
view-transition-name: title;
}
::view-transition-group(title) {
animation-timing-function: ease-in-out;
}
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
…
#card20 { view-transition-name: card20; }
::view-transition-group(card1),
::view-transition-group(card2),
::view-transition-group(card3),
::view-transition-group(card4),
…
::view-transition-group(card20) {
animation-timing-function: var(--bounce);
}
20 টি উপাদান পেয়েছেন? এটি আপনার 20 জন নির্বাচক লিখতে হবে। একটি নতুন উপাদান যোগ করা হচ্ছে? তারপরে আপনাকে অ্যানিমেশন শৈলী প্রয়োগ করে এমন নির্বাচকও বাড়ানো দরকার। ঠিক স্কেলযোগ্য নয়।
view-transition-class
একই স্টাইলের নিয়ম প্রয়োগ করতে ভিউ ট্রানজিশন সিউডো-উপাদানগুলিতে ব্যবহার করা যেতে পারে।
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
#card5 { view-transition-name: card5; }
…
#card20 { view-transition-name: card20; }
#cards-wrapper > div {
view-transition-class: card;
}
html::view-transition-group(.card) {
animation-timing-function: var(--bounce);
}
নিম্নলিখিত কার্ডগুলির উদাহরণ পূর্ববর্তী সিএসএস স্নিপেটকে উপার্জন করে। সমস্ত কার্ড-সদ্য যুক্ত হওয়াগুলি সহ-একই সময়টি একজন নির্বাচকের সাথে প্রয়োগ করুন: html::view-transition-group(.card)
।
ডিবাগ ট্রানজিশন
যেহেতু ভিউ ট্রানজিশনগুলি সিএসএস অ্যানিমেশনগুলির শীর্ষে নির্মিত হয়েছে, তাই ক্রোম ডেভটুলগুলিতে অ্যানিমেশন প্যানেলটি ডিবাগিং ট্রানজিশনের জন্য দুর্দান্ত।
অ্যানিমেশন প্যানেল ব্যবহার করে, আপনি পরবর্তী অ্যানিমেশনটি বিরতি দিতে পারেন, তারপরে অ্যানিমেশনটির মাধ্যমে পিছনে পিছনে স্ক্রাব করতে পারেন। এর সময়, রূপান্তর সিউডো-উপাদানগুলি উপাদান প্যানেলে পাওয়া যাবে।
রূপান্তরকারী উপাদানগুলির একই ডোম উপাদান হওয়ার দরকার নেই
এখনও অবধি আমরা শিরোনামের জন্য পৃথক রূপান্তর উপাদান এবং শিরোনামে পাঠ্য তৈরি করতে view-transition-name
ব্যবহার করেছি। এগুলি ডিওএম পরিবর্তনের আগে এবং পরে একই উপাদান হিসাবে একই উপাদান, তবে আপনি এমন ট্রানজিশন তৈরি করতে পারেন যেখানে এটি নয়।
উদাহরণস্বরূপ, মূল ভিডিও এম্বেডকে একটি view-transition-name
দেওয়া যেতে পারে:
.full-embed {
view-transition-name: full-embed;
}
তারপরে, যখন থাম্বনেইলটি ক্লিক করা হয়, তখন কেবল পরিবর্তনের সময়কালের জন্য এটি একই view-transition-name
দেওয়া যেতে পারে:
thumbnail.onclick = async () => {
thumbnail.style.viewTransitionName = 'full-embed';
document.startViewTransition(() => {
thumbnail.style.viewTransitionName = '';
updateTheDOMSomehow();
});
};
এবং ফলাফল:
থাম্বনেইল এখন মূল চিত্রটিতে রূপান্তরিত হয়। যদিও তারা ধারণাগতভাবে (এবং আক্ষরিক) বিভিন্ন উপাদান, ট্রানজিশন এপিআই তাদেরকে একই জিনিস হিসাবে বিবেচনা করে কারণ তারা একই view-transition-name
ভাগ করে নিয়েছে।
এই রূপান্তরের জন্য আসল কোডটি পূর্ববর্তী উদাহরণের চেয়ে কিছুটা জটিল, কারণ এটি থাম্বনেইল পৃষ্ঠায় রূপান্তরটিও পরিচালনা করে। সম্পূর্ণ বাস্তবায়নের জন্য উত্স দেখুন ।
কাস্টম এন্ট্রি এবং প্রস্থান ট্রানজিশন
এই উদাহরণ দেখুন:
সাইডবারটি পরিবর্তনের অংশ:
.sidebar {
view-transition-name: sidebar;
}
তবে, পূর্ববর্তী উদাহরণে শিরোনামের বিপরীতে, সাইডবারটি সমস্ত পৃষ্ঠায় উপস্থিত হয় না। উভয় রাজ্যের যদি সাইডবার থাকে তবে ট্রানজিশন সিউডো-উপাদানগুলি এর মতো দেখায়:
::view-transition
├─ …other transition groups…
└─ ::view-transition-group(sidebar)
└─ ::view-transition-image-pair(sidebar)
├─ ::view-transition-old(sidebar)
└─ ::view-transition-new(sidebar)
তবে, যদি সাইডবারটি কেবল নতুন পৃষ্ঠায় থাকে তবে ::view-transition-old(sidebar)
সিউডো-এলিমেন্ট থাকবে না। যেহেতু সাইডবারের জন্য কোনও 'পুরাতন' চিত্র নেই, তাই চিত্র-জুটিতে কেবল একটি ::view-transition-new(sidebar)
থাকবে। একইভাবে, যদি সাইডবারটি কেবল পুরানো পৃষ্ঠায় থাকে তবে চিত্র-জুটিতে কেবল একটি ::view-transition-old(sidebar)
থাকবে।
পূর্ববর্তী ডেমোতে, সাইডবারটি উভয় রাজ্যে প্রবেশ, প্রস্থান বা উপস্থিত কিনা তার উপর নির্ভর করে আলাদাভাবে রূপান্তর করে। এটি ডান দিক থেকে স্লাইড করে এবং ম্লান হয়ে প্রবেশ করে, এটি ডানদিকে স্লাইড করে এবং বিবর্ণ হয়ে প্রস্থান করে এবং এটি উভয় রাজ্যে উপস্থিত থাকলে এটি স্থানে থাকে।
নির্দিষ্ট এন্ট্রি এবং প্রস্থান ট্রানজিশন তৈরি করতে, আপনি যখন চিত্র-জুটির একমাত্র শিশু হয় তখন পুরানো বা নতুন সিউডো-উপাদানগুলি লক্ষ্য করতে আপনি :only-child
সিউডো-ক্লাসটি ব্যবহার করতে পারেন:
/* Entry transition */
::view-transition-new(sidebar):only-child {
animation: 300ms cubic-bezier(0, 0, 0.2, 1) both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Exit transition */
::view-transition-old(sidebar):only-child {
animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
}
এই ক্ষেত্রে, ডিফল্টটি নিখুঁত যেহেতু উভয় রাজ্যে সাইডবার উপস্থিত থাকে তার জন্য কোনও নির্দিষ্ট রূপান্তর নেই।
অ্যাসিঙ্ক ডোম আপডেটগুলি, এবং সামগ্রীর জন্য অপেক্ষা করছে
কলব্যাকটি .startViewTransition()
document.startViewTransition(async () => {
await something;
await updateTheDOMSomehow();
await somethingElse;
});
প্রতিশ্রুতি পূরণ না হওয়া পর্যন্ত রূপান্তর শুরু হবে না। এই সময়ের মধ্যে, পৃষ্ঠাটি হিমশীতল, সুতরাং এখানে বিলম্বগুলি সর্বনিম্ন রাখা উচিত। বিশেষত, কল করার .startViewTransition()
নেটওয়ার্ক ফেচগুলি করা উচিত .startViewTransition()
আপনি যদি চিত্র বা ফন্টগুলি প্রস্তুত হওয়ার জন্য অপেক্ষা করার সিদ্ধান্ত নেন তবে আক্রমণাত্মক সময়সীমা ব্যবহার করতে ভুলবেন না:
const wait = ms => new Promise(r => setTimeout(r, ms));
document.startViewTransition(async () => {
updateTheDOMSomehow();
// Pause for up to 100ms for fonts to be ready:
await Promise.race([document.fonts.ready, wait(100)]);
});
তবে কিছু ক্ষেত্রে বিলম্ব পুরোপুরি এড়ানো ভাল এবং আপনার ইতিমধ্যে থাকা সামগ্রীটি ব্যবহার করা ভাল।
আপনার ইতিমধ্যে থাকা বেশিরভাগ সামগ্রী তৈরি করুন
যে ক্ষেত্রে থাম্বনেইল একটি বৃহত্তর চিত্রটিতে স্থানান্তরিত হয়:
ডিফল্ট ট্রানজিশনটি ক্রস-ফেডে, যার অর্থ থাম্বনেইলটি এখনও অ-লোডযুক্ত পূর্ণ চিত্রের সাথে ক্রস ফেডিং হতে পারে।
এটি পরিচালনা করার একটি উপায় হ'ল ট্রানজিশন শুরু করার আগে পুরো চিত্রটি লোড হওয়ার জন্য অপেক্ষা করা। আদর্শভাবে এটি কল করার আগে এটি করা হবে .startViewTransition()
তবে এই ক্ষেত্রে আরও ভাল উপায় আছে:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
}
এখন থাম্বনেইলটি ম্লান হয় না, এটি কেবল পুরো চিত্রের নীচে বসে। এর অর্থ যদি নতুন দৃশ্যটি লোড না হয় তবে থাম্বনেইলটি পুরো রূপান্তর জুড়ে দৃশ্যমান। এর অর্থ হ'ল ট্রানজিশনটি সরাসরি শুরু হতে পারে এবং সম্পূর্ণ চিত্রটি তার নিজস্ব সময়ে লোড করতে পারে।
নতুন ভিউতে স্বচ্ছতা বৈশিষ্ট্যযুক্ত হলে এটি কাজ করবে না, তবে এই ক্ষেত্রে আমরা জানি এটি না, তাই আমরা এই অপ্টিমাইজেশন করতে পারি।
দিক অনুপাতের পরিবর্তনগুলি পরিচালনা করুন
সুবিধাজনকভাবে, এখন পর্যন্ত সমস্ত ট্রানজিশনগুলি একই দিক অনুপাতের সাথে উপাদানগুলিতে হয়েছে, তবে এটি সর্বদা ক্ষেত্রে হবে না। যদি থাম্বনেইল 1: 1 হয় এবং মূল চিত্রটি 16: 9 হয়?
ডিফল্ট ট্রানজিশনে, গোষ্ঠীটি আগের আকার থেকে পরে আকারে অ্যানিমেটেড করে। পুরানো এবং নতুন দর্শনগুলি গ্রুপের 100% প্রস্থ এবং অটো উচ্চতা, যার অর্থ তারা গ্রুপের আকার নির্বিশেষে তাদের দিক অনুপাত রাখে।
এটি একটি ভাল ডিফল্ট, তবে এটি এই ক্ষেত্রে যা চেয়েছিল তা নয়। তাই:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
/* Make the height the same as the group,
meaning the view size might not match its aspect-ratio. */
height: 100%;
/* Clip any overflow of the view */
overflow: clip;
}
/* The old view is the thumbnail */
::view-transition-old(full-embed) {
/* Maintain the aspect ratio of the view,
by shrinking it to fit within the bounds of the element */
object-fit: contain;
}
/* The new view is the full image */
::view-transition-new(full-embed) {
/* Maintain the aspect ratio of the view,
by growing it to cover the bounds of the element */
object-fit: cover;
}
এর অর্থ প্রস্থের প্রসারিত হওয়ার সাথে সাথে থাম্বনেইল উপাদানটির কেন্দ্রে থাকে তবে পুরো চিত্রটি 'আন-ফসল' হিসাবে এটি 1: 1 থেকে 16: 9 পর্যন্ত স্থানান্তরিত হয়।
আরও বিশদ তথ্যের জন্য, দেখুন ট্রানজিশনগুলি দেখুন: দিক অনুপাত পরিবর্তনগুলি পরিচালনা করা
বিভিন্ন ডিভাইস রাজ্যের জন্য ট্রানজিশন পরিবর্তন করতে মিডিয়া প্রশ্নগুলি ব্যবহার করুন
আপনি মোবাইল বনাম ডেস্কটপে বিভিন্ন ট্রানজিশন ব্যবহার করতে চাইতে পারেন, যেমন এই উদাহরণ যা মোবাইলের পাশ থেকে একটি সম্পূর্ণ স্লাইড সম্পাদন করে তবে ডেস্কটপে আরও সূক্ষ্ম স্লাইড:
এটি নিয়মিত মিডিয়া প্রশ্নগুলি ব্যবহার করে অর্জন করা যেতে পারে:
/* Transitions for mobile */
::view-transition-old(root) {
animation: 300ms ease-out both full-slide-to-left;
}
::view-transition-new(root) {
animation: 300ms ease-out both full-slide-from-right;
}
@media (min-width: 500px) {
/* Overrides for larger displays.
This is the shared axis transition from earlier in the article. */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
}
আপনি মিডিয়া প্রশ্নের সাথে মিলে কোন উপাদানগুলি একটি view-transition-name
নির্ধারণ করেছেন তাও আপনি পরিবর্তন করতে চাইতে পারেন।
'হ্রাস গতি' পছন্দকে প্রতিক্রিয়া জানান
ব্যবহারকারীরা নির্দেশ করতে পারেন যে তারা তাদের অপারেটিং সিস্টেমের মাধ্যমে হ্রাস গতি পছন্দ করে এবং সেই পছন্দটি সিএসএসে প্রকাশিত হয়।
আপনি এই ব্যবহারকারীদের জন্য কোনও ট্রানজিশন রোধ করতে বেছে নিতে পারেন:
@media (prefers-reduced-motion) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
তবে, 'হ্রাস গতি' এর জন্য একটি অগ্রাধিকারের অর্থ এই নয় যে ব্যবহারকারী কোনও গতি চায় না। পূর্ববর্তী স্নিপেটের পরিবর্তে, আপনি আরও সূক্ষ্ম অ্যানিমেশন চয়ন করতে পারেন, তবে এমন একটি যা এখনও উপাদানগুলির মধ্যে সম্পর্ক এবং ডেটার প্রবাহকে প্রকাশ করে।
ভিউ ট্রানজিশন প্রকারের সাথে একাধিক ভিউ ট্রানজিশন স্টাইলগুলি পরিচালনা করুন
ব্রাউজার সমর্থন
কখনও কখনও একটি নির্দিষ্ট দৃশ্য থেকে অন্যটিতে একটি রূপান্তর করা উচিত একটি নির্দিষ্টভাবে তৈরি ট্রানজিশন। উদাহরণস্বরূপ, যখন কোনও প্যাগিনেশন সিকোয়েন্সে পরবর্তী বা পূর্ববর্তী পৃষ্ঠায় যাওয়ার সময়, আপনি কোনও উচ্চতর পৃষ্ঠায় বা ক্রম থেকে কোনও নিম্ন পৃষ্ঠায় যাচ্ছেন কিনা তার উপর নির্ভর করে আপনি আলাদা দিকে সামগ্রীগুলি স্লাইড করতে চাইতে পারেন।
এর জন্য আপনি ভিউ ট্রানজিশন প্রকারগুলি ব্যবহার করতে পারেন, যা আপনাকে সক্রিয় ভিউ ট্রানজিশনে এক বা একাধিক প্রকারকে বরাদ্দ করতে দেয়। উদাহরণস্বরূপ, যখন কোনও প্যাগিনেশন সিকোয়েন্সে উচ্চতর পৃষ্ঠায় স্থানান্তরিত করা forwards
টাইপটি ব্যবহার করে এবং নিম্ন পৃষ্ঠায় যাওয়ার সময় backwards
ধরণটি ব্যবহার করুন। এই ধরণেরগুলি কেবল তখনই সক্রিয় থাকে যখন কোনও ট্রানজিশন ক্যাপচার বা সম্পাদন করা হয় এবং প্রতিটি ধরণের সিএসএসের মাধ্যমে বিভিন্ন অ্যানিমেশন ব্যবহার করতে কাস্টমাইজ করা যায়।
একই ডকুমেন্ট ভিউ ট্রানজিশনে প্রকারগুলি ব্যবহার করতে, আপনি startViewTransition
পদ্ধতিতে types
পাস করেন। এটির অনুমতি দেওয়ার জন্য, document.startViewTransition
কোনও অবজেক্ট গ্রহণ করে: update
হ'ল কলব্যাক ফাংশন যা ডিওএম আপডেট করে এবং types
প্রকারগুলির সাথে একটি অ্যারে।
const direction = determineBackwardsOrForwards();
const t = document.startViewTransition({
update: updateTheDOMSomehow,
types: ['slide', direction],
});
এই ধরণের প্রতিক্রিয়া জানাতে :active-view-transition-type()
নির্বাচক ব্যবহার করুন। আপনি নির্বাচকের মধ্যে লক্ষ্য করতে চান এমন type
পাস করুন। এটি আপনাকে একাধিক ভিউ ট্রানজিশনের স্টাইলগুলি একে অপরের থেকে পৃথক করে রাখতে দেয়, একে অপরের ঘোষণায় হস্তক্ষেপ না করে।
যেহেতু প্রকারগুলি কেবল রূপান্তরটি ক্যাপচার বা সম্পাদন করার সময় প্রযোজ্য, আপনি কেবলমাত্র সেই ধরণের সাথে ভিউ ট্রানজিশনের জন্য কোনও উপাদানের উপর-বা আনসেট-একটি view-transition-name
সেট করতে নির্বাচককে ব্যবহার করতে পারেন।
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only (using the default root snapshot) */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
নিম্নলিখিত পৃষ্ঠাগুলি ডেমোতে , পৃষ্ঠার সামগ্রীগুলি আপনি যে পৃষ্ঠায় নেভিগেট করছেন তার উপর ভিত্তি করে এগিয়ে বা পিছনের দিকে স্লাইড হয়। প্রকারগুলি ক্লিক করে নির্ধারিত হয় যার উপর তারা document.startViewTransition
পাস করা হয় rast স্টার্টভিউ ট্রান্সিশন।
প্রকার নির্বিশেষে যে কোনও সক্রিয় ভিউ ট্রানজিশনকে লক্ষ্য করতে, আপনি পরিবর্তে :active-view-transition
সিউডো-শ্রেণীর নির্বাচক ব্যবহার করতে পারেন।
html:active-view-transition {
…
}
ভিউ ট্রানজিশন রুটে একটি শ্রেণীর নাম সহ একাধিক ভিউ ট্রানজিশন স্টাইলগুলি পরিচালনা করুন
কখনও কখনও একটি নির্দিষ্ট ধরণের ভিউ থেকে অন্যটিতে রূপান্তর করা উচিত একটি নির্দিষ্টভাবে তৈরি ট্রানজিশন। বা, একটি 'ব্যাক' নেভিগেশন একটি 'ফরোয়ার্ড' নেভিগেশনের চেয়ে আলাদা হওয়া উচিত।
ট্রানজিশন প্রকারের আগে এই কেসগুলি পরিচালনা করার উপায়টি ছিল অস্থায়ীভাবে ট্রানজিশন রুটটিতে একটি শ্রেণীর নাম সেট করা। document.startViewTransition
কল করার সময়, এই ট্রানজিশন রুটটি হ'ল <html>
উপাদান, document.documentElement
ব্যবহার করে অ্যাক্সেসযোগ্য J জাভাস্ক্রিপ্টে ডকুমেন্টলিমেন্ট:
if (isBackNavigation) {
document.documentElement.classList.add('back-transition');
}
const transition = document.startViewTransition(() =>
updateTheDOMSomehow(data)
);
try {
await transition.finished;
} finally {
document.documentElement.classList.remove('back-transition');
}
রূপান্তর সমাপ্ত হওয়ার পরে ক্লাসগুলি অপসারণ করতে, এই উদাহরণটি transition.finished
ব্যবহার করে n এই বস্তুর অন্যান্য বৈশিষ্ট্যগুলি এপিআই রেফারেন্সে আচ্ছাদিত।
রূপান্তরটি পরিবর্তন করতে এখন আপনি আপনার সিএসএসে সেই শ্রেণীর নামটি ব্যবহার করতে পারেন:
/* 'Forward' transitions */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms
cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Overrides for 'back' transitions */
.back-transition::view-transition-old(root) {
animation-name: fade-out, slide-to-right;
}
.back-transition::view-transition-new(root) {
animation-name: fade-in, slide-from-left;
}
মিডিয়া প্রশ্নের মতো, এই শ্রেণীর উপস্থিতিগুলি পরিবর্তন করতেও ব্যবহার করা যেতে পারে কোন উপাদানগুলি একটি view-transition-name
পায়।
অন্যান্য অ্যানিমেশনগুলি হিমায়িত না করে ট্রানজিশন চালান
একটি ভিডিও স্থানান্তরিত অবস্থানের এই ডেমোটি একবার দেখুন:
আপনি কি এতে কিছু ভুল দেখেছেন? আপনি না করলে চিন্তা করবেন না। এখানে এটি ডানদিকে ধীর হয়ে গেছে:
রূপান্তর চলাকালীন, ভিডিওটি হিমশীতল হিসাবে উপস্থিত হয়, তারপরে ভিডিওটির প্লে সংস্করণটি ম্লান হয়ে যায় This এটি কারণ ::view-transition-old(video)
পুরানো দৃশ্যের একটি স্ক্রিনশট, যেখানে ::view-transition-new(video)
নতুন দৃশ্যের একটি লাইভ চিত্র।
আপনি এটি ঠিক করতে পারেন, তবে প্রথমে নিজেকে জিজ্ঞাসা করুন এটি ঠিক করার মতো কিনা। আপনি যখন 'সমস্যা' দেখতে না দেখেন যখন ট্রানজিশনটি তার স্বাভাবিক গতিতে খেলছিল, তবে আমি এটি পরিবর্তন করতে বিরক্ত করব না।
আপনি যদি সত্যিই এটি ঠিক করতে চান তবে ::view-transition-old(video)
প্রদর্শন করবেন না; সরাসরি ::view-transition-new(video)
এ স্যুইচ করুন। আপনি ডিফল্ট শৈলী এবং অ্যানিমেশনগুলি ওভাররাইড করে এটি করতে পারেন:
::view-transition-old(video) {
/* Don't show the frozen old view */
display: none;
}
::view-transition-new(video) {
/* Don't fade the new view in */
animation: none;
}
আর এটাই!
এখন ভিডিওটি পুরো রূপান্তর জুড়ে বাজায়।
নেভিগেশন এপিআই (এবং অন্যান্য ফ্রেমওয়ার্ক) এর সাথে সংহতকরণ
দেখুন ট্রানজিশনগুলি এমনভাবে নির্দিষ্ট করা হয়েছে যাতে এগুলি অন্যান্য ফ্রেমওয়ার্ক বা লাইব্রেরির সাথে সংহত করা যায়। উদাহরণস্বরূপ, যদি আপনার একক পৃষ্ঠার অ্যাপ্লিকেশন (এসপিএ) একটি রাউটার ব্যবহার করে থাকে তবে আপনি ভিউ ট্রানজিশন ব্যবহার করে সামগ্রী আপডেট করতে রাউটারের আপডেট প্রক্রিয়াটি সামঞ্জস্য করতে পারেন।
এই পৃষ্ঠাগুলি ডেমো থেকে নেওয়া নিম্নলিখিত কোডে স্নিপেটে নেভিগেশন এপিআইয়ের ইন্টারসেপশন হ্যান্ডলারটি কল document.startViewTransition
সামঞ্জস্য করা হয় vid
navigation.addEventListener("navigate", (e) => {
// Don't intercept if not needed
if (shouldNotIntercept(e)) return;
// Intercept the navigation
e.intercept({
handler: async () => {
// Fetch the new content
const newContent = await fetchNewContent(e.destination.url, {
signal: e.signal,
});
// The UA does not support View Transitions, or the UA
// already provided a Visual Transition by itself (e.g. swipe back).
// In either case, update the DOM directly
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
// Update the content using a View Transition
const t = document.startViewTransition(() => {
setContent(newContent);
});
}
});
});
কিছু, তবে সব কিছু নয়, ব্যবহারকারী যখন নেভিগেট করার জন্য একটি সোয়াইপ অঙ্গভঙ্গি সম্পাদন করে তখন ব্রাউজারগুলি তাদের নিজস্ব রূপান্তর সরবরাহ করে। সেক্ষেত্রে আপনার নিজের ভিউ ট্রানজিশনটি ট্রিগার করা উচিত নয় কারণ এটি কোনও দুর্বল বা বিভ্রান্তিকর ব্যবহারকারীর অভিজ্ঞতার দিকে পরিচালিত করবে। ব্যবহারকারী দুটি ট্রানজিশন দেখতে পাবে - একটি ব্রাউজার দ্বারা সরবরাহ করা এবং অন্যটি আপনার দ্বারা - উত্তরাধিকার সূত্রে পুনরুত্থিত।
অতএব, ব্রাউজারটি যখন নিজস্ব ভিজ্যুয়াল ট্রানজিশন সরবরাহ করেছে তখন কোনও ভিউ ট্রানজিশন শুরু হওয়া থেকে রোধ করার পরামর্শ দেওয়া হয়। এটি অর্জনের জন্য, NavigateEvent
উদাহরণের hasUAVisualTransition
সম্পত্তিটির মান পরীক্ষা করুন। যখন ব্রাউজারটি একটি ভিজ্যুয়াল ট্রানজিশন সরবরাহ করে তখন সম্পত্তিটি true
সেট করা থাকে। এই hasUIVisualTransition
সম্পত্তি PopStateEvent
উদাহরণগুলিতেও বিদ্যমান।
পূর্ববর্তী স্নিপেটে চেকটি নির্ধারণ করে যে ভিউ ট্রানজিশনটি চালানো হবে কিনা তা এই সম্পত্তিটিকে বিবেচনায় নেয়। যখন একই ডকুমেন্ট ভিউ ট্রানজিশনের জন্য কোনও সমর্থন নেই বা যখন ব্রাউজারটি ইতিমধ্যে তার নিজস্ব রূপান্তর সরবরাহ করে, তখন ভিউ ট্রানজিশনটি এড়িয়ে যায়।
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
নিম্নলিখিত রেকর্ডিংয়ে, ব্যবহারকারী পূর্ববর্তী পৃষ্ঠায় ফিরে নেভিগেট করতে সোয়াইপ করে। বাম দিকে ক্যাপচারে hasUAVisualTransition
পতাকা জন্য একটি চেক অন্তর্ভুক্ত নয়। ডানদিকে রেকর্ডিংয়ে চেক অন্তর্ভুক্ত রয়েছে, যার ফলে ম্যানুয়াল ভিউ ট্রানজিশনটি এড়িয়ে যায় কারণ ব্রাউজারটি একটি ভিজ্যুয়াল ট্রানজিশন সরবরাহ করে।
জাভাস্ক্রিপ্ট সহ অ্যানিমেটিং
এখনও অবধি, সমস্ত ট্রানজিশনগুলি সিএসএস ব্যবহার করে সংজ্ঞায়িত করা হয়েছে, তবে কখনও কখনও সিএসএস যথেষ্ট নয়:
এই রূপান্তরের কয়েকটি অংশ একা সিএসএস দিয়ে অর্জন করা যায় না:
- অ্যানিমেশনটি ক্লিক অবস্থান থেকে শুরু হয়।
- অ্যানিমেশনটি চেনাশোনাটি দূরের কোণে ব্যাসার্ধের সাথে শেষ হয়। যদিও, আশা করি ভবিষ্যতে সিএসএসের মাধ্যমে এটি সম্ভব হবে।
ধন্যবাদ, আপনি ওয়েব অ্যানিমেশন এপিআই ব্যবহার করে ট্রানজিশন তৈরি করতে পারেন!
let lastClick;
addEventListener('click', event => (lastClick = event));
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// Get the click position, or fallback to the middle of the screen
const x = lastClick?.clientX ?? innerWidth / 2;
const y = lastClick?.clientY ?? innerHeight / 2;
// Get the distance to the furthest corner
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);
// With a transition:
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
});
// Wait for the pseudo-elements to be created:
transition.ready.then(() => {
// Animate the root's new view
document.documentElement.animate(
{
clipPath: [
`circle(0 at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
],
},
{
duration: 500,
easing: 'ease-in',
// Specify which pseudo-element to animate
pseudoElement: '::view-transition-new(root)',
}
);
});
}
এই উদাহরণটি transition.ready
ব্যবহার করে RE রেডি, এমন একটি প্রতিশ্রুতি যা একবার ট্রানজিশন সিউডো-উপাদানগুলি সফলভাবে তৈরি হয়ে গেলে সমাধান করে। এই বস্তুর অন্যান্য বৈশিষ্ট্যগুলি এপিআই রেফারেন্সে আচ্ছাদিত।
বর্ধন হিসাবে রূপান্তর
ভিউ ট্রানজিশন এপিআই একটি ডোম পরিবর্তন 'মোড়ানো' এবং এর জন্য একটি রূপান্তর তৈরি করার জন্য ডিজাইন করা হয়েছে। যাইহোক, ট্রানজিশনটিকে বর্ধন হিসাবে বিবেচনা করা উচিত, যেমনটি, আপনার অ্যাপ্লিকেশনটি ডিওএম পরিবর্তন সফল হলে 'ত্রুটি' অবস্থায় প্রবেশ করা উচিত নয়, তবে রূপান্তরটি ব্যর্থ হয়। আদর্শভাবে ট্রানজিশনটি ব্যর্থ হওয়া উচিত নয়, তবে যদি তা হয় তবে এটি ব্যবহারকারীর বাকী অভিজ্ঞতা ভাঙবে না।
ট্রানজিশনগুলিকে বর্ধন হিসাবে বিবেচনা করার জন্য, ট্রানজিশন প্রতিশ্রুতিগুলি এমনভাবে ব্যবহার না করার বিষয়ে যত্ন নিন যা আপনার অ্যাপ্লিকেশনটি যদি ট্রানজিশন ব্যর্থ হয় তবে আপনার অ্যাপটি নিক্ষেপ করতে পারে।
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); }
এই উদাহরণটির সাথে সমস্যাটি হ'ল switchView()
প্রত্যাখ্যান করবে যদি ট্রানজিশনটি কোনও ready
অবস্থায় পৌঁছতে না পারে তবে এর অর্থ এই নয় যে ভিউটি স্যুইচ করতে ব্যর্থ হয়েছে। ডিওএম সফলভাবে আপডেট হতে পারে, তবে সেখানে সদৃশ view-transition-name
এস ছিল, সুতরাং রূপান্তরটি এড়িয়ে যাওয়া হয়েছিল।
পরিবর্তে:
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); animateFromMiddle(transition); await transition.updateCallbackDone; } async function animateFromMiddle(transition) { try { await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); } catch (err) { // You might want to log this error, but it shouldn't break the app } }
এই উদাহরণটি ডিওএম আপডেটের জন্য অপেক্ষা করতে এবং এটি ব্যর্থ হলে প্রত্যাখ্যান করার জন্য transition.updateCallbackDone
ব্যবহার করে। switchView
আর প্রত্যাখ্যান করে না যদি ট্রানজিশন ব্যর্থ হয়, ডিওএম আপডেটটি শেষ হলে এটি সমাধান করে এবং এটি ব্যর্থ হলে প্রত্যাখ্যান করে।
আপনি যদি নতুন ভিউটি 'নিষ্পত্তি' হয়ে গেলে switchView
সমাধান করতে চান তবে কোনও অ্যানিমেটেড ট্রানজিশনটি শেষ হয়েছে বা শেষ পর্যন্ত এড়িয়ে গেছে, transition.updateCallbackDone
প্রতিস্থাপন করুন transition.finished
পলিফিল নয়, তবে…
এটি পলিফিলের পক্ষে সহজ বৈশিষ্ট্য নয়। যাইহোক, এই সহায়ক ফাংশনটি ব্রাউজারগুলিতে জিনিসগুলি আরও সহজ করে তোলে যা ভিউ ট্রানজিশনগুলিকে সমর্থন করে না:
function transitionHelper({
skipTransition = false,
types = [],
update,
}) {
const unsupported = (error) => {
const updateCallbackDone = Promise.resolve(update()).then(() => {});
return {
ready: Promise.reject(Error(error)),
updateCallbackDone,
finished: updateCallbackDone,
skipTransition: () => {},
types,
};
}
if (skipTransition || !document.startViewTransition) {
return unsupported('View Transitions are not supported in this browser');
}
try {
const transition = document.startViewTransition({
update,
types,
});
return transition;
} catch (e) {
return unsupported('View Transitions with types are not supported in this browser');
}
}
এবং এটি এর মতো ব্যবহার করা যেতে পারে:
function spaNavigate(data) {
const types = isBackNavigation ? ['back-transition'] : [];
const transition = transitionHelper({
update() {
updateTheDOMSomehow(data);
},
types,
});
// …
}
ব্রাউজারগুলিতে যা ভিউ ট্রানজিশনগুলিকে সমর্থন করে না, updateDOM
এখনও কল করা হবে, তবে অ্যানিমেটেড ট্রানজিশন হবে না।
ট্রানজিশনের সময় <html>
এ যুক্ত করতে আপনি কিছু classNames
সরবরাহ করতে পারেন, নেভিগেশনের ধরণের উপর নির্ভর করে রূপান্তরটি পরিবর্তন করা আরও সহজ করে তোলে।
আপনি যদি কোনও অ্যানিমেশন না চান তবে আপনি skipTransition
ক্ষেত্রেও true
পাস করতে পারেন, এমনকি ব্রাউজারগুলিতেও যা ভিউ ট্রানজিশনগুলিকে সমর্থন করে। যদি আপনার সাইটে ট্রানজিশনগুলি অক্ষম করার জন্য কোনও ব্যবহারকারীর পছন্দ থাকে তবে এটি কার্যকর।
ফ্রেমওয়ার্ক নিয়ে কাজ করা
আপনি যদি এমন কোনও গ্রন্থাগার বা কাঠামোর সাথে কাজ করছেন যা ডোম পরিবর্তনগুলি বিমূর্ত করে, তবে জটিল অংশটি কখন ডোম পরিবর্তনটি সম্পূর্ণ হবে তা জেনে চলেছে। বিভিন্ন ফ্রেমওয়ার্কগুলিতে উপরের সহায়ক ব্যবহার করে এখানে উদাহরণগুলির একটি সেট রয়েছে।
- প্রতিক্রিয়া - এখানে কীটি
flushSync
, যা রাষ্ট্রীয় পরিবর্তনের একটি সেটকে সিঙ্ক্রোনালিভাবে প্রয়োগ করে। হ্যাঁ, সেই এপিআই ব্যবহার করার বিষয়ে একটি বড় সতর্কতা রয়েছে, তবে ড্যান আব্রামভ আমাকে আশ্বাস দিয়েছেন যে এটি এই ক্ষেত্রে উপযুক্ত। প্রতিক্রিয়া এবং অ্যাসিঙ্ক কোড সহ যথারীতি,startViewTransition
দ্বারা ফিরে আসা বিভিন্ন প্রতিশ্রুতি ব্যবহার করার সময়, আপনার কোডটি সঠিক অবস্থার সাথে চলছে সেদিকে খেয়াল রাখুন। - Vue.js - এখানে কীটি
nextTick
, যা ডিওএম আপডেট হওয়ার পরে পূরণ করে। - Vevete - vue এর মতোই, তবে পরবর্তী পরিবর্তনের জন্য অপেক্ষা করার পদ্ধতিটি হ'ল
tick
। - লিট - কীটি এখানে
this.updateComplete
- কৌণিক - এখানে কীটি
applicationRef.tick
, যা ডোম পরিবর্তনগুলি মুলতুবি করে দেয়। কৌণিক সংস্করণ 17 হিসাবে আপনি@angular/router
সাথে আসাwithViewTransitions
ব্যবহার করতে পারেন।
API রেফারেন্স
-
const viewTransition = document.startViewTransition(update)
একটি নতুন
ViewTransition
শুরু করুন।update
এমন একটি ফাংশন যা একবার ডকুমেন্টের বর্তমান অবস্থা ধরা পড়লে বলা হয়।তারপরে, যখন
updateCallback
মাধ্যমে প্রতিশ্রুতিটি ফিরে আসে তখন পরবর্তী ফ্রেমে রূপান্তরটি শুরু হয়। যদিupdateCallback
দ্বারা প্রতিশ্রুতি দেওয়া হয় তবে তা প্রত্যাখ্যান করে, রূপান্তরটি পরিত্যাগ করা হয়।-
const viewTransition = document.startViewTransition({ update, types })
নির্দিষ্ট প্রকারের সাথে একটি নতুন
ViewTransition
শুরু করুনডকুমেন্টের বর্তমান অবস্থা ক্যাপচার হয়ে গেলে
update
বলা হয়।types
রূপান্তরটি ক্যাপচার বা সম্পাদন করার সময় সক্রিয় প্রকারগুলি সেট করে। এটি প্রাথমিকভাবে খালি। আরও তথ্যের জন্যviewTransition.types
আরও নীচে দেখুন।
উদাহরণস্বরূপ ViewTransition
ট্রান্সশনিশনের সদস্যরা:
-
viewTransition.updateCallbackDone
একটি প্রতিশ্রুতি যা
updateCallback
দ্বারা প্রতিশ্রুতিটি ফিরে আসে যখন তা পূরণ হয়, বা এটি প্রত্যাখ্যান করার পরে প্রত্যাখ্যান করে।ভিউ ট্রানজিশন এপিআই একটি ডোম পরিবর্তনকে আবৃত করে এবং একটি রূপান্তর তৈরি করে। যাইহোক, কখনও কখনও আপনি ট্রানজিশন অ্যানিমেশনের সাফল্য বা ব্যর্থতার বিষয়ে চিন্তা করেন না, আপনি কেবল এবং কখন ডোম পরিবর্তনটি ঘটে তা জানতে চান।
updateCallbackDone
সেই ব্যবহার-কেসের জন্য।-
viewTransition.ready
এমন একটি প্রতিশ্রুতি যা রূপান্তরটির জন্য সিউডো-উপাদানগুলি তৈরি হয়ে গেলে এবং অ্যানিমেশনটি শুরু হতে চলেছে।
রূপান্তর শুরু না হলে এটি প্রত্যাখ্যান করে। এটি ভুল কনফিগারেশনের কারণে হতে পারে, যেমন সদৃশ
view-transition-name
এস, বা যদিupdateCallback
কোনও প্রত্যাখ্যানিত প্রতিশ্রুতি দেয়।এটি জাভাস্ক্রিপ্ট সহ ট্রানজিশন সিউডো-উপাদানগুলি অ্যানিমেট করার জন্য দরকারী।
-
viewTransition.finished
একটি প্রতিশ্রুতি যা একবার শেষের পরে সম্পূর্ণরূপে দৃশ্যমান এবং ব্যবহারকারীর কাছে ইন্টারেক্টিভ হয়।
এটি কেবল তখনই প্রত্যাখ্যান করে যদি
updateCallback
কোনও প্রত্যাখ্যানিত প্রতিশ্রুতি দেয়, কারণ এটি নির্দেশ করে যে শেষ অবস্থাটি তৈরি করা হয়নি।অন্যথায়, যদি কোনও ট্রানজিশন শুরু হতে ব্যর্থ হয়, বা রূপান্তরকালে এড়িয়ে যাওয়া হয় তবে শেষের অবস্থাটি এখনও পৌঁছেছে, তাই
finished
হয়।-
viewTransition.types
একটি
Set
-জাতীয় অবজেক্ট যা সক্রিয় ভিউ ট্রানজিশনের প্রকারগুলি ধারণ করে। এন্ট্রিগুলি পরিচালনা করতে, এর উদাহরণ পদ্ধতিগুলিclear()
,add()
, এবংdelete()
ব্যবহার করুন।সিএসএসে একটি নির্দিষ্ট ধরণের প্রতিক্রিয়া জানাতে, ট্রানজিশন রুটে
:active-view-transition-type(type)
সিউডো-শ্রেণীর নির্বাচক ব্যবহার করুন।ভিউ ট্রানজিশন শেষ হয়ে গেলে প্রকারগুলি স্বয়ংক্রিয়ভাবে পরিষ্কার হয়ে যায়।
-
viewTransition.skipTransition()
ট্রানজিশনের অ্যানিমেশন অংশটি এড়িয়ে যান।
এটি কলিং
updateCallback
এড়িয়ে যাবে না, কারণ ডিওএম পরিবর্তনটি রূপান্তর থেকে পৃথক।
ডিফল্ট শৈলী এবং রূপান্তর রেফারেন্স
-
::view-transition
- রুট সিউডো-উপাদান যা ভিউপোর্টটি পূরণ করে এবং প্রতিটি
::view-transition-group
রয়েছে। -
::view-transition-group
একেবারে অবস্থান।
'আগের' এবং 'পরে' রাজ্যের মধ্যে
width
এবংheight
রূপান্তর করে।ট্রানজিশনগুলি 'আগে' এবং 'পরে' ভিউপোর্ট-স্পেস কোয়াডের মধ্যে
transform
।-
::view-transition-image-pair
গোষ্ঠীটি পূরণ করার জন্য একেবারে অবস্থানযুক্ত।
isolation: isolate
পুরানো এবং নতুন দৃশ্যেmix-blend-mode
প্রভাব সীমাবদ্ধ করতে বিচ্ছিন্ন।-
::view-transition-new
এবং::view-transition-old
একেবারে মোড়কের শীর্ষ-বাম দিকে অবস্থিত।
গ্রুপের প্রস্থের 100% পূরণ করে তবে একটি অটো উচ্চতা রয়েছে, সুতরাং এটি গ্রুপটি পূরণ করার পরিবর্তে এর দিক অনুপাত বজায় রাখবে।
mix-blend-mode: plus-lighter
সত্যিকারের ক্রস-ফেডের জন্য অনুমতি দেয়।opacity: 1
opacity: 0
।opacity: 0
opacity: 1
।
প্রতিক্রিয়া
বিকাশকারী প্রতিক্রিয়া সর্বদা প্রশংসা করা হয়। এটি করার জন্য, পরামর্শ এবং প্রশ্নগুলির সাথে গিটহাবের সিএসএস ওয়ার্কিং গ্রুপে একটি সমস্যা ফাইল করুন । [css-view-transitions]
দিয়ে আপনার ইস্যুটিকে উপস্থাপিত করুন।
আপনি যদি কোনও বাগের মধ্যে চলে যান তবে পরিবর্তে ক্রোমিয়াম বাগ ফাইল করুন ।
,প্রকাশিত: আগস্ট 17, 2021, সর্বশেষ আপডেট: সেপ্টেম্বর 25, 2024
যখন কোনও ভিউ ট্রানজিশন একটি একক নথিতে চলে তখন তাকে একই ডকুমেন্ট ভিউ ট্রানজিশন বলা হয়। এটি সাধারণত একক পৃষ্ঠার অ্যাপ্লিকেশনগুলিতে (এসপিএ) কেস যেখানে জাভাস্ক্রিপ্টটি ডিওএম আপডেট করতে ব্যবহৃত হয়। একই ডকুমেন্ট ভিউ ট্রানজিশনগুলি ক্রোম 111 হিসাবে ক্রোমে সমর্থিত।
একটি একই ডকুমেন্ট ভিউ ট্রানজিশন ট্রিগার করতে, document.startViewTransition
কল করুন rast স্টার্টভিউ ট্রান্সিশন:
function handleClick(e) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow();
return;
}
// With a View Transition:
document.startViewTransition(() => updateTheDOMSomehow());
}
যখন অনুরোধ করা হয়, ব্রাউজারটি স্বয়ংক্রিয়ভাবে সমস্ত উপাদানগুলির স্ন্যাপশটগুলি ক্যাপচার করে যার মধ্যে একটি view-transition-name
সিএসএস সম্পত্তি তাদের ঘোষণা করা হয়।
এরপরে এটি কলব্যাকের পাসটি কার্যকর করে যা ডোম আপডেট করে, এর পরে এটি নতুন রাজ্যের স্ন্যাপশট নেয়।
এই স্ন্যাপশটগুলি তখন সিউডো-উপাদানগুলির একটি গাছে সাজানো হয় এবং সিএসএস অ্যানিমেশনগুলির শক্তি ব্যবহার করে অ্যানিমেটেড। পুরানো এবং নতুন রাজ্য থেকে স্ন্যাপশটের জোড়াগুলি তাদের পুরানো অবস্থান এবং আকার থেকে তাদের নতুন স্থানে সহজেই স্থানান্তরিত করে, যখন তাদের সামগ্রী ক্রসফ্যাড করে। আপনি যদি চান তবে আপনি অ্যানিমেশনগুলি কাস্টমাইজ করতে সিএসএস ব্যবহার করতে পারেন।
ডিফল্ট ট্রানজিশন: ক্রস-ফেড
ডিফল্ট ভিউ ট্রানজিশনটি ক্রস-ফেড, সুতরাং এটি এপিআইয়ের একটি দুর্দান্ত ভূমিকা হিসাবে কাজ করে:
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// With a transition:
document.startViewTransition(() => updateTheDOMSomehow(data));
}
যেখানে updateTheDOMSomehow
ডমকে নতুন অবস্থায় পরিবর্তন করে। আপনি চান যদিও এটি করা যেতে পারে। উদাহরণস্বরূপ, আপনি উপাদানগুলি যুক্ত করতে বা অপসারণ করতে পারেন, শ্রেণীর নাম পরিবর্তন করতে পারেন বা শৈলী পরিবর্তন করতে পারেন।
এবং ঠিক এর মতো, পৃষ্ঠাগুলি ক্রস-ফেড:
ঠিক আছে, একটি ক্রস-ফেড তেমন চিত্তাকর্ষক নয়। ধন্যবাদ, ট্রানজিশনগুলি কাস্টমাইজ করা যেতে পারে তবে প্রথমে আপনাকে বুঝতে হবে যে এই বেসিক ক্রস-ফেড কীভাবে কাজ করেছে।
এই রূপান্তরগুলি কীভাবে কাজ করে
আগের কোড নমুনা আপডেট করা যাক।
document.startViewTransition(() => updateTheDOMSomehow(data));
যখন .startViewTransition()
বলা হয়, এপিআই পৃষ্ঠার বর্তমান অবস্থাটি ক্যাপচার করে। এর মধ্যে একটি স্ন্যাপশট নেওয়া অন্তর্ভুক্ত।
একবার সম্পূর্ণ হয়ে গেলে, কলব্যাকটি .startViewTransition()
এ পাস করা হয়। সেখানেই ডোম পরিবর্তন করা হয়েছে। তারপরে, এপিআই পৃষ্ঠার নতুন অবস্থাটি ক্যাপচার করে।
নতুন রাষ্ট্রটি ধরা হলে, এপিআই এর মতো একটি সিউডো-এলিমেন্ট গাছ তৈরি করে:
::view-transition
└─ ::view-transition-group(root)
└─ ::view-transition-image-pair(root)
├─ ::view-transition-old(root)
└─ ::view-transition-new(root)
::view-transition
পৃষ্ঠার সমস্ত কিছুর উপরে একটি ওভারলেতে বসে। আপনি যদি স্থানান্তরের জন্য একটি পটভূমি রঙ সেট করতে চান তবে এটি দরকারী।
::view-transition-old(root)
পুরানো দৃশ্যের একটি স্ক্রিনশট এবং ::view-transition-new(root)
নতুন দৃশ্যের লাইভ উপস্থাপনা। উভয়ই সিএসএস 'প্রতিস্থাপন সামগ্রী' হিসাবে রেন্ডার (একটি <img>
এর মতো)।
পুরানো দৃশ্যটি opacity: 1
opacity: 0
, যখন নতুন দৃশ্যটি opacity: 0
opacity: 1
, ক্রস-ফেড তৈরি করে।
সমস্ত অ্যানিমেশন সিএসএস অ্যানিমেশন ব্যবহার করে সঞ্চালিত হয়, যাতে সেগুলি সিএসএস দিয়ে কাস্টমাইজ করা যায়।
রূপান্তর কাস্টমাইজ করুন
সমস্ত ভিউ ট্রানজিশন সিউডো-উপাদানগুলি সিএসএসের সাথে লক্ষ্য করা যায় এবং যেহেতু অ্যানিমেশনগুলি সিএসএস ব্যবহার করে সংজ্ঞায়িত করা হয়, আপনি বিদ্যমান সিএসএস অ্যানিমেশন বৈশিষ্ট্যগুলি ব্যবহার করে সেগুলি সংশোধন করতে পারেন। যেমন:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 5s;
}
সেই এক পরিবর্তন সহ, বিবর্ণটি এখন সত্যিই ধীর:
ঠিক আছে, এটি এখনও চিত্তাকর্ষক নয়। পরিবর্তে, নিম্নলিখিত কোডটি উপাদান ডিজাইনের ভাগ করা অক্ষ রূপান্তর প্রয়োগ করে:
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(30px); }
}
@keyframes slide-to-left {
to { transform: translateX(-30px); }
}
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
এবং এখানে ফলাফল:
একাধিক উপাদান রূপান্তর
পূর্ববর্তী ডেমোতে, পুরো পৃষ্ঠাটি ভাগ করা অক্ষের রূপান্তরটিতে জড়িত। এটি বেশিরভাগ পৃষ্ঠার জন্য কাজ করে তবে এটি শিরোনামগুলির পক্ষে একেবারেই ঠিক মনে হয় না, কারণ এটি কেবল আবার পিছলে যেতে স্লাইড হয়ে যায়।
এটি এড়াতে, আপনি বাকী পৃষ্ঠাটি থেকে শিরোনামটি বের করতে পারেন যাতে এটি আলাদাভাবে অ্যানিমেটেড করা যায়। এটি উপাদানটির জন্য একটি view-transition-name
নির্ধারণ করে করা হয়।
.main-header {
view-transition-name: main-header;
}
view-transition-name
মান আপনি যা চান তা হতে পারে ( none
ব্যতীত, যার অর্থ কোনও রূপান্তর নাম নেই)। এটি রূপান্তর জুড়ে উপাদানটি অনন্যভাবে সনাক্ত করতে ব্যবহৃত হয়।
এবং এর ফলাফল:
এখন শিরোনামটি জায়গায় এবং ক্রস-ফেডে থাকে।
এই সিএসএস ঘোষণার ফলে সিউডো-এলিমেন্ট ট্রি পরিবর্তন হয়েছিল:
::view-transition
├─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
└─ ::view-transition-group(main-header)
└─ ::view-transition-image-pair(main-header)
├─ ::view-transition-old(main-header)
└─ ::view-transition-new(main-header)
এখন দুটি ট্রানজিশন গ্রুপ রয়েছে। একটি শিরোনামের জন্য, এবং অন্যটি বাকি জন্য। এগুলি সিএসএস দিয়ে স্বাধীনভাবে লক্ষ্য করা যায় এবং বিভিন্ন রূপান্তর দেওয়া যেতে পারে। যদিও, এক্ষেত্রে main-header
ডিফল্ট ট্রানজিশনের সাথে রেখে দেওয়া হয়েছিল, যা ক্রস-ফেড।
ঠিক আছে, ঠিক আছে, ডিফল্ট ট্রানজিশনটি কেবল একটি ক্রস বিবর্ণ নয়, ::view-transition-group
ট্রানজিশন:
- অবস্থান এবং রূপান্তর (একটি
transform
ব্যবহার করে) - প্রস্থ
- উচ্চতা
এটি এখনও অবধি গুরুত্বপূর্ণ হয়নি, কারণ শিরোনামটি একই আকার এবং ডিওএম পরিবর্তনের উভয় পক্ষের অবস্থান। তবে আপনি শিরোনামে পাঠ্যটিও বের করতে পারেন:
.main-header-text {
view-transition-name: main-header-text;
width: fit-content;
}
fit-content
ব্যবহৃত হয় তাই উপাদানটি বাকী প্রস্থ পর্যন্ত প্রসারিত না করে পাঠ্যের আকার। এটি ব্যতীত, পিছনের তীরটি উভয় পৃষ্ঠায় একই আকারের চেয়ে শিরোনাম পাঠ্য উপাদানটির আকার হ্রাস করে।
সুতরাং এখন আমাদের সাথে খেলতে তিনটি অংশ রয়েছে:
::view-transition
├─ ::view-transition-group(root)
│ └─ …
├─ ::view-transition-group(main-header)
│ └─ …
└─ ::view-transition-group(main-header-text)
└─ …
তবে আবার, কেবল ডিফল্টগুলির সাথে চলছে:
এখন শিরোনাম পাঠ্যটি ব্যাক বোতামের জন্য স্থান তৈরি করতে কিছুটা সন্তোষজনক স্লাইড করে।
view-transition-class
সহ একইভাবে একাধিক সিউডো-উপাদানগুলি অ্যানিমেট করুন
ব্রাউজার সমর্থন
বলুন আপনার একগুচ্ছ কার্ডের সাথে একটি ভিউ ট্রানজিশন রয়েছে তবে পৃষ্ঠায় একটি শিরোনামও রয়েছে। To animate all cards except the title, you have to write a selector that targets each and every individual card.
h1 {
view-transition-name: title;
}
::view-transition-group(title) {
animation-timing-function: ease-in-out;
}
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
…
#card20 { view-transition-name: card20; }
::view-transition-group(card1),
::view-transition-group(card2),
::view-transition-group(card3),
::view-transition-group(card4),
…
::view-transition-group(card20) {
animation-timing-function: var(--bounce);
}
Got 20 elements? That's 20 selectors you need to write. Adding a new element? Then you also need to grow the selector that applies the animation styles. Not exactly scalable.
The view-transition-class
can be used in the view transition pseudo-elements to apply the same style rule.
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
#card5 { view-transition-name: card5; }
…
#card20 { view-transition-name: card20; }
#cards-wrapper > div {
view-transition-class: card;
}
html::view-transition-group(.card) {
animation-timing-function: var(--bounce);
}
The following cards example leverages the previous CSS snippet. All cards–including newly added ones–get the same timing applied with one selector: html::view-transition-group(.card)
.
Debug transitions
Since view transitions are built on top of CSS animations, the Animations panel in Chrome DevTools is great for debugging transitions.
Using the Animations panel, you can pause the next animation, then scrub back and forth through the animation. During this, the transition pseudo-elements can be found in the Elements panel.
Transitioning elements don't need to be the same DOM element
So far we've used view-transition-name
to create separate transition elements for the header, and the text in the header. These are conceptually the same element before and after the DOM change, but you can create transitions where that isn't the case.
For example, the main video embed can be given a view-transition-name
:
.full-embed {
view-transition-name: full-embed;
}
Then, when the thumbnail is clicked, it can be given the same view-transition-name
, just for the duration of the transition:
thumbnail.onclick = async () => {
thumbnail.style.viewTransitionName = 'full-embed';
document.startViewTransition(() => {
thumbnail.style.viewTransitionName = '';
updateTheDOMSomehow();
});
};
And the result:
The thumbnail now transitions into the main image. Even though they're conceptually (and literally) different elements, the transition API treats them as the same thing because they shared the same view-transition-name
.
The real code for this transition is a little more complicated than the preceding example, as it also handles the transition back to the thumbnail page. See the source for the full implementation.
Custom entry and exit transitions
এই উদাহরণ দেখুন:
The sidebar is part of the transition:
.sidebar {
view-transition-name: sidebar;
}
But, unlike the header in the previous example, the sidebar doesn't appear on all pages. If both states have the sidebar, the transition pseudo-elements look like this:
::view-transition
├─ …other transition groups…
└─ ::view-transition-group(sidebar)
└─ ::view-transition-image-pair(sidebar)
├─ ::view-transition-old(sidebar)
└─ ::view-transition-new(sidebar)
However, if the sidebar is only on the new page, the ::view-transition-old(sidebar)
pseudo-element won't be there. Since there's no 'old' image for the sidebar, the image-pair will only have a ::view-transition-new(sidebar)
. Similarly, if the sidebar is only on the old page, the image-pair will only have a ::view-transition-old(sidebar)
.
In the previous demo, the sidebar transitions differently depending on whether it's entering, exiting, or present in both states. It enters by sliding from the right and fading in, it exits by sliding to the right and fading out, and it stays in place when it's present in both states.
To create specific entry and exit transitions, you can use the :only-child
pseudo-class to target the old or new pseudo-elements when it's the only child in the image-pair:
/* Entry transition */
::view-transition-new(sidebar):only-child {
animation: 300ms cubic-bezier(0, 0, 0.2, 1) both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Exit transition */
::view-transition-old(sidebar):only-child {
animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
}
In this case, there's no specific transition for when the sidebar is present in both states, since the default is perfect.
Async DOM updates, and waiting for content
The callback passed to .startViewTransition()
can return a promise, which allows for async DOM updates, and waiting for important content to be ready.
document.startViewTransition(async () => {
await something;
await updateTheDOMSomehow();
await somethingElse;
});
The transition won't be started until the promise fulfills. During this time, the page is frozen, so delays here should be kept to a minimum. Specifically, network fetches should be done before calling .startViewTransition()
, while the page is still fully interactive, rather than doing them as part of the .startViewTransition()
callback.
If you decide to wait for images or fonts to be ready, be sure to use an aggressive timeout:
const wait = ms => new Promise(r => setTimeout(r, ms));
document.startViewTransition(async () => {
updateTheDOMSomehow();
// Pause for up to 100ms for fonts to be ready:
await Promise.race([document.fonts.ready, wait(100)]);
});
However, in some cases it's better to avoid the delay altogether, and use the content you already have.
Make the most of content you already have
In the case where the thumbnail transitions to a larger image:
The default transition is to cross-fade, which means the thumbnail could be cross-fading with a not-yet-loaded full image.
One way to handle this is to wait for the full image to load before starting the transition. Ideally this would be done before calling .startViewTransition()
, so the page remains interactive, and a spinner can be shown to indicate to the user that things are loading. But in this case there's a better way:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
}
Now the thumbnail doesn't fade away, it just sits underneath the full image. This means if the new view hasn't loaded, the thumbnail is visible throughout the transition. This means the transition can start straight away, and the full image can load in its own time.
This wouldn't work if the new view featured transparency, but in this case we know it doesn't, so we can make this optimization.
Handle changes in aspect ratio
Conveniently, all the transitions so far have been to elements with the same aspect ratio, but that won't always be the case. What if the thumbnail is 1:1, and the main image is 16:9?
In the default transition, the group animates from the before size to the after size. The old and new views are 100% width of the group, and auto height, meaning they keep their aspect ratio regardless of the group's size.
This is a good default, but it isn't what is wanted in this case. তাই:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
/* Make the height the same as the group,
meaning the view size might not match its aspect-ratio. */
height: 100%;
/* Clip any overflow of the view */
overflow: clip;
}
/* The old view is the thumbnail */
::view-transition-old(full-embed) {
/* Maintain the aspect ratio of the view,
by shrinking it to fit within the bounds of the element */
object-fit: contain;
}
/* The new view is the full image */
::view-transition-new(full-embed) {
/* Maintain the aspect ratio of the view,
by growing it to cover the bounds of the element */
object-fit: cover;
}
This means the thumbnail stays in the center of the element as the width expands, but the full image 'un-crops' as it transitions from 1:1 to 16:9.
For more detailed information, check out View transitions: Handling aspect ratio changes
Use media queries to change transitions for different device states
You may want to use different transitions on mobile versus desktop, such as this example which performs a full slide from the side on mobile, but a more subtle slide on desktop:
This can be achieved using regular media queries:
/* Transitions for mobile */
::view-transition-old(root) {
animation: 300ms ease-out both full-slide-to-left;
}
::view-transition-new(root) {
animation: 300ms ease-out both full-slide-from-right;
}
@media (min-width: 500px) {
/* Overrides for larger displays.
This is the shared axis transition from earlier in the article. */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
}
You may also want to change which elements you assign a view-transition-name
depending on matching media queries.
React to the 'reduced motion' preference
Users can indicate they prefer reduced motion through their operating system, and that preference is exposed in CSS .
You could chose to prevent any transitions for these users:
@media (prefers-reduced-motion) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
However, a preference for 'reduced motion' doesn't mean the user wants no motion . Instead of the preceding snippet, you could choose a more subtle animation, but one that still expresses the relationship between elements, and the flow of data.
Handle multiple view transition styles with view transition types
ব্রাউজার সমর্থন
Sometimes a transition from one particular view to another should have a specifically tailored transition. For example, when going to the next or to the previous page in a pagination sequence, you might want to slide the contents in a different direction depending on whether you are going to a higher page or a lower page from the sequence.
For this you can use view transition types, which allow you to assign one or more types to an active view transition. For example, when transitioning to a higher page in a pagination sequence use the forwards
type and when going to a lower page use the backwards
type. These types are only active when capturing or performing a transition, and each type can be customized through CSS to use different animations.
To use types in a same-document view transition, you pass types
into the startViewTransition
method. To allow this, document.startViewTransition
also accepts an object: update
is the callback function that updates the DOM, and types
is an array with the types.
const direction = determineBackwardsOrForwards();
const t = document.startViewTransition({
update: updateTheDOMSomehow,
types: ['slide', direction],
});
To respond to these types, use the :active-view-transition-type()
selector. Pass the type
you want to target into the selector. This lets you keep the styles of multiple view transitions separated from each other, without the declarations of the one interfering with the declarations of the other.
Because types only apply when capturing or performing the transition, you can use the selector to set–or unset–a view-transition-name
on an element only for the view transition with that type.
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only (using the default root snapshot) */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
In the following pagination demo , the page contents slide forwards or backwards based on the page number that you are navigating to. The types are determined on click upon which they get passed into document.startViewTransition
.
To target any active view transition, regardless of the type, you can use the :active-view-transition
pseudo-class selector instead.
html:active-view-transition {
…
}
Handle multiple view transition styles with a class name on the view transition root
Sometimes a transition from one particular type of view to another should have a specifically tailored transition. Or, a 'back' navigation should be different to a 'forward' navigation.
Before transition types the way to handle these cases was to temporarily set a class name on the transition root. When calling document.startViewTransition
, this transition root is the <html>
element, accessible using document.documentElement
in JavaScript:
if (isBackNavigation) {
document.documentElement.classList.add('back-transition');
}
const transition = document.startViewTransition(() =>
updateTheDOMSomehow(data)
);
try {
await transition.finished;
} finally {
document.documentElement.classList.remove('back-transition');
}
To remove the classes after the transition finishes, this example uses transition.finished
, a promise that resolves once the transition has reached its end state. Other properties of this object are covered in the API reference .
Now you can use that class name in your CSS to change the transition:
/* 'Forward' transitions */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms
cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Overrides for 'back' transitions */
.back-transition::view-transition-old(root) {
animation-name: fade-out, slide-to-right;
}
.back-transition::view-transition-new(root) {
animation-name: fade-in, slide-from-left;
}
As with media queries, the presence of these classes could also be used to change which elements get a view-transition-name
.
Run transitions without freezing other animations
Take a look at this demo of a video transitioning position:
Did you see anything wrong with it? Don't worry if you didn't. Here it is slowed right down:
During the transition, the video appears to freeze, then the playing version of the video fades in. This is because the ::view-transition-old(video)
is a screenshot of the old view, whereas the ::view-transition-new(video)
is a live image of the new view.
You can fix this, but first, ask yourself if it's worth fixing. If you didn't see the 'problem' when the transition was playing at its normal speed, I wouldn't bother changing it.
If you really want to fix it, then don't show the ::view-transition-old(video)
; switch straight to the ::view-transition-new(video)
. You can do this by overriding the default styles and animations:
::view-transition-old(video) {
/* Don't show the frozen old view */
display: none;
}
::view-transition-new(video) {
/* Don't fade the new view in */
animation: none;
}
আর এটাই!
Now the video plays throughout the transition.
Integration with the Navigation API (and other frameworks)
View transitions are specified in such a way that they can be integrated with other frameworks or libraries. For example, if your single-page application (SPA) is using a router, you can adjust the router's update mechanism to update the content using a view transition.
In the following code snippet taken from this pagination demo the Navigation API's interception handler is adjusted to call document.startViewTransition
when view transitions are supported.
navigation.addEventListener("navigate", (e) => {
// Don't intercept if not needed
if (shouldNotIntercept(e)) return;
// Intercept the navigation
e.intercept({
handler: async () => {
// Fetch the new content
const newContent = await fetchNewContent(e.destination.url, {
signal: e.signal,
});
// The UA does not support View Transitions, or the UA
// already provided a Visual Transition by itself (e.g. swipe back).
// In either case, update the DOM directly
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
// Update the content using a View Transition
const t = document.startViewTransition(() => {
setContent(newContent);
});
}
});
});
Some, but not all, browsers provide their own transition when the user performs a swipe gesture to navigate. In that case you shouldn't trigger your own view transition as it would lead to a poor or confusing user experience. The user would see two transitions—one provided by the browser and the other one by you—running in succession.
Therefore, it is recommended to prevent a view transition from starting when the browser has provided its own visual transition. To achieve this, check the value of the hasUAVisualTransition
property of the NavigateEvent
instance. The property is set to true
when the browser has provided a visual transition. This hasUIVisualTransition
property also exists on PopStateEvent
instances.
In the previous snippet the check that determines whether to run the view transition takes this property into account. When there is no support for same-document view transitions or when the browser already provided its own transition, the view transition is skipped.
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
In the following recording, the user swipes to navigate back to the previous page. The capture on the left doesn't include a check for the hasUAVisualTransition
flag. The recording on the right does include the check, thereby skipping the manual view transition because the browser provided a visual transition.
Animating with JavaScript
So far, all the transitions have been defined using CSS, but sometimes CSS isn't enough:
A couple of parts of this transition can't be achieved with CSS alone:
- The animation starts from the click location.
- The animation ends with the circle having a radius to the farthest corner. Although, hopefully this will be possible with CSS in future .
Thankfully, you can create transitions using the Web Animation API !
let lastClick;
addEventListener('click', event => (lastClick = event));
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// Get the click position, or fallback to the middle of the screen
const x = lastClick?.clientX ?? innerWidth / 2;
const y = lastClick?.clientY ?? innerHeight / 2;
// Get the distance to the furthest corner
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);
// With a transition:
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
});
// Wait for the pseudo-elements to be created:
transition.ready.then(() => {
// Animate the root's new view
document.documentElement.animate(
{
clipPath: [
`circle(0 at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
],
},
{
duration: 500,
easing: 'ease-in',
// Specify which pseudo-element to animate
pseudoElement: '::view-transition-new(root)',
}
);
});
}
This example uses transition.ready
, a promise that resolves once the transition pseudo-elements have been successfully created. Other properties of this object are covered in the API reference .
Transitions as an enhancement
The View Transition API is designed to 'wrap' a DOM change and create a transition for it. However, the transition should be treated as an enhancement, as in, your app shouldn't enter an 'error' state if the DOM change succeeds, but the transition fails. Ideally the transition shouldn't fail, but if it does, it shouldn't break the rest of the user experience.
In order to treat transitions as an enhancement, take care not to use transition promises in a way that would cause your app to throw if the transition fails.
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); }
The problem with this example is that switchView()
will reject if the transition cannot reach a ready
state, but that doesn't mean that the view failed to switch. The DOM may have successfully updated, but there were duplicate view-transition-name
s, so the transition was skipped.
পরিবর্তে:
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); animateFromMiddle(transition); await transition.updateCallbackDone; } async function animateFromMiddle(transition) { try { await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); } catch (err) { // You might want to log this error, but it shouldn't break the app } }
This example uses transition.updateCallbackDone
to wait for the DOM update, and to reject if it fails. switchView
no longer rejects if the transition fails, it resolves when the DOM update completes, and rejects if it fails.
If you want switchView
to resolve when the new view has 'settled', as in, any animated transition has completed or skipped to the end, replace transition.updateCallbackDone
with transition.finished
.
Not a polyfill, but…
This is not an easy feature to polyfill. However, this helper function makes things much easier in browsers that don't support view transitions:
function transitionHelper({
skipTransition = false,
types = [],
update,
}) {
const unsupported = (error) => {
const updateCallbackDone = Promise.resolve(update()).then(() => {});
return {
ready: Promise.reject(Error(error)),
updateCallbackDone,
finished: updateCallbackDone,
skipTransition: () => {},
types,
};
}
if (skipTransition || !document.startViewTransition) {
return unsupported('View Transitions are not supported in this browser');
}
try {
const transition = document.startViewTransition({
update,
types,
});
return transition;
} catch (e) {
return unsupported('View Transitions with types are not supported in this browser');
}
}
And it can be used like this:
function spaNavigate(data) {
const types = isBackNavigation ? ['back-transition'] : [];
const transition = transitionHelper({
update() {
updateTheDOMSomehow(data);
},
types,
});
// …
}
In browsers that don't support view transitions, updateDOM
will still be called, but there won't be an animated transition.
You can also provide some classNames
to add to <html>
during the transition, making it easier to change the transition depending on the type of navigation .
You can also pass true
to skipTransition
if you don't want an animation, even in browsers that support view transitions. This is useful if your site has a user preference to disable transitions.
Working with frameworks
If you're working with a library or framework that abstracts away DOM changes, the tricky part is knowing when the DOM change is complete. Here's a set of examples, using the helper above , in various frameworks.
- React —the key here is
flushSync
, which applies a set of state changes synchronously. Yes, there's a big warning about using that API, but Dan Abramov assures me it's appropriate in this case. As usual with React and async code, when using the various promises returned bystartViewTransition
, take care that your code is running with the correct state. - Vue.js —the key here is
nextTick
, which fulfills once the DOM has been updated. - Svelte —very similar to Vue, but the method to await the next change is
tick
. - Lit —the key here is the
this.updateComplete
promise within components, which fulfills once the DOM has been updated. - Angular —the key here is
applicationRef.tick
, which flushes pending DOM changes. As of Angular version 17 you can usewithViewTransitions
that comes with@angular/router
.
API রেফারেন্স
-
const viewTransition = document.startViewTransition(update)
Start a new
ViewTransition
.update
is a function that is called once the current state of the document is captured.Then, when the promise returned by
updateCallback
fulfills, the transition begins in the next frame. If the promise returned byupdateCallback
rejects, the transition is abandoned.-
const viewTransition = document.startViewTransition({ update, types })
Start a new
ViewTransition
with the specified typesupdate
is called once the current state of the document is captured.types
sets the active types for the transition when capturing or performing the transition. It is initially empty. SeeviewTransition.types
further down for more information.
Instance members of ViewTransition
:
-
viewTransition.updateCallbackDone
A promise that fulfills when the promise returned by
updateCallback
fulfills, or rejects when it rejects.The View Transition API wraps a DOM change and creates a transition. However, sometimes you don't care about the success or failure of the transition animation, you just want to know if and when the DOM change happens.
updateCallbackDone
is for that use-case.-
viewTransition.ready
A promise that fulfills once the pseudo-elements for the transition are created, and the animation is about to start.
It rejects if the transition cannot begin. This can be due to misconfiguration, such as duplicate
view-transition-name
s, or ifupdateCallback
returns a rejected promise.This is useful for animating the transition pseudo-elements with JavaScript .
-
viewTransition.finished
A promise that fulfills once the end state is fully visible and interactive to the user.
It only rejects if
updateCallback
returns a rejected promise, as this indicates the end state wasn't created.Otherwise, if a transition fails to begin, or is skipped during the transition, the end state is still reached, so
finished
fulfills.-
viewTransition.types
A
Set
-like object that holds the active view transition's types. To manipulate the entries, use its instance methodsclear()
,add()
, anddelete()
.To respond to a specific type in CSS, use the
:active-view-transition-type(type)
pseudo-class selector on the transition root.Types automatically get cleaned up when the view transition finishes.
-
viewTransition.skipTransition()
Skip the animation part of the transition.
This won't skip calling
updateCallback
, as the DOM change is separate to the transition.
Default style and transition reference
-
::view-transition
- The root pseudo-element which fills the viewport and contains each
::view-transition-group
. -
::view-transition-group
Absolutely positioned.
Transitions
width
andheight
between the 'before' and 'after' states.Transitions
transform
between the 'before' and 'after' viewport-space quad.-
::view-transition-image-pair
Absolutely positioned to fill the group.
Has
isolation: isolate
to limit the effect of themix-blend-mode
on the old and new views.-
::view-transition-new
and::view-transition-old
Absolutely positioned to the top-left of the wrapper.
Fills 100% of the group width, but has an auto height, so it will maintain its aspect ratio rather than filling the group.
Has
mix-blend-mode: plus-lighter
to allow for a true cross-fade.The old view transitions from
opacity: 1
toopacity: 0
. The new view transitions fromopacity: 0
toopacity: 1
.
প্রতিক্রিয়া
Developer feedback is always appreciated. To do so, file an issue with the CSS Working Group on GitHub with suggestions and questions. Prefix your issue with [css-view-transitions]
.
Should you run into a bug, then file a Chromium bug instead.
,Published: Aug 17, 2021, Last updated: Sep 25, 2024
When a view transition runs on a single document it is called a same-document view transition . This is typically the case in single-page applications (SPAs) where JavaScript is used to update the DOM. Same-document view transitions are supported in Chrome as of Chrome 111.
To trigger a same-document view transition, call document.startViewTransition
:
function handleClick(e) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow();
return;
}
// With a View Transition:
document.startViewTransition(() => updateTheDOMSomehow());
}
When invoked, the browser automatically captures snapshots of all elements that have a view-transition-name
CSS property declared on them.
It then executes the passed in callback that updates the DOM, after which it takes snapshots of the new state.
These snapshots are then arranged in a tree of pseudo-elements and animated using the power of CSS animations. Pairs of snapshots from the old and new state smoothly transition from their old position and size to their new location, while their content crossfades. If you want, you can use CSS to customize the animations.
The default transition: Cross-fade
The default view transition is a cross-fade, so it serves as a nice introduction to the API:
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// With a transition:
document.startViewTransition(() => updateTheDOMSomehow(data));
}
Where updateTheDOMSomehow
changes the DOM to the new state. That can be done however you want. For example, you can add or remove elements, change class names, or change styles.
And just like that, pages cross-fade:
Okay, a cross-fade isn't that impressive. Thankfully, transitions can be customized, but first, you need to understand how this basic cross-fade worked.
How these transitions work
Let's update the previous code sample.
document.startViewTransition(() => updateTheDOMSomehow(data));
When .startViewTransition()
is called, the API captures the current state of the page. This includes taking a snapshot.
Once complete, the callback passed to .startViewTransition()
is called. That's where the DOM is changed. Then, the API captures the new state of the page.
Once the new state is captured, the API constructs a pseudo-element tree like this:
::view-transition
└─ ::view-transition-group(root)
└─ ::view-transition-image-pair(root)
├─ ::view-transition-old(root)
└─ ::view-transition-new(root)
The ::view-transition
sits in an overlay, over everything else on the page. This is useful if you want to set a background color for the transition.
::view-transition-old(root)
is a screenshot of the old view, and ::view-transition-new(root)
is a live representation of the new view. Both render as CSS 'replaced content' (like an <img>
).
The old view animates from opacity: 1
to opacity: 0
, while the new view animates from opacity: 0
to opacity: 1
, creating a cross-fade.
All of the animation is performed using CSS animations, so they can be customized with CSS.
Customize the transition
All of the view transition pseudo-elements can be targeted with CSS, and since the animations are defined using CSS, you can modify them using existing CSS animation properties. যেমন:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 5s;
}
With that one change, the fade is now really slow:
Okay, that's still not impressive. Instead, the following code implements Material Design's shared axis transition :
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(30px); }
}
@keyframes slide-to-left {
to { transform: translateX(-30px); }
}
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
এবং এখানে ফলাফল:
Transition multiple elements
In the previous demo, the whole page is involved in the shared axis transition. That works for most of the page, but it doesn't seem quite right for the heading, as it slides out just to slide back in again.
To avoid this, you can extract the header from the rest of the page so it can be animated separately. This is done by assigning a view-transition-name
to the element.
.main-header {
view-transition-name: main-header;
}
The value of view-transition-name
can be whatever you want (except for none
, which means there's no transition name). It's used to uniquely identify the element across the transition.
And the result of that:
Now the header stays in place and cross-fades.
That CSS declaration caused the pseudo-element tree to change:
::view-transition
├─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
└─ ::view-transition-group(main-header)
└─ ::view-transition-image-pair(main-header)
├─ ::view-transition-old(main-header)
└─ ::view-transition-new(main-header)
There are now two transition groups. One for the header, and another for the rest. These can be targeted independently with CSS, and given different transitions. Although, in this case main-header
was left with the default transition, which is a cross-fade.
Well, okay, the default transition isn't just a cross fade, the ::view-transition-group
also transitions:
- Position and transform (using a
transform
) - প্রস্থ
- উচ্চতা
That hasn't mattered until now, as the header is the same size and position both sides of the DOM change. But you can also extract the text in the header:
.main-header-text {
view-transition-name: main-header-text;
width: fit-content;
}
fit-content
is used so the element is the size of the text, rather than stretching to the remaining width. Without this, the back arrow reduces the size of the header text element, rather than the same size in both pages.
So now we have three parts to play with:
::view-transition
├─ ::view-transition-group(root)
│ └─ …
├─ ::view-transition-group(main-header)
│ └─ …
└─ ::view-transition-group(main-header-text)
└─ …
But again, just going with the defaults:
Now the heading text does a little satisfying slide across to make space for the back button.
Animate multiple pseudo-elements in the same way with view-transition-class
ব্রাউজার সমর্থন
Say you have a view transition with a bunch of cards but also a title on the page. To animate all cards except the title, you have to write a selector that targets each and every individual card.
h1 {
view-transition-name: title;
}
::view-transition-group(title) {
animation-timing-function: ease-in-out;
}
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
…
#card20 { view-transition-name: card20; }
::view-transition-group(card1),
::view-transition-group(card2),
::view-transition-group(card3),
::view-transition-group(card4),
…
::view-transition-group(card20) {
animation-timing-function: var(--bounce);
}
Got 20 elements? That's 20 selectors you need to write. Adding a new element? Then you also need to grow the selector that applies the animation styles. Not exactly scalable.
The view-transition-class
can be used in the view transition pseudo-elements to apply the same style rule.
#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
#card5 { view-transition-name: card5; }
…
#card20 { view-transition-name: card20; }
#cards-wrapper > div {
view-transition-class: card;
}
html::view-transition-group(.card) {
animation-timing-function: var(--bounce);
}
The following cards example leverages the previous CSS snippet. All cards–including newly added ones–get the same timing applied with one selector: html::view-transition-group(.card)
.
Debug transitions
Since view transitions are built on top of CSS animations, the Animations panel in Chrome DevTools is great for debugging transitions.
Using the Animations panel, you can pause the next animation, then scrub back and forth through the animation. During this, the transition pseudo-elements can be found in the Elements panel.
Transitioning elements don't need to be the same DOM element
So far we've used view-transition-name
to create separate transition elements for the header, and the text in the header. These are conceptually the same element before and after the DOM change, but you can create transitions where that isn't the case.
For example, the main video embed can be given a view-transition-name
:
.full-embed {
view-transition-name: full-embed;
}
Then, when the thumbnail is clicked, it can be given the same view-transition-name
, just for the duration of the transition:
thumbnail.onclick = async () => {
thumbnail.style.viewTransitionName = 'full-embed';
document.startViewTransition(() => {
thumbnail.style.viewTransitionName = '';
updateTheDOMSomehow();
});
};
And the result:
The thumbnail now transitions into the main image. Even though they're conceptually (and literally) different elements, the transition API treats them as the same thing because they shared the same view-transition-name
.
The real code for this transition is a little more complicated than the preceding example, as it also handles the transition back to the thumbnail page. See the source for the full implementation.
Custom entry and exit transitions
এই উদাহরণ দেখুন:
The sidebar is part of the transition:
.sidebar {
view-transition-name: sidebar;
}
But, unlike the header in the previous example, the sidebar doesn't appear on all pages. If both states have the sidebar, the transition pseudo-elements look like this:
::view-transition
├─ …other transition groups…
└─ ::view-transition-group(sidebar)
└─ ::view-transition-image-pair(sidebar)
├─ ::view-transition-old(sidebar)
└─ ::view-transition-new(sidebar)
However, if the sidebar is only on the new page, the ::view-transition-old(sidebar)
pseudo-element won't be there. Since there's no 'old' image for the sidebar, the image-pair will only have a ::view-transition-new(sidebar)
. Similarly, if the sidebar is only on the old page, the image-pair will only have a ::view-transition-old(sidebar)
.
In the previous demo, the sidebar transitions differently depending on whether it's entering, exiting, or present in both states. It enters by sliding from the right and fading in, it exits by sliding to the right and fading out, and it stays in place when it's present in both states.
To create specific entry and exit transitions, you can use the :only-child
pseudo-class to target the old or new pseudo-elements when it's the only child in the image-pair:
/* Entry transition */
::view-transition-new(sidebar):only-child {
animation: 300ms cubic-bezier(0, 0, 0.2, 1) both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Exit transition */
::view-transition-old(sidebar):only-child {
animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
}
In this case, there's no specific transition for when the sidebar is present in both states, since the default is perfect.
Async DOM updates, and waiting for content
The callback passed to .startViewTransition()
can return a promise, which allows for async DOM updates, and waiting for important content to be ready.
document.startViewTransition(async () => {
await something;
await updateTheDOMSomehow();
await somethingElse;
});
The transition won't be started until the promise fulfills. During this time, the page is frozen, so delays here should be kept to a minimum. Specifically, network fetches should be done before calling .startViewTransition()
, while the page is still fully interactive, rather than doing them as part of the .startViewTransition()
callback.
If you decide to wait for images or fonts to be ready, be sure to use an aggressive timeout:
const wait = ms => new Promise(r => setTimeout(r, ms));
document.startViewTransition(async () => {
updateTheDOMSomehow();
// Pause for up to 100ms for fonts to be ready:
await Promise.race([document.fonts.ready, wait(100)]);
});
However, in some cases it's better to avoid the delay altogether, and use the content you already have.
Make the most of content you already have
In the case where the thumbnail transitions to a larger image:
The default transition is to cross-fade, which means the thumbnail could be cross-fading with a not-yet-loaded full image.
One way to handle this is to wait for the full image to load before starting the transition. Ideally this would be done before calling .startViewTransition()
, so the page remains interactive, and a spinner can be shown to indicate to the user that things are loading. But in this case there's a better way:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
}
Now the thumbnail doesn't fade away, it just sits underneath the full image. This means if the new view hasn't loaded, the thumbnail is visible throughout the transition. This means the transition can start straight away, and the full image can load in its own time.
This wouldn't work if the new view featured transparency, but in this case we know it doesn't, so we can make this optimization.
Handle changes in aspect ratio
Conveniently, all the transitions so far have been to elements with the same aspect ratio, but that won't always be the case. What if the thumbnail is 1:1, and the main image is 16:9?
In the default transition, the group animates from the before size to the after size. The old and new views are 100% width of the group, and auto height, meaning they keep their aspect ratio regardless of the group's size.
This is a good default, but it isn't what is wanted in this case. তাই:
::view-transition-old(full-embed),
::view-transition-new(full-embed) {
/* Prevent the default animation,
so both views remain opacity:1 throughout the transition */
animation: none;
/* Use normal blending,
so the new view sits on top and obscures the old view */
mix-blend-mode: normal;
/* Make the height the same as the group,
meaning the view size might not match its aspect-ratio. */
height: 100%;
/* Clip any overflow of the view */
overflow: clip;
}
/* The old view is the thumbnail */
::view-transition-old(full-embed) {
/* Maintain the aspect ratio of the view,
by shrinking it to fit within the bounds of the element */
object-fit: contain;
}
/* The new view is the full image */
::view-transition-new(full-embed) {
/* Maintain the aspect ratio of the view,
by growing it to cover the bounds of the element */
object-fit: cover;
}
This means the thumbnail stays in the center of the element as the width expands, but the full image 'un-crops' as it transitions from 1:1 to 16:9.
For more detailed information, check out View transitions: Handling aspect ratio changes
Use media queries to change transitions for different device states
You may want to use different transitions on mobile versus desktop, such as this example which performs a full slide from the side on mobile, but a more subtle slide on desktop:
This can be achieved using regular media queries:
/* Transitions for mobile */
::view-transition-old(root) {
animation: 300ms ease-out both full-slide-to-left;
}
::view-transition-new(root) {
animation: 300ms ease-out both full-slide-from-right;
}
@media (min-width: 500px) {
/* Overrides for larger displays.
This is the shared axis transition from earlier in the article. */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
}
You may also want to change which elements you assign a view-transition-name
depending on matching media queries.
React to the 'reduced motion' preference
Users can indicate they prefer reduced motion through their operating system, and that preference is exposed in CSS .
You could chose to prevent any transitions for these users:
@media (prefers-reduced-motion) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
However, a preference for 'reduced motion' doesn't mean the user wants no motion . Instead of the preceding snippet, you could choose a more subtle animation, but one that still expresses the relationship between elements, and the flow of data.
Handle multiple view transition styles with view transition types
ব্রাউজার সমর্থন
Sometimes a transition from one particular view to another should have a specifically tailored transition. For example, when going to the next or to the previous page in a pagination sequence, you might want to slide the contents in a different direction depending on whether you are going to a higher page or a lower page from the sequence.
For this you can use view transition types, which allow you to assign one or more types to an active view transition. For example, when transitioning to a higher page in a pagination sequence use the forwards
type and when going to a lower page use the backwards
type. These types are only active when capturing or performing a transition, and each type can be customized through CSS to use different animations.
To use types in a same-document view transition, you pass types
into the startViewTransition
method. To allow this, document.startViewTransition
also accepts an object: update
is the callback function that updates the DOM, and types
is an array with the types.
const direction = determineBackwardsOrForwards();
const t = document.startViewTransition({
update: updateTheDOMSomehow,
types: ['slide', direction],
});
To respond to these types, use the :active-view-transition-type()
selector. Pass the type
you want to target into the selector. This lets you keep the styles of multiple view transitions separated from each other, without the declarations of the one interfering with the declarations of the other.
Because types only apply when capturing or performing the transition, you can use the selector to set–or unset–a view-transition-name
on an element only for the view transition with that type.
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only (using the default root snapshot) */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
In the following pagination demo , the page contents slide forwards or backwards based on the page number that you are navigating to. The types are determined on click upon which they get passed into document.startViewTransition
.
To target any active view transition, regardless of the type, you can use the :active-view-transition
pseudo-class selector instead.
html:active-view-transition {
…
}
Handle multiple view transition styles with a class name on the view transition root
Sometimes a transition from one particular type of view to another should have a specifically tailored transition. Or, a 'back' navigation should be different to a 'forward' navigation.
Before transition types the way to handle these cases was to temporarily set a class name on the transition root. When calling document.startViewTransition
, this transition root is the <html>
element, accessible using document.documentElement
in JavaScript:
if (isBackNavigation) {
document.documentElement.classList.add('back-transition');
}
const transition = document.startViewTransition(() =>
updateTheDOMSomehow(data)
);
try {
await transition.finished;
} finally {
document.documentElement.classList.remove('back-transition');
}
To remove the classes after the transition finishes, this example uses transition.finished
, a promise that resolves once the transition has reached its end state. Other properties of this object are covered in the API reference .
Now you can use that class name in your CSS to change the transition:
/* 'Forward' transitions */
::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms
cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* Overrides for 'back' transitions */
.back-transition::view-transition-old(root) {
animation-name: fade-out, slide-to-right;
}
.back-transition::view-transition-new(root) {
animation-name: fade-in, slide-from-left;
}
As with media queries, the presence of these classes could also be used to change which elements get a view-transition-name
.
Run transitions without freezing other animations
Take a look at this demo of a video transitioning position:
Did you see anything wrong with it? Don't worry if you didn't. Here it is slowed right down:
During the transition, the video appears to freeze, then the playing version of the video fades in. This is because the ::view-transition-old(video)
is a screenshot of the old view, whereas the ::view-transition-new(video)
is a live image of the new view.
You can fix this, but first, ask yourself if it's worth fixing. If you didn't see the 'problem' when the transition was playing at its normal speed, I wouldn't bother changing it.
If you really want to fix it, then don't show the ::view-transition-old(video)
; switch straight to the ::view-transition-new(video)
. You can do this by overriding the default styles and animations:
::view-transition-old(video) {
/* Don't show the frozen old view */
display: none;
}
::view-transition-new(video) {
/* Don't fade the new view in */
animation: none;
}
আর এটাই!
Now the video plays throughout the transition.
Integration with the Navigation API (and other frameworks)
View transitions are specified in such a way that they can be integrated with other frameworks or libraries. For example, if your single-page application (SPA) is using a router, you can adjust the router's update mechanism to update the content using a view transition.
In the following code snippet taken from this pagination demo the Navigation API's interception handler is adjusted to call document.startViewTransition
when view transitions are supported.
navigation.addEventListener("navigate", (e) => {
// Don't intercept if not needed
if (shouldNotIntercept(e)) return;
// Intercept the navigation
e.intercept({
handler: async () => {
// Fetch the new content
const newContent = await fetchNewContent(e.destination.url, {
signal: e.signal,
});
// The UA does not support View Transitions, or the UA
// already provided a Visual Transition by itself (e.g. swipe back).
// In either case, update the DOM directly
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
// Update the content using a View Transition
const t = document.startViewTransition(() => {
setContent(newContent);
});
}
});
});
Some, but not all, browsers provide their own transition when the user performs a swipe gesture to navigate. In that case you shouldn't trigger your own view transition as it would lead to a poor or confusing user experience. The user would see two transitions—one provided by the browser and the other one by you—running in succession.
Therefore, it is recommended to prevent a view transition from starting when the browser has provided its own visual transition. To achieve this, check the value of the hasUAVisualTransition
property of the NavigateEvent
instance. The property is set to true
when the browser has provided a visual transition. This hasUIVisualTransition
property also exists on PopStateEvent
instances.
In the previous snippet the check that determines whether to run the view transition takes this property into account. When there is no support for same-document view transitions or when the browser already provided its own transition, the view transition is skipped.
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
In the following recording, the user swipes to navigate back to the previous page. The capture on the left doesn't include a check for the hasUAVisualTransition
flag. The recording on the right does include the check, thereby skipping the manual view transition because the browser provided a visual transition.
Animating with JavaScript
So far, all the transitions have been defined using CSS, but sometimes CSS isn't enough:
A couple of parts of this transition can't be achieved with CSS alone:
- The animation starts from the click location.
- The animation ends with the circle having a radius to the farthest corner. Although, hopefully this will be possible with CSS in future .
Thankfully, you can create transitions using the Web Animation API !
let lastClick;
addEventListener('click', event => (lastClick = event));
function spaNavigate(data) {
// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
// Get the click position, or fallback to the middle of the screen
const x = lastClick?.clientX ?? innerWidth / 2;
const y = lastClick?.clientY ?? innerHeight / 2;
// Get the distance to the furthest corner
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);
// With a transition:
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
});
// Wait for the pseudo-elements to be created:
transition.ready.then(() => {
// Animate the root's new view
document.documentElement.animate(
{
clipPath: [
`circle(0 at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
],
},
{
duration: 500,
easing: 'ease-in',
// Specify which pseudo-element to animate
pseudoElement: '::view-transition-new(root)',
}
);
});
}
This example uses transition.ready
, a promise that resolves once the transition pseudo-elements have been successfully created. Other properties of this object are covered in the API reference .
Transitions as an enhancement
The View Transition API is designed to 'wrap' a DOM change and create a transition for it. However, the transition should be treated as an enhancement, as in, your app shouldn't enter an 'error' state if the DOM change succeeds, but the transition fails. Ideally the transition shouldn't fail, but if it does, it shouldn't break the rest of the user experience.
In order to treat transitions as an enhancement, take care not to use transition promises in a way that would cause your app to throw if the transition fails.
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); }
The problem with this example is that switchView()
will reject if the transition cannot reach a ready
state, but that doesn't mean that the view failed to switch. The DOM may have successfully updated, but there were duplicate view-transition-name
s, so the transition was skipped.
পরিবর্তে:
async function switchView(data) { // Fallback for browsers that don't support this API: if (!document.startViewTransition) { await updateTheDOM(data); return; } const transition = document.startViewTransition(async () => { await updateTheDOM(data); }); animateFromMiddle(transition); await transition.updateCallbackDone; } async function animateFromMiddle(transition) { try { await transition.ready; document.documentElement.animate( { clipPath: [`inset(50%)`, `inset(0)`], }, { duration: 500, easing: 'ease-in', pseudoElement: '::view-transition-new(root)', } ); } catch (err) { // You might want to log this error, but it shouldn't break the app } }
This example uses transition.updateCallbackDone
to wait for the DOM update, and to reject if it fails. switchView
no longer rejects if the transition fails, it resolves when the DOM update completes, and rejects if it fails.
If you want switchView
to resolve when the new view has 'settled', as in, any animated transition has completed or skipped to the end, replace transition.updateCallbackDone
with transition.finished
.
Not a polyfill, but…
This is not an easy feature to polyfill. However, this helper function makes things much easier in browsers that don't support view transitions:
function transitionHelper({
skipTransition = false,
types = [],
update,
}) {
const unsupported = (error) => {
const updateCallbackDone = Promise.resolve(update()).then(() => {});
return {
ready: Promise.reject(Error(error)),
updateCallbackDone,
finished: updateCallbackDone,
skipTransition: () => {},
types,
};
}
if (skipTransition || !document.startViewTransition) {
return unsupported('View Transitions are not supported in this browser');
}
try {
const transition = document.startViewTransition({
update,
types,
});
return transition;
} catch (e) {
return unsupported('View Transitions with types are not supported in this browser');
}
}
And it can be used like this:
function spaNavigate(data) {
const types = isBackNavigation ? ['back-transition'] : [];
const transition = transitionHelper({
update() {
updateTheDOMSomehow(data);
},
types,
});
// …
}
In browsers that don't support view transitions, updateDOM
will still be called, but there won't be an animated transition.
You can also provide some classNames
to add to <html>
during the transition, making it easier to change the transition depending on the type of navigation .
You can also pass true
to skipTransition
if you don't want an animation, even in browsers that support view transitions. This is useful if your site has a user preference to disable transitions.
Working with frameworks
If you're working with a library or framework that abstracts away DOM changes, the tricky part is knowing when the DOM change is complete. Here's a set of examples, using the helper above , in various frameworks.
- React —the key here is
flushSync
, which applies a set of state changes synchronously. Yes, there's a big warning about using that API, but Dan Abramov assures me it's appropriate in this case. As usual with React and async code, when using the various promises returned bystartViewTransition
, take care that your code is running with the correct state. - Vue.js —the key here is
nextTick
, which fulfills once the DOM has been updated. - Svelte —very similar to Vue, but the method to await the next change is
tick
. - Lit —the key here is the
this.updateComplete
promise within components, which fulfills once the DOM has been updated. - Angular —the key here is
applicationRef.tick
, which flushes pending DOM changes. As of Angular version 17 you can usewithViewTransitions
that comes with@angular/router
.
API রেফারেন্স
-
const viewTransition = document.startViewTransition(update)
Start a new
ViewTransition
.update
is a function that is called once the current state of the document is captured.Then, when the promise returned by
updateCallback
fulfills, the transition begins in the next frame. If the promise returned byupdateCallback
rejects, the transition is abandoned.-
const viewTransition = document.startViewTransition({ update, types })
Start a new
ViewTransition
with the specified typesupdate
is called once the current state of the document is captured.types
sets the active types for the transition when capturing or performing the transition. It is initially empty. SeeviewTransition.types
further down for more information.
Instance members of ViewTransition
:
-
viewTransition.updateCallbackDone
A promise that fulfills when the promise returned by
updateCallback
fulfills, or rejects when it rejects.The View Transition API wraps a DOM change and creates a transition. However, sometimes you don't care about the success or failure of the transition animation, you just want to know if and when the DOM change happens.
updateCallbackDone
is for that use-case.-
viewTransition.ready
A promise that fulfills once the pseudo-elements for the transition are created, and the animation is about to start.
It rejects if the transition cannot begin. This can be due to misconfiguration, such as duplicate
view-transition-name
s, or ifupdateCallback
returns a rejected promise.This is useful for animating the transition pseudo-elements with JavaScript .
-
viewTransition.finished
A promise that fulfills once the end state is fully visible and interactive to the user.
It only rejects if
updateCallback
returns a rejected promise, as this indicates the end state wasn't created.Otherwise, if a transition fails to begin, or is skipped during the transition, the end state is still reached, so
finished
fulfills.-
viewTransition.types
A
Set
-like object that holds the active view transition's types. To manipulate the entries, use its instance methodsclear()
,add()
, anddelete()
.To respond to a specific type in CSS, use the
:active-view-transition-type(type)
pseudo-class selector on the transition root.Types automatically get cleaned up when the view transition finishes.
-
viewTransition.skipTransition()
Skip the animation part of the transition.
This won't skip calling
updateCallback
, as the DOM change is separate to the transition.
Default style and transition reference
-
::view-transition
- The root pseudo-element which fills the viewport and contains each
::view-transition-group
. -
::view-transition-group
Absolutely positioned.
Transitions
width
andheight
between the 'before' and 'after' states.Transitions
transform
between the 'before' and 'after' viewport-space quad.-
::view-transition-image-pair
Absolutely positioned to fill the group.
Has
isolation: isolate
to limit the effect of themix-blend-mode
on the old and new views.-
::view-transition-new
and::view-transition-old
Absolutely positioned to the top-left of the wrapper.
Fills 100% of the group width, but has an auto height, so it will maintain its aspect ratio rather than filling the group.
Has
mix-blend-mode: plus-lighter
to allow for a true cross-fade.The old view transitions from
opacity: 1
toopacity: 0
. The new view transitions fromopacity: 0
toopacity: 1
.
প্রতিক্রিয়া
Developer feedback is always appreciated. To do so, file an issue with the CSS Working Group on GitHub with suggestions and questions. Prefix your issue with [css-view-transitions]
.
Should you run into a bug, then file a Chromium bug instead.