বিল্ডিং পারফরম্যান্ট প্রসারিত & অ্যানিমেশন পতন

স্টিফেন ম্যাকগ্রুর
Stephen McGruer

টিএল; ডিআর

ক্লিপ অ্যানিমেটিং করার সময় স্কেল ট্রান্সফর্ম ব্যবহার করুন। অ্যানিমেশনের সময় বাচ্চাদের পাল্টা স্কেলিং করে প্রসারিত এবং তির্যক হওয়া থেকে আপনি আটকাতে পারেন।

পূর্বে আমরা কীভাবে পারফরম্যান্ট প্যারালাক্স ইফেক্ট এবং অসীম স্ক্রলার তৈরি করতে হয় সে সম্পর্কে আপডেট পোস্ট করেছি। এই পোস্টে, আপনি যদি পারফরম্যান্ট ক্লিপ অ্যানিমেশন চান তবে আমরা কী জড়িত তা দেখতে যাচ্ছি। আপনি যদি একটি ডেমো দেখতে চান, নমুনা UI উপাদান GitHub রেপো দেখুন।

উদাহরণস্বরূপ, একটি প্রসারিত মেনু নিন:

এটি নির্মাণের জন্য কিছু বিকল্প অন্যদের তুলনায় আরো কর্মক্ষম।

খারাপ: একটি ধারক উপাদানের প্রস্থ এবং উচ্চতা অ্যানিমেটিং

আপনি কন্টেইনার উপাদানের প্রস্থ এবং উচ্চতা অ্যানিমেট করতে কিছুটা CSS ব্যবহার করে কল্পনা করতে পারেন।

.menu {
  overflow: hidden;
  width: 350px;
  height: 600px;
  transition: width 600ms ease-out, height 600ms ease-out;
}

.menu--collapsed {
  width: 200px;
  height: 60px;
}

এই পদ্ধতির সাথে তাত্ক্ষণিক সমস্যা হল যে এটির width এবং height অ্যানিমেটিং প্রয়োজন। এই বৈশিষ্ট্যগুলির জন্য লেআউট গণনা করা প্রয়োজন, এবং অ্যানিমেশনের প্রতিটি ফ্রেমে ফলাফলগুলি আঁকতে হবে, যা খুব ব্যয়বহুল হতে পারে এবং সাধারণত আপনি 60fps মিস করতে পারেন৷ যদি এটি আপনার কাছে খবর হয় তবে আমাদের রেন্ডারিং পারফরম্যান্স গাইডগুলি পড়ুন, যেখানে আপনি রেন্ডারিং প্রক্রিয়া কীভাবে কাজ করে সে সম্পর্কে আরও তথ্য পেতে পারেন৷

খারাপ: CSS ক্লিপ বা ক্লিপ-পাথ বৈশিষ্ট্য ব্যবহার করুন

width এবং height অ্যানিমেট করার একটি বিকল্প হতে পারে প্রসারিত এবং পতন প্রভাবকে অ্যানিমেট করতে (এখন-বঞ্চিত) clip বৈশিষ্ট্য ব্যবহার করা। অথবা, আপনি যদি পছন্দ করেন, আপনি পরিবর্তে clip-path ব্যবহার করতে পারেন। যদিও clip-path ব্যবহার করা clip চেয়ে কম সমর্থিত । কিন্তু clip অবহেলিত। ঠিক। তবে হতাশ হবেন না, এটি এমন সমাধান নয় যা আপনি চেয়েছিলেন!

.menu {
  position: absolute;
  clip: rect(0px 112px 175px 0px);
  transition: clip 600ms ease-out;
}

.menu--collapsed {
  clip: rect(0px 70px 34px 0px);
}

মেনু উপাদানটির width এবং height অ্যানিমেট করার চেয়ে ভাল হলেও, এই পদ্ধতির নেতিবাচক দিক হল এটি এখনও পেইন্টকে ট্রিগার করে। এছাড়াও clip প্রপার্টি, আপনি যদি সেই পথে যেতে চান, এটি যে উপাদানটিতে কাজ করছে তা হয় একেবারে বা স্থির অবস্থানে থাকা প্রয়োজন, যার জন্য একটু অতিরিক্ত ঝগড়ার প্রয়োজন হতে পারে।

