ভিউ ট্রানজিশন API-এর সাথে মসৃণ এবং সহজ রূপান্তর

ব্রাউজার সমর্থন

  • 111
  • 111
  • এক্স
  • এক্স

উৎস

ভিউ ট্রানজিশন এপিআই দুই রাজ্যের মধ্যে একটি অ্যানিমেটেড ট্রানজিশন তৈরি করার সময় একক ধাপে DOM পরিবর্তন করা সহজ করে তোলে। এটি Chrome 111+ এ উপলব্ধ৷

ভিউ ট্রানজিশন API দিয়ে ট্রানজিশন তৈরি করা হয়েছে। ডেমো সাইট ব্যবহার করে দেখুন - Chrome 111+ প্রয়োজন৷

কেন আমরা এই বৈশিষ্ট্য প্রয়োজন?

পৃষ্ঠা রূপান্তরগুলি কেবল দুর্দান্ত দেখায় না, তারা প্রবাহের দিকটিও যোগাযোগ করে এবং এটি স্পষ্ট করে যে কোন উপাদানগুলি পৃষ্ঠা থেকে পৃষ্ঠায় সম্পর্কিত। এমনকি ডেটা আনার সময়ও এগুলি ঘটতে পারে, যার ফলে কার্যক্ষমতার দ্রুত উপলব্ধি হয়।

কিন্তু, আমাদের ওয়েবে ইতিমধ্যেই অ্যানিমেশন টুল রয়েছে, যেমন CSS ট্রানজিশন , CSS অ্যানিমেশন এবং ওয়েব অ্যানিমেশন API , তাহলে জিনিসগুলিকে ঘুরিয়ে দেওয়ার জন্য কেন আমাদের একটি নতুন জিনিস দরকার?

সত্য হল, রাষ্ট্রীয় রূপান্তর কঠিন, এমনকি আমাদের কাছে ইতিমধ্যে থাকা সরঞ্জামগুলির সাথেও।

এমনকি একটি সাধারণ ক্রস-ফেডের মতো কিছুতে উভয় রাজ্য একই সময়ে উপস্থিত থাকা জড়িত। এটি ব্যবহারযোগ্যতার চ্যালেঞ্জগুলি উপস্থাপন করে, যেমন বহির্গামী উপাদানে অতিরিক্ত মিথস্ক্রিয়া পরিচালনা করা। এছাড়াও, সহায়ক ডিভাইস ব্যবহারকারীদের জন্য, এমন একটি সময়কাল রয়েছে যেখানে আগের এবং পরে উভয় অবস্থাই একই সময়ে DOM-এ থাকে এবং জিনিসগুলি গাছের চারপাশে এমনভাবে ঘোরাফেরা করতে পারে যা দৃশ্যত ভাল, তবে সহজেই পড়ার অবস্থান এবং ফোকাস করতে পারে ঘুচা.

রাজ্যের পরিবর্তনগুলি পরিচালনা করা বিশেষভাবে চ্যালেঞ্জিং যদি দুটি রাজ্য স্ক্রোল অবস্থানে পৃথক হয়। এবং, যদি একটি উপাদান এক পাত্র থেকে অন্য পাত্রে চলে যায়, তাহলে আপনি overflow: hidden এবং অন্যান্য ধরণের ক্লিপিং, যার অর্থ আপনি যে প্রভাব চান তা পেতে আপনাকে আপনার CSS পুনর্গঠন করতে হবে।

এটা অসম্ভব নয়, এটা সত্যিই কঠিন

ভিউ ট্রানজিশনগুলি আপনাকে একটি সহজ উপায় দেয়, আপনাকে রাজ্যগুলির মধ্যে কোনও ওভারল্যাপ ছাড়াই আপনার DOM পরিবর্তন করার অনুমতি দিয়ে, কিন্তু স্ন্যাপশট করা ভিউ ব্যবহার করে রাজ্যগুলির মধ্যে একটি ট্রানজিশন অ্যানিমেশন তৈরি করে৷

উপরন্তু, যদিও বর্তমান বাস্তবায়ন একক পৃষ্ঠার অ্যাপস (এসপিএ) লক্ষ্য করে, এই বৈশিষ্ট্যটি সম্পূর্ণ পৃষ্ঠা লোডের মধ্যে স্থানান্তরের অনুমতি দেওয়ার জন্য প্রসারিত করা হবে, যা বর্তমানে অসম্ভব।

স্ট্যান্ডার্ডাইজেশন স্ট্যাটাস

ফিচারটি ডব্লিউ3সি সিএসএস ওয়ার্কিং গ্রুপের মধ্যে একটি খসড়া স্পেসিফিকেশন হিসেবে তৈরি করা হচ্ছে।

একবার আমরা API ডিজাইনের সাথে খুশি হলে, আমরা এই বৈশিষ্ট্যটিকে স্থিতিশীল করার জন্য প্রয়োজনীয় প্রক্রিয়াগুলি এবং চেকগুলি শুরু করব৷

