স্ক্রল করার সময় প্যারালাক্স এফেক্ট তৈরি করা

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

কীভাবে প্রয়োগ করতে হয়

কীভাবে একটি প্রাথমিক প্যারালাক্স এফেক্ট তৈরি করতে হয় তা এখানে দেওয়া হল:

  1. র‌্যাপার এলিমেন্ট তৈরি করুন: এই এলিমেন্টটি প্যারালাক্স এফেক্টের সব লেয়ারকে একসাথে গ্রুপ করে। এটি স্ক্রল করার মতো এলিমেন্ট নয়, তাই এর ওভারফ্লো ক্লিপ করা উচিত। এছাড়াও, এটিকে এমন একটি height দিন যা প্যারালাক্স এফেক্টের কোনও একটি লেয়ারের উচ্চতার সাথে মেলে।

    <div class="wrapper">
      …
    </div>
    
    .wrapper {
      overflow: clip;
      height: 100vh; /* Height of one of the layers of the parallax */
    }
    
  2. লেয়ার ঘোষণা করুন: র‍্যাপারের মধ্যে, আলাদা আলাদা লেয়ার যোগ করুন যা বিভিন্ন স্পিডে সরবে।

    <div class="wrapper">
      <div class="layer">LAYER 0</div>
      <div class="layer">LAYER 1</div>
      <div class="layer">LAYER 2</div>
      …
    </div>
    
  3. ট্রান্সলেট অ্যানিমেশন যোগ করুন: CSS অ্যানিমেশন নির্ধারণ করুন যা লেয়ারের transform প্রপার্টি পরিবর্তন করে। প্যারালাক্স এফেক্টের জন্য, আপনি সাধারণত লেয়ারগুলি উল্লম্বভাবে সরানোর জন্য translateY ব্যবহার করবেন।

    @keyframes parallax {
      from {
        transform: translateY(700px);
      }
    }
    
  4. view-timeline সেট-আপ করুন: স্ক্রল পজিশনে অ্যানিমেশন লিঙ্ক করতে, র‍্যাপার এলিমেন্টে view-timeline তৈরি করুন এবং তারপর লেয়ারে এটি প্রয়োগ করুন।

    .wrapper {
      view-timeline: --wrapper;
    }
    
    .layer {
      animation: parallax linear both;
      animation-timeline: --wrapper;
    }
    
  5. অ্যানিমেশন স্ট্যাগার করা: লেয়ারগুলিকে বিভিন্ন গতিতে সরানোর জন্য, আপনি দুটি প্রধান পদ্ধতির মধ্যে একটি ব্যবহার করতে পারেন: কীফ্রেমে স্ট্যাগারিং, অথবা animation-range স্ট্যাগারিং।

    এই দুটি পদ্ধতিই হার্ডকোড করা ভ্যালু ব্যবহার করতে পারে অথবা sibling-index()/sibling-count() প্রয়োগ করতে পারে। হার্ডকোড করা ভ্যালু সবচেয়ে সহজ এবং সীমিত সংখ্যক লেয়ার থাকলে এটি উপযোগী। আপনার অনেক লেয়ার থাকলে sibling-index()/sibling-count() প্রয়োগ করা সুবিধাজনক।

    • কীফ্রেমে স্ট্যাগারিং:

      হার্ডকোড করা ভ্যালু ব্যবহার করে, আপনি প্রতিটি লেয়ারের জন্য একটি কাস্টম প্রপার্টি নির্ধারণ করতে পারেন যাতে ম্যানুয়ালি এর প্যারালাক্স অফসেট কন্ট্রোল করা যায়।

      .layer:nth-child(1) { --offset: 100px; }
      .layer:nth-child(2) { --offset: 200px; }
      .layer:nth-child(3) { --offset: 300px; }
      
      @keyframes parallax {
        from {
          transform: translateY(var(--offset));
        }
      }
      

      sibling-index() ব্যবহার করে, sibling-index() ফাংশনকে তার সিবলিংয়ের মধ্যে চাইল্ড এলিমেন্টের ইন্ডেক্স ফিরিয়ে দিতে দিন যাতে স্ট্যাগার্ড এফেক্ট অটোমেটিক গণনা করা যায়।

      @keyframes parallax {
        from {
          transform: translateY(calc(100px * sibling-index()));
        }
      }
      
    • animation-range-এর স্ট্যাগারিং:

      হার্ডকোড করা ভ্যালু ব্যবহার করে, আপনি প্রতিটি লেয়ারে আলাদাভাবে animation-range-এর সীমা স্পষ্টভাবে নির্ধারণ করতে পারবেন।

      .layer:nth-child(1) { animation-range: entry 25% exit 50%; }
      .layer:nth-child(2) { animation-range: entry 25% exit 75%; }
      .layer:nth-child(3) { animation-range: entry 25% exit 100%; }
      

      sibling-index() এবং sibling-count() ব্যবহার করে, আপনি মোট লেয়ারের (sibling-count()) সংখ্যার উপর ভিত্তি করে গাণিতিকভাবে রেঞ্জ গণনা করতে পারবেন।

      .layer {
        animation-range: entry 25% exit calc(100% / sibling-count() * sibling-index());
      }
      