ভালো: অ্যানিমেটিং স্কেল

যেহেতু এই প্রভাবের সাথে কিছু বড় এবং ছোট হওয়া জড়িত, আপনি একটি স্কেল রূপান্তর ব্যবহার করতে পারেন। এটি একটি দুর্দান্ত খবর কারণ রূপান্তরগুলি পরিবর্তন করার জন্য এমন কিছু যা লেআউট বা পেইন্টের প্রয়োজন হয় না, এবং যা ব্রাউজার GPU-তে হস্তান্তর করতে পারে, যার অর্থ প্রভাবটি ত্বরান্বিত হয় এবং উল্লেখযোগ্যভাবে 60fps আঘাত করার সম্ভাবনা বেশি৷

এই পদ্ধতির নেতিবাচক দিক, রেন্ডারিং পারফরম্যান্সের বেশিরভাগ জিনিসের মতো, এটির জন্য কিছুটা সেট আপ প্রয়োজন। এটা পুরোপুরি মূল্য, যদিও!

ধাপ 1: শুরু এবং শেষ অবস্থা গণনা করুন

স্কেল অ্যানিমেশন ব্যবহার করে এমন একটি পদ্ধতির সাথে, প্রথম ধাপ হল এমন উপাদানগুলি পড়া যা আপনাকে বলে যে মেনুটি ভেঙে পড়ার সময় এবং এটি প্রসারিত হওয়ার সময় উভয়ই হতে হবে। এটা হতে পারে যে কিছু পরিস্থিতিতে আপনি এই দুটি বিট তথ্য এক সাথে পেতে পারেন না, এবং উপাদানটির বিভিন্ন অবস্থা পড়তে সক্ষম হওয়ার জন্য আপনাকে - বলুন - কিছু ক্লাস টগল করতে হবে। আপনি যদি এটি করতে চান, তবে, সতর্ক থাকুন: getBoundingClientRect() (অথবা offsetWidth এবং offsetHeight ) ব্রাউজারকে শৈলী এবং লেআউট পাস চালানোর জন্য বাধ্য করে যদি শৈলীগুলি শেষ চালানোর পর থেকে পরিবর্তিত হয়।

function calculateCollapsedScale () {
    // The menu title can act as the marker for the collapsed state.
    const collapsed = menuTitle.getBoundingClientRect();

    // Whereas the menu as a whole (title plus items) can act as
    // a proxy for the expanded state.
    const expanded = menu.getBoundingClientRect();
    return {
    x: collapsed.width / expanded.width,
    y: collapsed.height / expanded.height
    };
}

একটি মেনুর মতো কিছুর ক্ষেত্রে, আমরা যুক্তিসঙ্গত অনুমান করতে পারি যে এটি তার প্রাকৃতিক স্কেল (1, 1) থেকে শুরু হবে। এই প্রাকৃতিক স্কেলটি তার প্রসারিত অবস্থার প্রতিনিধিত্ব করে, যার অর্থ আপনাকে একটি স্কেল ডাউন সংস্করণ (যা উপরে গণনা করা হয়েছিল) থেকে সেই প্রাকৃতিক স্কেল পর্যন্ত ব্যাক আপ করতে হবে।

কিন্তু অপেক্ষা করো! অবশ্যই এটি মেনুর বিষয়বস্তুকেও স্কেল করবে, তাই না? ভাল, আপনি নীচে দেখতে পারেন, হ্যাঁ.