বিকাশকারীর প্রতিক্রিয়া সত্যিই গুরুত্বপূর্ণ, তাই অনুগ্রহ করে পরামর্শ এবং প্রশ্ন সহ গিটহাবে সমস্যাগুলি ফাইল করুন

সহজতম রূপান্তর: একটি ক্রস-ফেইড

ডিফল্ট ভিউ ট্রানজিশন একটি ক্রস-ফেড, তাই এটি 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 , একটি ক্রস-ফেড তৈরি করে।

সমস্ত অ্যানিমেশন সিএসএস অ্যানিমেশন ব্যবহার করে সঞ্চালিত হয়, তাই সেগুলি সিএসএস দিয়ে কাস্টমাইজ করা যায়।

সহজ কাস্টমাইজেশন

উপরের সমস্ত ছদ্ম-উপাদানগুলিকে সিএসএস দিয়ে টার্গেট করা যেতে পারে এবং যেহেতু অ্যানিমেশনগুলি সিএসএস ব্যবহার করে সংজ্ঞায়িত করা হয়েছে, আপনি বিদ্যমান সিএসএস অ্যানিমেশন বৈশিষ্ট্যগুলি ব্যবহার করে সেগুলি সংশোধন করতে পারেন৷ উদাহরণ স্বরূপ:

::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)
   └─ …

কিন্তু আবার, শুধু ডিফল্টের সাথে যাচ্ছে:

স্লাইডিং হেডার টেক্সট। ন্যূনতম ডেমোউৎস .

এখন হেডিং টেক্সট পিছনের বোতামের জন্য জায়গা তৈরি করতে একটু সন্তোষজনক স্লাইড করে।

ডিবাগিং ট্রানজিশন

যেহেতু ভিউ ট্রানজিশনগুলি 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;
  }
}

যাইহোক, 'কমানো গতি' এর জন্য একটি পছন্দের মানে এই নয় যে ব্যবহারকারী কোনো গতি চায় না। উপরের পরিবর্তে, আপনি একটি আরও সূক্ষ্ম অ্যানিমেশন চয়ন করতে পারেন, তবে একটি যা এখনও উপাদান এবং ডেটা প্রবাহের মধ্যে সম্পর্ক প্রকাশ করে।

নেভিগেশন ধরনের উপর নির্ভর করে ট্রানজিশন পরিবর্তন করা

কখনও কখনও একটি নির্দিষ্ট ধরনের পৃষ্ঠা থেকে অন্য একটি নেভিগেশন একটি বিশেষভাবে উপযোগী রূপান্তর থাকা উচিত। অথবা, একটি 'ব্যাক' নেভিগেশন একটি 'ফরোয়ার্ড' নেভিগেশন থেকে ভিন্ন হওয়া উচিত।

'ফিরে' যাওয়ার সময় বিভিন্ন পরিবর্তন। ন্যূনতম ডেমোউৎস .

এই কেসগুলি পরিচালনা করার সর্বোত্তম উপায় হল <html> এ একটি ক্লাস নাম সেট করা, যা নথি উপাদান হিসাবেও পরিচিত:

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;
}

এবং এটাই!

ভিডিও রূপান্তর, ধীর। ন্যূনতম ডেমোউৎস .

এখন ভিডিওটি ট্রানজিশন জুড়ে চলে।

জাভাস্ক্রিপ্ট দিয়ে অ্যানিমেটিং

এখন পর্যন্ত, সমস্ত রূপান্তর 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,
  classNames = [],
  updateDOM,
}) {
  if (skipTransition || !document.startViewTransition) {
    const updateCallbackDone = Promise.resolve(updateDOM()).then(() => {});

    return {
      ready: Promise.reject(Error('View transitions unsupported')),
      updateCallbackDone,
      finished: updateCallbackDone,
      skipTransition: () => {},
    };
  }

  document.documentElement.classList.add(...classNames);

  const transition = document.startViewTransition(updateDOM);

  transition.finished.finally(() =>
    document.documentElement.classList.remove(...classNames)
  );

  return transition;
}

এবং এটি এই মত ব্যবহার করা যেতে পারে:

function spaNavigate(data) {
  const classNames = isBackNavigation ? ['back-transition'] : [];

  const transition = transitionHelper({
    classNames,
    updateDOM() {
      updateTheDOMSomehow(data);
    },
  });

  // …
}

যেসব ব্রাউজারে ভিউ ট্রানজিশন সমর্থন করে না, সেখানে 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(updateCallback)

একটি নতুন ViewTransition শুরু করুন।

নথির বর্তমান অবস্থা ক্যাপচার করা হলে updateCallback বলা হয়।