কোডের উদাহরণ

@keyframes parallax {
  from {
    transform: translateY(calc(100px * sibling-index()));
  }
}

.wrapper {
  view-timeline: --wrapper;
}

.layer {
  animation: parallax linear both;
  animation-timeline: --wrapper;
}

@media (prefers-reduced-motion: reduce) {
  .layer {
    animation: none;
  }
}

বিকল্প হিসেবে, একই ধরনের প্রভাব পেতে আপনি animation-range প্রপার্টি ব্যবহার করতে পারেন:

@keyframes parallax {
  from {
    transform: translateY(700px);
  }
}

.wrapper {
  view-timeline: --wrapper;
}

.layer {
  animation: parallax linear both;
  animation-timeline: --wrapper;
  animation-range: entry 25% exit calc(100% / sibling-count() * sibling-index());
}

@media (prefers-reduced-motion: reduce) {
  .layer {
    animation: none;
  }
}

পেশাদার পদ্ধতি

স্ক্রল-চালিত অ্যানিমেশন ব্যবহার করার সময়, মসৃণ ও অ্যাক্সেসযোগ্য অভিজ্ঞতা নিশ্চিত করতে কয়েকটি পেশাদার পদ্ধতি অনুসরণ করা গুরুত্বপূর্ণ:

  • ফিচার শনাক্তকরণ অন্তর্ভুক্ত করুন: সব ব্রাউজার স্ক্রল-চালিত অ্যানিমেশন সাপোর্ট করে না। সাপোর্ট চেক করতে @supports ((animation-timeline: view()) and (animation-range: entry)) ব্যবহার করুন এবং যেসব ব্রাউজারে এটি কাজ করে না সেগুলির জন্য একটি ফলব্যাক প্রদান করুন।
    • শুধুমাত্র আংশিক সহায়তা প্রদানকারী ব্রাউজার ফিল্টার করতে, এখানে (animation-range: entry) চেক অবশ্যই অন্তর্ভুক্ত করতে হবে।
    • ফলব্যাক স্ট্র্যাটেজির জন্য scroll-timeline-polyfill প্যাকেজ ব্যবহার করবেন না কারণ এটিতে সব ফিচার নেই এবং অনেক পরিচিত সমস্যা আছে।
    • অ্যানিমেশনটি যদি শুধুমাত্র আলংকারিক হিসেবে বিবেচিত হয়, তাহলে প্রোগ্রেসিভ এনহ্যান্সমেন্ট বেছে নিন এবং ফলব্যাক প্রদান করবেন না।
  • ব্যবহারকারীর পছন্দকে সম্মান করুন: কিছু ব্যবহারকারী ওয়েবে কম মোশন পছন্দ করেন। এইসব ব্যবহারকারীর জন্য আপনার অ্যানিমেশন বন্ধ করতে বা কমাতে prefers-reduced-motion মিডিয়া কোয়েরি ব্যবহার করুন।
  • শুধুমাত্র পারফর্ম্যান্ট CSS প্রপার্টি অ্যানিমেট করার চেষ্টা করুন: সবচেয়ে মসৃণ অ্যানিমেশনের জন্য, ব্রাউজারের কম্পোজিটর থ্রেড দ্বারা ম্যানেজ করা যায় এমন প্রপার্টি অ্যানিমেট করুন, যেমন transform এবং opacity। width বা height-এর মতো অন্যান্য প্রপার্টি অ্যানিমেট করলে পারফর্ম্যান্স সংক্রান্ত সমস্যা হতে পারে।
  • সঠিক ডিক্লারেশন অর্ডার ব্যবহার করুন: animation শর্টহ্যান্ড প্রপার্টি ব্যবহার করার সময়, শর্টহ্যান্ড যাতে টাইমলাইন রিসেট না করে দেয় তার জন্য animation-timeline এবং animation-range এর পরে ডিক্লেয়ার করুন।