তাই আপনি এই সম্পর্কে কি করতে পারেন? ঠিক আছে আপনি বিষয়বস্তুতে একটি পাল্টা- রূপান্তর প্রয়োগ করতে পারেন, তাই উদাহরণস্বরূপ যদি কন্টেইনারটি তার স্বাভাবিক আকারের 1/5 ভাগে স্কেল করা হয়, তাহলে আপনি বিষয়বস্তুগুলিকে 5x পর্যন্ত স্ক্যাল করতে পারেন যাতে বিষয়বস্তুগুলিকে স্কোয়াশ করা না হয়। এটি সম্পর্কে লক্ষ্য করার জন্য দুটি জিনিস রয়েছে:

  1. কাউন্টার-ট্রান্সফর্মও একটি স্কেল অপারেশন । এটি ভাল কারণ এটিও ত্বরান্বিত হতে পারে, ঠিক কন্টেইনারে অ্যানিমেশনের মতো। আপনাকে নিশ্চিত করতে হতে পারে যে অ্যানিমেটেড উপাদানগুলি তাদের নিজস্ব কম্পোজিটর স্তর পায় (জিপিইউকে সাহায্য করতে সক্ষম করে), এবং এর জন্য আপনি will-change: transform বা, যদি আপনার পুরানো ব্রাউজারগুলিকে সমর্থন করতে হয় তবে backface-visiblity: hidden

  2. কাউন্টার-ট্রান্সফর্ম প্রতি ফ্রেমে গণনা করা আবশ্যক। এখানেই জিনিসগুলি একটু জটিল হয়ে উঠতে পারে, কারণ ধরে নিই যে অ্যানিমেশনটি CSS-এ রয়েছে এবং একটি ইজিং ফাংশন ব্যবহার করে, কাউন্টার-ট্রান্সফর্ম অ্যানিমেট করার সময় ইজিং নিজেই কাউন্টার করা দরকার। যাইহোক, — বলুন — cubic-bezier(0, 0, 0.3, 1) এর বিপরীত বক্ররেখা গণনা করা এতটা স্পষ্ট নয়।

জাভাস্ক্রিপ্ট ব্যবহার করে প্রভাবকে অ্যানিমেট করার বিষয়টি বিবেচনা করা লোভনীয় হতে পারে। সর্বোপরি, আপনি প্রতি ফ্রেমে স্কেল এবং কাউন্টার-স্কেল মান গণনা করতে একটি সহজ সমীকরণ ব্যবহার করতে পারেন। যেকোন জাভাস্ক্রিপ্ট-ভিত্তিক অ্যানিমেশনের নেতিবাচক দিক হল যখন প্রধান থ্রেড (যেখানে আপনার জাভাস্ক্রিপ্ট চলে) অন্য কোনও কাজে ব্যস্ত থাকে তখন কী ঘটে। সংক্ষিপ্ত উত্তর হল যে আপনার অ্যানিমেশন তোতলাতে পারে বা সম্পূর্ণভাবে থামতে পারে, যা UX এর জন্য দুর্দান্ত নয়।

ধাপ 2: ফ্লাইতে CSS অ্যানিমেশন তৈরি করুন

সমাধান, যা প্রথমে অদ্ভুত বলে মনে হতে পারে, তা হল আমাদের নিজস্ব ইজিং ফাংশনের সাথে গতিশীলভাবে একটি কীফ্রেমযুক্ত অ্যানিমেশন তৈরি করা এবং মেনু দ্বারা ব্যবহারের জন্য এটিকে পৃষ্ঠায় ইনজেক্ট করা। (এটি নির্দেশ করার জন্য ক্রোম ইঞ্জিনিয়ার রবার্ট ফ্ল্যাককে অনেক ধন্যবাদ!) এর প্রাথমিক সুবিধা হল একটি কীফ্রেমযুক্ত অ্যানিমেশন যা রূপান্তরকে পরিবর্তন করে তা কম্পোজিটরে চালানো যেতে পারে, যার অর্থ এটি মূল থ্রেডের কাজগুলির দ্বারা প্রভাবিত হয় না।

কীফ্রেম অ্যানিমেশন তৈরি করতে, আমরা 0 থেকে 100 পর্যন্ত ধাপ করি এবং উপাদান এবং এর বিষয়বস্তুর জন্য কোন স্কেল মানগুলির প্রয়োজন হবে তা গণনা করি। এগুলিকে তারপর একটি স্ট্রিংয়ে ফুটিয়ে তোলা যেতে পারে, যা একটি শৈলী উপাদান হিসাবে পৃষ্ঠায় ইনজেকশন করা যেতে পারে। শৈলীগুলি ইনজেকশনের ফলে পৃষ্ঠায় একটি পুনঃগণনা শৈলী পাস হবে, যা ব্রাউজারকে করতে হবে এমন অতিরিক্ত কাজ, কিন্তু উপাদানটি বুট করার সময় এটি শুধুমাত্র একবারই করবে৷