তারপর, যখন updateCallback দ্বারা প্রত্যাবর্তিত প্রতিশ্রুতি পূরণ হয়, তখন পরবর্তী ফ্রেমে রূপান্তর শুরু হয়। updateCallback দ্বারা প্রত্যাবর্তিত প্রতিশ্রুতি প্রত্যাখ্যান করলে, রূপান্তর পরিত্যক্ত হয়।

ViewTransition সদস্যদের উদাহরণ:

viewTransition.updateCallbackDone

একটি প্রতিশ্রুতি যা পরিপূর্ণ হয় যখন updateCallback দ্বারা প্রত্যাবর্তিত প্রতিশ্রুতি পূর্ণ হয়, বা প্রত্যাখ্যান করলে তা প্রত্যাখ্যান করে।

ভিউ ট্রানজিশন এপিআই একটি DOM পরিবর্তন মোড়ানো এবং একটি ট্রানজিশন তৈরি করে। যাইহোক, কখনও কখনও আপনি ট্রানজিশন অ্যানিমেশনের সাফল্য/ব্যর্থতার বিষয়ে চিন্তা করেন না, আপনি শুধু জানতে চান যে DOM পরিবর্তন কখন ঘটবে। updateCallbackDone সেই ব্যবহারের ক্ষেত্রে।

viewTransition.ready

একটি প্রতিশ্রুতি যা পূর্ণ হয় একবার রূপান্তরের জন্য ছদ্ম-উপাদানগুলি তৈরি হয়ে গেলে এবং অ্যানিমেশন শুরু হতে চলেছে৷

রূপান্তর শুরু করতে না পারলে এটি প্রত্যাখ্যান করে। এটি ভুল কনফিগারেশনের কারণে হতে পারে, যেমন ডুপ্লিকেট view-transition-name s, অথবা যদি updateCallback একটি প্রত্যাখ্যাত প্রতিশ্রুতি প্রদান করে।

এটি জাভাস্ক্রিপ্টের সাথে ট্রানজিশন সিউডো-এলিমেন্ট অ্যানিমেট করার জন্য দরকারী।

viewTransition.finished

একটি প্রতিশ্রুতি যা ব্যবহারকারীর কাছে শেষ অবস্থা সম্পূর্ণরূপে দৃশ্যমান এবং ইন্টারেক্টিভ হওয়ার পরে পূরণ করে।

এটি শুধুমাত্র প্রত্যাখ্যান করে যদি updateCallback একটি প্রত্যাখ্যাত প্রতিশ্রুতি প্রদান করে, কারণ এটি ইঙ্গিত করে যে শেষ অবস্থা তৈরি করা হয়নি।

অন্যথায়, যদি একটি ট্রানজিশন শুরু হতে ব্যর্থ হয়, বা ট্রানজিশনের সময় এড়িয়ে যাওয়া হয়, তাহলেও শেষ অবস্থায় পৌঁছে যায়, তাই finished হয়।

viewTransition.skipTransition()

ট্রানজিশনের অ্যানিমেশন অংশটি এড়িয়ে যান।

এটি updateCallback কলিং এড়িয়ে যাবে না, কারণ DOM পরিবর্তনটি ট্রানজিশনের জন্য আলাদা।

ডিফল্ট শৈলী এবং ট্রানজিশন রেফারেন্স

::view-transition
রুট ছদ্ম-উপাদান যা ভিউপোর্ট পূরণ করে এবং প্রতিটি ::view-transition-group ধারণ করে।
::view-transition-group

একেবারে অবস্থান.

'আগে' এবং 'পরে' অবস্থার মধ্যে width এবং height পরিবর্তন করে।

রূপান্তরগুলি 'আগে' এবং 'পরে' ভিউপোর্ট-স্পেস কোয়াডের মধ্যে transform

::view-transition-image-pair

গ্রুপ পূরণ করার জন্য একেবারে অবস্থান.

isolation: isolate পুরানো এবং নতুন ভিউতে plus-lighter ব্লেন্ড মোডের প্রভাব সীমিত করতে আইসোলেট করুন।

::view-transition-new এবং ::view-transition-old

একেবারে র‍্যাপারের উপরের-বামে অবস্থান।

গোষ্ঠীর প্রস্থের 100% পূরণ করে, কিন্তু একটি স্বয়ংক্রিয় উচ্চতা রয়েছে, তাই এটি গোষ্ঠীটি পূরণ করার পরিবর্তে তার আকৃতির অনুপাত বজায় রাখবে।

mix-blend-mode: plus-lighter

পুরানো ভিউ opacity: 1 opacity: 0 তে। নতুন ভিউ opacity: 0 থেকে opacity: 1

প্রতিক্রিয়া

এই পর্যায়ে বিকাশকারীর প্রতিক্রিয়া সত্যিই গুরুত্বপূর্ণ, তাই অনুগ্রহ করে পরামর্শ এবং প্রশ্ন সহ গিটহাবে সমস্যাগুলি ফাইল করুন