animation-range সেট করার জন্য:

  • সব লেয়ারে একই স্টার্ট অফসেট দিন, যেমন entry 25%
  • অবশ্যই সব লেয়ারে আলাদা এন্ড অফসেট দিন যা অফসেট ডিস্ট্রিবিউট করার জন্য sibling-count() ও sibling-index() ব্যবহার করে, যেমন exit calc(100% / sibling-count() * sibling-index())।

ব্রাউজার সাপোর্ট ও ফলব্যাক স্ট্র্যাটেজি

তাই, সাধারণত একটি ফলব্যাক স্ট্র্যাটেজি প্রয়োজন হয়।

যেসব ব্রাউজারে স্ক্রল-চালিত অ্যানিমেশন কাজ করে না, সেখানে ভিজ্যুয়াল এফেক্ট আবার তৈরি করতে আপনি ফলব্যাক ব্যবহার করতে পারেন। ফলব্যাক সাধারণত স্ক্রল লিসেনার (ScrollTimeline এফেক্টের জন্য) বা IntersectionObserver API (ViewTimeline এফেক্টের জন্য) দিয়ে তৈরি করা হয়।

স্ক্রল-চালিত অ্যানিমেশনের জন্য বিল্ট-ইন সাপোর্ট সহ ব্রাউজারে, সর্বদা নেটিভ CSS ইমপ্লিমেন্টেশন ব্যবহার করুন কারণ সেগুলি আরও বেশি পারফর্ম্যান্স দেয়।

মনে রাখবেন, ফলব্যাক অ্যাপ্রোচ ব্যবহার করে সব এফেক্ট আবার তৈরি করা যায় না।

বিশেষ করে এই ব্যবহারের ক্ষেত্রে, স্ক্রল-চালিত অ্যানিমেশন কাজ করে না এমন ব্রাউজারের জন্য নিম্নলিখিত স্ক্রিপ্ট ফলব্যাক প্রয়োগ করে। এটি .wrapper এলিমেন্টের দৃশ্যমানতা ট্র্যাক করতে IntersectionObserver ব্যবহার করে এবং স্ক্রল পজিশনের উপর ভিত্তি করে লেয়ারের transform প্রপার্টি আপডেট করে।

// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: view()) and (animation-range: entry)')) {
  const wrapper = document.querySelector('.wrapper');
  const layers = document.querySelectorAll('.layer');

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        window.addEventListener('scroll', onScroll);
      } else {
        window.removeEventListener('scroll', onScroll);
      }
    });
  }, { threshold: 0 });

  observer.observe(wrapper);

  function onScroll() {
    const scrollY = window.scrollY;
    const wrapperRect = wrapper.getBoundingClientRect();
    const wrapperTop = wrapperRect.top + scrollY;
    const wrapperHeight = wrapperRect.height;
    const windowHeight = window.innerHeight;

    if (scrollY >= wrapperTop - windowHeight && scrollY <= wrapperTop + wrapperHeight) {
      const scrollPercent = (scrollY - (wrapperTop - windowHeight)) / (wrapperHeight + windowHeight);
      
      layers.forEach((layer, index) => {
        // This matches the effect as defined in the CSS example above.
        // Customize this further if needed.
        const initialTranslateY = 100 * index;
        const translateY = initialTranslateY * (1 - scrollPercent);
        layer.style.transform = `translateY(${translateY}px)`;
      });
    }
  }

  // Trigger onScroll once to set initial positions
  onScroll();
}