function createKeyframeAnimation () {
    // Figure out the size of the element when collapsed.
    let {x, y} = calculateCollapsedScale();
    let animation = '';
    let inverseAnimation = '';

    for (let step = 0; step <= 100; step++) {
    // Remap the step value to an eased one.
    let easedStep = ease(step / 100);

    // Calculate the scale of the element.
    const xScale = x + (1 - x) * easedStep;
    const yScale = y + (1 - y) * easedStep;

    animation += `${step}% {
        transform: scale(${xScale}, ${yScale});
    }`;

    // And now the inverse for the contents.
    const invXScale = 1 / xScale;
    const invYScale = 1 / yScale;
    inverseAnimation += `${step}% {
        transform: scale(${invXScale}, ${invYScale});
    }`;

    }

    return `
    @keyframes menuAnimation {
    ${animation}
    }

    @keyframes menuContentsAnimation {
    ${inverseAnimation}
    }`;
}

অন্তহীনভাবে কৌতূহলীরা ফর-লুপের ভিতরে ease() ফাংশন সম্পর্কে ভাবতে পারে। আপনি 0 থেকে 1 পর্যন্ত একটি সহজ সমতুল্য মান মানচিত্র করতে এই মত কিছু ব্যবহার করতে পারেন.

function ease (v, pow=4) {
  return 1 - Math.pow(1 - v, pow);
}

আপনি Google অনুসন্ধান ব্যবহার করতে পারেন যে প্লট কি মত দেখায় . সুবিধাজনক ! আপনার যদি অন্যান্য সহজীকরণ সমীকরণের প্রয়োজন হয়, তাহলে Soledad Penadés-এর Tween.js দেখুন, যাতে সেগুলির সম্পূর্ণ গাদা রয়েছে।

ধাপ 3: CSS অ্যানিমেশন সক্রিয় করুন

এই অ্যানিমেশনগুলি জাভাস্ক্রিপ্টে পৃষ্ঠায় তৈরি এবং বেক আউট করার সাথে, চূড়ান্ত পদক্ষেপ হল অ্যানিমেশনগুলি সক্ষম করার জন্য ক্লাসগুলি টগল করা৷

.menu--expanded {
  animation-name: menuAnimation;
  animation-duration: 0.2s;
  animation-timing-function: linear;
}

.menu__contents--expanded {
  animation-name: menuContentsAnimation;
  animation-duration: 0.2s;
  animation-timing-function: linear;
}

এটি পূর্ববর্তী ধাপে তৈরি করা অ্যানিমেশনগুলিকে চালানোর কারণ করে। যেহেতু বেকড অ্যানিমেশনগুলি ইতিমধ্যেই সহজ করা হয়েছে, টাইমিং ফাংশনটি linear সেট করা দরকার অন্যথায় আপনি প্রতিটি কীফ্রেমের মধ্যে সহজ হবেন যা দেখতে খুব অদ্ভুত হবে!

যখন উপাদানটিকে নিচের দিকে ভেঙে ফেলার কথা আসে, তখন দুটি বিকল্প রয়েছে: সিএসএস অ্যানিমেশন আপডেট করুন যাতে সামনের দিকে না গিয়ে বিপরীত দিকে চালানো যায়। এটি ঠিক কাজ করবে, কিন্তু অ্যানিমেশনের "অনুভূতি" বিপরীত হবে, তাই আপনি যদি একটি সহজ-আউট বক্ররেখা ব্যবহার করেন, তাহলে বিপরীতটি সহজবোধ করবে, যা এটিকে অলস বোধ করবে৷ একটি আরও উপযুক্ত সমাধান হল উপাদানটি ভেঙে পড়ার জন্য একটি দ্বিতীয় জোড়া অ্যানিমেশন তৈরি করা। এগুলি প্রসারিত কীফ্রেম অ্যানিমেশনগুলির মতো ঠিক একইভাবে তৈরি করা যেতে পারে, তবে অদলবদল করা শুরু এবং শেষ মানগুলির সাথে।

const xScale = 1 + (x - 1) * easedStep;
const yScale = 1 + (y - 1) * easedStep;

একটি আরও উন্নত সংস্করণ: বিজ্ঞপ্তি প্রকাশ করে

বৃত্তাকার প্রসারিত এবং সংকুচিত অ্যানিমেশন তৈরি করতে এই কৌশলটি ব্যবহার করাও সম্ভব।

নীতিগুলি মূলত পূর্ববর্তী সংস্করণের মতোই, যেখানে আপনি একটি উপাদানকে স্কেল করেন এবং তার অবিলম্বে শিশুদের পাল্টা-স্কেল করেন। এই ক্ষেত্রে, যে উপাদানটি স্কেল করা হচ্ছে তার border-radius 50% আছে, এটিকে বৃত্তাকার করে, এবং অন্য একটি উপাদান দ্বারা মোড়ানো হয় যা overflow: hidden , যার অর্থ আপনি উপাদান সীমার বাইরে বৃত্তটি প্রসারিত দেখতে পাচ্ছেন না।

এই বিশেষ বৈকল্পিক সম্পর্কে সতর্কতার একটি শব্দ: অ্যানিমেশনের সময় কম DPI স্ক্রিনে Chrome-এর টেক্সট স্কেল এবং কাউন্টার-স্কেলের কারণে রাউন্ডিং ত্রুটির কারণে অস্পষ্ট পাঠ্য রয়েছে। আপনি যদি এটির জন্য বিশদ বিবরণে আগ্রহী হন তবে একটি বাগ ফাইল করা হয়েছে যা আপনি তারকা এবং অনুসরণ করতে পারেন

বৃত্তাকার প্রসারিত প্রভাবের কোডটি গিটহাব রেপোতে পাওয়া যাবে।

উপসংহার

সুতরাং আপনার কাছে এটি রয়েছে, স্কেল ট্রান্সফর্ম ব্যবহার করে পারফরম্যান্স ক্লিপ অ্যানিমেশন করার একটি উপায়। একটি নিখুঁত বিশ্বে, ক্লিপ অ্যানিমেশনগুলিকে ত্বরান্বিত করা দেখতে খুব ভাল হবে (জেক আর্কিবাল্ডের তৈরি একটি ক্রোমিয়াম বাগ রয়েছে), কিন্তু আমরা সেখানে না পৌঁছানো পর্যন্ত, clip বা clip-path অ্যানিমেটিং করার সময় আপনার সতর্ক হওয়া উচিত এবং অবশ্যই অ্যানিমেটিং এড়ানো উচিত width বা height

এই ধরনের প্রভাবগুলির জন্য ওয়েব অ্যানিমেশনগুলি ব্যবহার করাও সুবিধাজনক হবে, কারণ তাদের একটি জাভাস্ক্রিপ্ট API আছে কিন্তু আপনি যদি শুধুমাত্র transform এবং opacity অ্যানিমেট করেন তবে কম্পোজিটর থ্রেডে চলতে পারে। দুর্ভাগ্যবশত, ওয়েব অ্যানিমেশনগুলির জন্য সমর্থন দুর্দান্ত নয় , যদিও সেগুলি উপলব্ধ থাকলে আপনি সেগুলি ব্যবহার করার জন্য প্রগতিশীল বর্ধন ব্যবহার করতে পারেন৷

if ('animate' in HTMLElement.prototype) {
    // Animate with Web Animations.
} else {
    // Fall back to generated CSS Animations or JS.
}

এটি পরিবর্তন না হওয়া পর্যন্ত, আপনি অ্যানিমেশন করার জন্য জাভাস্ক্রিপ্ট-ভিত্তিক লাইব্রেরি ব্যবহার করতে পারেন , আপনি একটি CSS অ্যানিমেশন বেক করে এবং পরিবর্তে এটি ব্যবহার করে আরও নির্ভরযোগ্য কর্মক্ষমতা পেতে পারেন। সমানভাবে, যদি আপনার অ্যাপ ইতিমধ্যেই তার অ্যানিমেশনের জন্য জাভাস্ক্রিপ্টের উপর নির্ভর করে, তাহলে আপনার বিদ্যমান কোডবেসের সাথে অন্তত সামঞ্জস্যপূর্ণ হয়ে আপনাকে আরও ভাল পরিবেশন করা যেতে পারে।

আপনি যদি এই প্রভাবের জন্য কোডটি দেখতে চান তবে UI এলিমেন্ট স্যাম্পলস গিটহাব রেপোটি দেখুন এবং সর্বদা হিসাবে, নীচের মন্তব্যগুলিতে আপনি কীভাবে পাবেন তা আমাদের জানান।