বিল্ডিং Chrometober!

এই ক্রোমটোবারে মজাদার এবং ভীতিকর টিপস এবং কৌশলগুলি ভাগ করার জন্য স্ক্রোলিং বইটি কীভাবে প্রাণবন্ত হয়েছে৷

Designcember থেকে অনুসরণ করে, আমরা কমিউনিটি এবং Chrome টিমের ওয়েব বিষয়বস্তু হাইলাইট এবং শেয়ার করার উপায় হিসেবে এই বছর আপনার জন্য Chrometober তৈরি করতে চেয়েছিলাম। Designcember কন্টেইনার ক্যোয়ারী ব্যবহার প্রদর্শন করেছে, কিন্তু এই বছর আমরা CSS স্ক্রোল-লিঙ্কড অ্যানিমেশন API প্রদর্শন করছি।

web.dev/chrometober-2022- এ স্ক্রলিং বইয়ের অভিজ্ঞতা দেখুন।

ওভারভিউ

প্রজেক্টের লক্ষ্য ছিল স্ক্রোল-লিঙ্কড অ্যানিমেশন এপিআই হাইলাইট করে একটি অদ্ভুত অভিজ্ঞতা প্রদান করা। কিন্তু, বাতিক থাকাকালীন, অভিজ্ঞতাটি প্রতিক্রিয়াশীল এবং অ্যাক্সেসযোগ্যও হওয়া দরকার। প্রজেক্টটি এপিআই পলিফিল পরীক্ষা করার একটি দুর্দান্ত উপায় যা সক্রিয় বিকাশে রয়েছে; যে, সেইসাথে সংমিশ্রণে বিভিন্ন কৌশল এবং সরঞ্জাম চেষ্টা করে। এবং সব একটি উত্সব হ্যালোইন থিম সঙ্গে!

আমাদের দলের গঠন এই মত দেখায়:

একটি স্ক্রলিটেলিং অভিজ্ঞতার খসড়া তৈরি করা

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

প্রকল্পের সাথে সম্পর্কিত বিভিন্ন ডুডল এবং স্ক্রিবল সহ একটি নোটবুক একটি ডেস্কে রয়েছে।

আমার প্রথম Google প্রকল্পকে একটি অপ্রত্যাশিত দিকে নিয়ে যাওয়ার সৃজনশীল স্বাধীনতা পাওয়াটা উত্তেজনাপূর্ণ ছিল। এটি একটি প্রাথমিক প্রোটোটাইপ ছিল কিভাবে একজন ব্যবহারকারী বিষয়বস্তুর মাধ্যমে নেভিগেট করতে পারে।

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

আমি যে ডেমোগুলি তৈরি করেছি তার মধ্যে একটি ছিল একটি 3D-CSS বই যেখানে আপনি স্ক্রোল করার সাথে সাথে পৃষ্ঠাগুলি উল্টে যায় এবং আমরা Chrometober এর জন্য যা চেয়েছিলাম তার জন্য এটি অনেক বেশি উপযুক্ত মনে হয়েছিল৷ স্ক্রোল-লিঙ্কড অ্যানিমেশন API সেই কার্যকারিতার জন্য একটি নিখুঁত অদলবদল। এটি scroll-snap সাথেও ভাল কাজ করে, যেমন আপনি দেখতে পাবেন!

প্রকল্পের জন্য আমাদের চিত্রকর, Tyler Reed , আমরা ধারণা পরিবর্তন করার সাথে সাথে নকশা পরিবর্তন করতে দুর্দান্ত ছিলেন। টাইলার তার প্রতি নিক্ষিপ্ত সমস্ত সৃজনশীল ধারণা গ্রহণ এবং সেগুলিকে জীবন্ত করার জন্য একটি দুর্দান্ত কাজ করেছিলেন। এটি একসাথে অনেক মজার চিন্তাভাবনা ছিল। আমরা কীভাবে এটি কাজ করতে চেয়েছিলাম তার একটি বড় অংশ ছিল বিচ্ছিন্ন ব্লকগুলিতে বিভক্ত বৈশিষ্ট্যগুলি। এইভাবে, আমরা সেগুলিকে দৃশ্যগুলিতে রচনা করতে পারি এবং তারপরে আমরা যা জীবন্ত এনেছি তা বাছাই করে বেছে নিতে পারি।

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

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

API এর সাথে পরিচিত হচ্ছেন

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

উচ্চ স্তরে, আপনি স্ক্রোল করার জন্য অ্যানিমেশন লিঙ্ক করতে এই API ব্যবহার করতে পারেন। এটা মনে রাখা গুরুত্বপূর্ণ যে আপনি স্ক্রলে একটি অ্যানিমেশন ট্রিগার করতে পারবেন না—এটি এমন কিছু যা পরে আসতে পারে। স্ক্রোল-লিঙ্কড অ্যানিমেশনগুলিও দুটি প্রধান বিভাগে পড়ে:

  1. যারা স্ক্রোল অবস্থানে প্রতিক্রিয়া.
  2. যারা তার স্ক্রলিং পাত্রে একটি উপাদান অবস্থান প্রতিক্রিয়া.

পরবর্তীটি তৈরি করতে, আমরা একটি animation-timeline সম্পত্তির মাধ্যমে প্রয়োগ করা একটি ViewTimeline ব্যবহার করি।

ViewTimeline ব্যবহার করে CSS-এ কেমন দেখায় তার একটি উদাহরণ এখানে দেওয়া হল:

.element-moving-in-viewport {
  view-timeline-name: foo;
  view-timeline-axis: block;
}

.element-scroll-linked {
  animation: rotate both linear;
  animation-timeline: foo;
  animation-delay: enter 0%;
  animation-end-delay: cover 50%;
}

@keyframes rotate {
 to {
   rotate: 360deg;
 }
}

আমরা view-timeline-name সহ একটি ViewTimeline তৈরি করি এবং এটির জন্য অক্ষ নির্ধারণ করি। এই উদাহরণে, block লজিক্যাল block বোঝায়। অ্যানিমেশনটি animation-timeline সম্পত্তির সাথে স্ক্রোল করার সাথে সংযুক্ত হয়। animation-delay এবং animation-end-delay (লেখার সময়) আমরা কীভাবে পর্যায়গুলি সংজ্ঞায়িত করি।

এই পর্যায়গুলি সেই পয়েন্টগুলিকে সংজ্ঞায়িত করে যেখানে অ্যানিমেশনটি তার স্ক্রলিং পাত্রে একটি উপাদানের অবস্থানের সাথে সংযুক্ত হওয়া উচিত। আমাদের উদাহরণে, আমরা বলছি অ্যানিমেশন শুরু করুন যখন উপাদানটি স্ক্রলিং কন্টেইনারে প্রবেশ করে ( enter 0% )। এবং এটি স্ক্রোলিং কন্টেইনারের 50% ( cover 50% ) ঢেকে গেলে শেষ করুন।

এখানে আমাদের কর্মের ডেমো:

আপনি ভিউপোর্টে চলমান উপাদানের সাথে একটি অ্যানিমেশন লিঙ্ক করতে পারেন। আপনি animation-timeline উপাদানটির view-timeline হিসাবে সেট করে এটি করতে পারেন। এটি তালিকা অ্যানিমেশনের মতো পরিস্থিতিগুলির জন্য ভাল। আচরণটি একই রকম যে আপনি IntersectionObserver ব্যবহার করে প্রবেশের সময় উপাদানগুলিকে অ্যানিমেট করতে পারেন।

element-moving-in-viewport {
  view-timeline-name: foo;
  view-timeline-axis: block;
  animation: scale both linear;
  animation-delay: enter 0%;
  animation-end-delay: cover 50%;
  animation-timeline: foo;
}

@keyframes scale {
  0% {
    scale: 0;
  }
}

এটির সাথে "মুভার" স্কেল বাড়ার সাথে সাথে এটি ভিউপোর্টে প্রবেশ করে, "স্পিনার" এর ঘূর্ণনকে ট্রিগার করে।

আমি পরীক্ষা করে যা পেয়েছি তা হল API স্ক্রোল-স্ন্যাপের সাথে খুব ভাল কাজ করে। ViewTimeline সাথে স্ক্রোল-স্ন্যাপ একটি বইতে পৃষ্ঠা ঘুরানোর জন্য একটি দুর্দান্ত ফিট হবে।

মেকানিক্সের প্রোটোটাইপিং

কিছু পরীক্ষা করার পরে, আমি একটি বইয়ের প্রোটোটাইপ কাজ করতে সক্ষম হয়েছি। বইয়ের পাতা উল্টাতে আপনি অনুভূমিকভাবে স্ক্রোল করুন।

ডেমোতে, আপনি ড্যাশ বর্ডার সহ হাইলাইট করা বিভিন্ন ট্রিগার দেখতে পাবেন।

মার্কআপটি কিছুটা এইরকম দেখাচ্ছে:

<body>
  <div class="book-placeholder">
    <ul class="book" style="--count: 7;">
      <li
        class="page page--cover page--cover-front"
        data-scroll-target="1"
        style="--index: 0;"
      >
        <div class="page__paper">
          <div class="page__side page__side--front"></div>
          <div class="page__side page__side--back"></div>
        </div>
      </li>
      <!-- Markup for other pages here -->
    </ul>
  </div>
  <div>
    <p>intro spacer</p>
  </div>
  <div data-scroll-intro>
    <p>scale trigger</p>
  </div>
  <div data-scroll-trigger="1">
    <p>page trigger</p>
  </div>
  <!-- Markup for other triggers here -->
</body>

আপনি স্ক্রোল করার সাথে সাথে বইয়ের পৃষ্ঠাগুলি উল্টে যায়, তবে স্ন্যাপ খোলা বা বন্ধ। এটি ট্রিগারগুলির স্ক্রোল-স্ন্যাপ প্রান্তিককরণের উপর নির্ভরশীল।

html {
  scroll-snap-type: x mandatory;
}

body {
  grid-template-columns: repeat(var(--trigger-count), auto);
  overflow-y: hidden;
  overflow-x: scroll;
  display: grid;
}

body > [data-scroll-trigger] {
  height: 100vh;
  width: clamp(10rem, 10vw, 300px);
}

body > [data-scroll-trigger] {
  scroll-snap-align: end;
}

এইবার, আমরা CSS-এ ViewTimeline সংযোগ করি না, কিন্তু JavaScript-এ ওয়েব অ্যানিমেশন API ব্যবহার করি। এটির অতিরিক্ত সুবিধা রয়েছে উপাদানগুলির একটি সেটের উপর লুপ করতে এবং প্রতিটি হাতে তৈরি করার পরিবর্তে আমাদের প্রয়োজনীয় ViewTimeline তৈরি করতে সক্ষম হওয়ার।

const triggers = document.querySelectorAll("[data-scroll-trigger]")

const commonProps = {
  delay: { phase: "enter", percent: CSS.percent(0) },
  endDelay: { phase: "enter", percent: CSS.percent(100) },
  fill: "both"
}

const setupPage = (trigger, index) => {
  const target = document.querySelector(
    `[data-scroll-target="${trigger.getAttribute("data-scroll-trigger")}"]`
  );

  const viewTimeline = new ViewTimeline({
    subject: trigger,
    axis: 'inline',
  });

  target.animate(
    [
      {
        transform: `translateZ(${(triggers.length - index) * 2}px)`
      },
      {
        transform: `translateZ(${(triggers.length - index) * 2}px)`,
        offset: 0.75
      },
      {
        transform: `translateZ(${(triggers.length - index) * -1}px)`
      }
    ],
    {
      timeline: viewTimeline,
      commonProps,
    }
  );
  target.querySelector(".page__paper").animate(
    [
      {
        transform: "rotateY(0deg)"
      },
      {
        transform: "rotateY(-180deg)"
      }
    ],
    {
      timeline: viewTimeline,
      commonProps,
    }
  );
};

const triggers = document.querySelectorAll('[data-scroll-trigger]')
triggers.forEach(setupPage);

প্রতিটি ট্রিগারের জন্য, আমরা একটি ViewTimeline তৈরি করি। তারপরে আমরা সেই ViewTimeline ব্যবহার করে ট্রিগারের সংশ্লিষ্ট পৃষ্ঠাটিকে অ্যানিমেট করি। এটি স্ক্রোল করার জন্য পৃষ্ঠার অ্যানিমেশনকে লিঙ্ক করে। আমাদের অ্যানিমেশনের জন্য, আমরা পৃষ্ঠার একটি উপাদানকে y-অক্ষে ঘুরিয়ে পৃষ্ঠাটি ঘুরিয়ে দিচ্ছি। এছাড়াও আমরা পৃষ্ঠাটিকে z-অক্ষে অনুবাদ করি যাতে এটি একটি বইয়ের মতো আচরণ করে।

এটা সব একসাথে নির্বাণ

একবার আমি বইটির মেকানিজম তৈরি করে ফেললে, আমি টাইলারের চিত্রগুলিকে জীবন্ত করার দিকে মনোনিবেশ করতে পারতাম।

অ্যাস্ট্রো

দলটি 2021 সালে ডিজাইনসেম্বারের জন্য Astro ব্যবহার করেছিল এবং আমি Chrometober-এর জন্য এটি আবার ব্যবহার করতে আগ্রহী। জিনিসগুলিকে উপাদানগুলিতে বিভক্ত করতে সক্ষম হওয়ার বিকাশকারীর অভিজ্ঞতা এই প্রকল্পের জন্য উপযুক্ত।

বই নিজেই একটি উপাদান. এটি পৃষ্ঠা উপাদানগুলির একটি সংগ্রহও। প্রতিটি পৃষ্ঠার দুটি দিক রয়েছে এবং তাদের ব্যাকড্রপ রয়েছে। একটি পৃষ্ঠার পাশের শিশুরা এমন উপাদান যা যোগ করা, সরানো এবং সহজে অবস্থান করা যায়।

একটি বই নির্মাণ

ব্লকগুলি পরিচালনা করা সহজ করা আমার জন্য গুরুত্বপূর্ণ ছিল। আমি দলের বাকিদের জন্য অবদান রাখা সহজ করতে চেয়েছিলাম।

একটি উচ্চ স্তরের পৃষ্ঠাগুলি একটি কনফিগারেশন অ্যারে দ্বারা সংজ্ঞায়িত করা হয়। অ্যারের প্রতিটি পৃষ্ঠা বস্তু একটি পৃষ্ঠার জন্য বিষয়বস্তু, ব্যাকড্রপ এবং অন্যান্য মেটাডেটা সংজ্ঞায়িত করে।

const pages = [
  {
    front: {
      marked: true,
      content: PageTwo,
      backdrop: spreadOne,
      darkBackdrop: spreadOneDark
    },
    back: {
      content: PageThree,
      backdrop: spreadTwo,
      darkBackdrop: spreadTwoDark
    },
    aria: `page 1`
  },
  /* Obfuscated page objects */
]

এই Book উপাদান পাস করা.

<Book pages={pages} />

Book উপাদান হল যেখানে স্ক্রলিং প্রক্রিয়া প্রয়োগ করা হয় এবং বইয়ের পৃষ্ঠাগুলি তৈরি করা হয়। প্রোটোটাইপ থেকে একই প্রক্রিয়া ব্যবহার করা হয়; কিন্তু আমরা বিশ্বব্যাপী তৈরি ViewTimeline একাধিক উদাহরণ শেয়ার করি।

window.CHROMETOBER_TIMELINES.push(viewTimeline);

এইভাবে, আমরা তাদের পুনরায় তৈরি করার পরিবর্তে অন্য কোথাও ব্যবহার করার জন্য টাইমলাইন শেয়ার করতে পারি। এই বিষয়ে পরে আরো.

পৃষ্ঠা রচনা

প্রতিটি পৃষ্ঠা একটি তালিকার মধ্যে একটি তালিকা আইটেম:

<ul class="book">
  {
    pages.map((page, index) => {
      const FrontSlot = page.front.content
      const BackSlot = page.back.content
      return (
        <Page
          index={index}
          cover={page.cover}
          aria={page.aria}
          backdrop={
            {
              front: {
                light: page.front.backdrop,
                dark: page.front.darkBackdrop
              },
              back: {
                light: page.back.backdrop,
                dark: page.back.darkBackdrop
              }
            }
          }>
          {page.front.content && <FrontSlot slot="front" />}    
          {page.back.content && <BackSlot slot="back" />}    
        </Page>
      )
    })
  }
</ul>

এবং সংজ্ঞায়িত কনফিগারেশন প্রতিটি Page উদাহরণে পাস হয়। পৃষ্ঠাগুলি প্রতিটি পৃষ্ঠায় বিষয়বস্তু সন্নিবেশ করার জন্য Astro এর স্লট বৈশিষ্ট্য ব্যবহার করে।

<li
  class={className}
  data-scroll-target={target}
  style={`--index:${index};`}
  aria-label={aria}
>
  <div class="page__paper">
    <div
      class="page__side page__side--front"
      aria-label={`Right page of ${index}`}
    >
      <picture>
        <source
          srcset={darkFront}
          media="(prefers-color-scheme: dark)"
          height="214"
          width="150"
        >
        <img
          src={lightFront}
          class="page__background page__background--right"
          alt=""
          aria-hidden="true"
          height="214"
          width="150"
        >
      </picture>
      <div class="page__content">
        <slot name="front" />
      </div>
    </div>
    <!-- Markup for back page -->
  </div>
</li>

এই কোডটি বেশিরভাগ কাঠামো সেট আপ করার জন্য। অবদানকারীরা এই কোডটি স্পর্শ না করেই বেশিরভাগ অংশের জন্য বইয়ের বিষয়বস্তুতে কাজ করতে পারে৷

ব্যাকড্রপ

একটি বইয়ের দিকে সৃজনশীল স্থানান্তরটি বিভাগগুলিকে বিভক্ত করা আরও সহজ করে তোলে এবং বইটির প্রতিটি স্প্রেড মূল নকশা থেকে নেওয়া একটি দৃশ্য।

পৃষ্ঠাটি বই থেকে চিত্র ছড়িয়েছে যাতে একটি কবরস্থানে একটি আপেল গাছ রয়েছে। কবরস্থানে একাধিক শিরোনাম রয়েছে এবং একটি বড় চাঁদের সামনে আকাশে একটি বাদুড় রয়েছে।

যেহেতু আমরা বইয়ের জন্য একটি আকৃতির অনুপাতের সিদ্ধান্ত নিয়েছিলাম, প্রতিটি পৃষ্ঠার ব্যাকড্রপে একটি ছবির উপাদান থাকতে পারে। সেই উপাদানটিকে 200% প্রস্থে সেট করা এবং পৃষ্ঠার পাশের উপর ভিত্তি করে object-position ব্যবহার করা কৌশলটি করে।

.page__background {
  height: 100%;
  width: 200%;
  object-fit: cover;
  object-position: 0 0;
  position: absolute;
  top: 0;
  left: 0;
}

.page__background--right {
  object-position: 100% 0;
}

পৃষ্ঠার বিষয়বস্তু

পৃষ্ঠাগুলির একটি তৈরি করা যাক। পৃষ্ঠা তিনে একটি পেঁচা যা একটি গাছে উঠে আসে।

কনফিগারেশনে সংজ্ঞায়িত হিসাবে এটি একটি PageThree উপাদান দিয়ে জনবহুল হয়। এটি একটি অ্যাস্ট্রো উপাদান ( PageThree.astro )। এই উপাদানগুলি দেখতে এইচটিএমএল ফাইলগুলির মতো তবে তাদের সামনের অংশের মতো শীর্ষে একটি কোড বেড়া রয়েছে৷ এটি আমাদের অন্যান্য উপাদান আমদানি করার মতো জিনিসগুলি করতে সক্ষম করে। পৃষ্ঠা তিনের উপাদানটি এইরকম দেখাচ্ছে:

---
import TreeOwl from '../TreeOwl/TreeOwl.astro'
import { contentBlocks } from '../../assets/content-blocks.json'
import ContentBlock from '../ContentBlock/ContentBlock.astro'
---
<TreeOwl/>
<ContentBlock {...contentBlocks[3]} id="four" />

<style is:global>
  .content-block--four {
    left: 30%;
    bottom: 10%;
  }
</style>

আবার, পৃষ্ঠাগুলি প্রকৃতিতে পরমাণু। তারা বৈশিষ্ট্য একটি সংগ্রহ থেকে নির্মিত হয়. পৃষ্ঠা তিনে একটি বিষয়বস্তু ব্লক এবং ইন্টারেক্টিভ পেঁচা রয়েছে, তাই প্রতিটির জন্য একটি উপাদান রয়েছে।

বিষয়বস্তু ব্লক হল বইয়ের মধ্যে দেখা সামগ্রীর লিঙ্ক। এগুলিও একটি কনফিগারেশন অবজেক্ট দ্বারা চালিত হয়।

{
 "contentBlocks": [
    {
      "id": "one",
      "title": "New in Chrome",
      "blurb": "Lift your spirits with a round up of all the tools and features in Chrome.",
      "link": "https://www.youtube.com/watch?v=qwdN1fJA_d8&list=PLNYkxOF6rcIDfz8XEA3loxY32tYh7CI3m"
    },
    …otherBlocks
  ]
}

এই কনফিগারেশনটি আমদানি করা হয় যেখানে সামগ্রী ব্লকের প্রয়োজন হয়। তারপর প্রাসঙ্গিক ব্লক কনফিগারেশন ContentBlock উপাদানে পাস হয়।

<ContentBlock {...contentBlocks[3]} id="four" />

আমরা কীভাবে পৃষ্ঠার উপাদানকে বিষয়বস্তুর অবস্থানের জায়গা হিসাবে ব্যবহার করি তার একটি উদাহরণও রয়েছে। এখানে, একটি বিষয়বস্তু ব্লক অবস্থান পায়।

<style is:global>
  .content-block--four {
    left: 30%;
    bottom: 10%;
  }
</style>

কিন্তু, একটি বিষয়বস্তু ব্লকের সাধারণ শৈলীগুলি উপাদান কোডের সাথে সহ-অবস্থিত থাকে।

.content-block {
  background: hsl(0deg 0% 0% / 70%);
  color: var(--gray-0);
  border-radius:  min(3vh, var(--size-4));
  padding: clamp(0.75rem, 2vw, 1.25rem);
  display: grid;
  gap: var(--size-2);
  position: absolute;
  cursor: pointer;
  width: 50%;
}

আমাদের পেঁচার জন্য, এটি একটি ইন্টারেক্টিভ বৈশিষ্ট্য - এই প্রকল্পের অনেকগুলির মধ্যে একটি। এটি একটি সুন্দর ছোট উদাহরণ যা দেখায় যে আমরা কীভাবে আমাদের তৈরি করা শেয়ার্ড ভিউটাইমলাইন ব্যবহার করেছি।

উচ্চ স্তরে, আমাদের পেঁচার উপাদান কিছু SVG আমদানি করে এবং Astro's Fragment ব্যবহার করে ইনলাইন করে।

---
import { default as Owl } from '../Features/Owl.svg?raw'
---
<Fragment set:html={Owl} />

এবং আমাদের পেঁচার অবস্থানের শৈলীগুলি উপাদান কোডের সাথে সহ-অবস্থিত।

.owl {
  width: 34%;
  left: 10%;
  bottom: 34%;
}

স্টাইলিংয়ের একটি অতিরিক্ত অংশ রয়েছে যা পেঁচার জন্য transform আচরণকে সংজ্ঞায়িত করে।

.owl__owl {
  transform-origin: 50% 100%;
  transform-box: fill-box;
}

transform-box ব্যবহার transform-origin প্রভাবিত করে। এটি এটিকে SVG-এর মধ্যে বস্তুর বাউন্ডিং বাক্সের সাথে আপেক্ষিক করে তোলে। পেঁচা নীচের কেন্দ্র থেকে উপরে উঠে আসে, তাই transform-origin: 50% 100%

মজার অংশ হল যখন আমরা পেঁচাটিকে আমাদের তৈরি করা ViewTimeline একটিতে লিঙ্ক করি:

const setUpOwl = () => {
   const owl = document.querySelector('.owl__owl');

   owl.animate([
     {
       translate: '0% 110%',
     },
     {
       translate: '0% 10%',
     },
   ], {
     timeline: CHROMETOBER_TIMELINES[1],
     delay: { phase: "enter", percent: CSS.percent(80) },
     endDelay: { phase: "enter", percent: CSS.percent(90) },
     fill: 'both' 
   });
 }

 if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches)
   setUpOwl()

কোডের এই ব্লকে, আমরা দুটি জিনিস করি:

  1. ব্যবহারকারীর গতি পছন্দ জন্য পরীক্ষা করুন.
  2. যদি তাদের কোন পছন্দ না থাকে, পেঁচার একটি অ্যানিমেশন স্ক্রোল করতে লিঙ্ক করুন।

দ্বিতীয় অংশের জন্য, পেঁচা ওয়েব অ্যানিমেশন API ব্যবহার করে y-অক্ষে অ্যানিমেট করে। স্বতন্ত্র রূপান্তর সম্পত্তি translate ব্যবহার করা হয়, এবং একটি ViewTimeline সাথে লিঙ্ক করা হয়। এটি timeline প্রপার্টির মাধ্যমে CHROMETOBER_TIMELINES[1] এর সাথে লিঙ্ক করা হয়েছে। এটি একটি ViewTimeline যা পৃষ্ঠার পরিবর্তনের জন্য তৈরি করা হয়। এটি enter ফেজ ব্যবহার করে পেঁচার অ্যানিমেশনকে পেজ টার্নের সাথে লিঙ্ক করে। এটি সংজ্ঞায়িত করে যে, যখন পৃষ্ঠাটি 80% ঘুরিয়ে দেওয়া হয়, তখন পেঁচাটিকে সরানো শুরু করুন। 90% এ, পেঁচা তার অনুবাদ শেষ করা উচিত।

বই বৈশিষ্ট্য

এখন আপনি একটি পৃষ্ঠা তৈরি করার পদ্ধতি এবং প্রকল্পের আর্কিটেকচার কীভাবে কাজ করে তা দেখেছেন। আপনি দেখতে পারেন যে এটি কীভাবে অবদানকারীদের তাদের পছন্দের একটি পৃষ্ঠা বা বৈশিষ্ট্যে ঝাঁপিয়ে পড়তে এবং কাজ করতে দেয়৷ বইয়ের বিভিন্ন বৈশিষ্ট্যের সাথে তাদের অ্যানিমেশনগুলি বইয়ের পৃষ্ঠা ঘুরানোর সাথে যুক্ত রয়েছে; উদাহরণস্বরূপ, ব্যাট যেটি উড়ে যায় এবং পাতায় ঘুরতে থাকে।

এটিতে এমন উপাদান রয়েছে যা CSS অ্যানিমেশন দ্বারা চালিত হয়।

একবার বিষয়বস্তু ব্লকগুলি বইয়ে ছিল, অন্যান্য বৈশিষ্ট্যগুলির সাথে সৃজনশীল হওয়ার সময় ছিল। এটি কিছু ভিন্ন মিথস্ক্রিয়া তৈরি করার এবং জিনিসগুলি বাস্তবায়নের বিভিন্ন উপায় চেষ্টা করার একটি সুযোগ প্রদান করে।

জিনিষ প্রতিক্রিয়াশীল রাখা

প্রতিক্রিয়াশীল ভিউপোর্ট ইউনিট বই এবং এর বৈশিষ্ট্যের আকার দেয়। যাইহোক, ফন্ট প্রতিক্রিয়াশীল রাখা একটি আকর্ষণীয় চ্যালেঞ্জ ছিল. কন্টেইনার ক্যোয়ারী ইউনিট এখানে একটি ভাল ফিট. যদিও তারা এখনও সব জায়গায় সমর্থিত নয়। বইয়ের আকার সেট করা হয়েছে, তাই আমাদের একটি কন্টেইনার কোয়েরির প্রয়োজন নেই। একটি ইনলাইন কন্টেইনার ক্যোয়ারী ইউনিট CSS calc() দিয়ে তৈরি করা যেতে পারে এবং ফন্ট সাইজিংয়ের জন্য ব্যবহার করা যেতে পারে।


.book-placeholder {
  --size: clamp(12rem, 72vw, 80vmin);
  --aspect-ratio: 360 / 504;
  --cqi: calc(0.01 * (var(--size) * (var(--aspect-ratio))));
}

.content-block h2 {
  color: var(--gray-0);
  font-size: clamp(0.6rem, var(--cqi) * 4, 1.5rem);
}

.content-block :is(p, a) {
  font-size: clamp(0.6rem, var(--cqi) * 3, 1.5rem);
}

কুমড়ো রাতে জ্বলজ্বল করে

যারা তীক্ষ্ণ দৃষ্টি রাখেন তারা আগে পাতার ব্যাকড্রপ নিয়ে আলোচনা করার সময় <source> উপাদানের ব্যবহার লক্ষ্য করেছেন। উনা একটি মিথস্ক্রিয়া করতে আগ্রহী ছিল যা রঙের স্কিম পছন্দের প্রতিক্রিয়া জানায়। ফলস্বরূপ, ব্যাকড্রপগুলি বিভিন্ন রূপের সাথে হালকা এবং অন্ধকার উভয় মোড সমর্থন করে। যেহেতু আপনি <picture> উপাদানের সাথে মিডিয়া প্রশ্নগুলি ব্যবহার করতে পারেন, এটি দুটি ব্যাকড্রপ শৈলী প্রদান করার একটি দুর্দান্ত উপায়। রঙের স্কিম পছন্দের জন্য <source> উৎস> উপাদান ক্যোয়ারী, এবং উপযুক্ত ব্যাকড্রপ দেখায়।

<picture>
  <source srcset={darkFront} media="(prefers-color-scheme: dark)" height="214" width="150">
  <img src={lightFront} class="page__background page__background--right" alt="" aria-hidden="true" height="214" width="150">
</picture>

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

.pumpkin__flame,
 .pumpkin__flame circle {
   transform-box: fill-box;
   transform-origin: 50% 100%;
 }

 .pumpkin__flame {
   scale: 0.8;
 }

 .pumpkin__flame circle {
   transition: scale 0.2s;
   scale: 0;
 }

@media(prefers-color-scheme: dark) {
   .pumpkin__flame {
     animation: pumpkin-flicker 3s calc(var(--index, 0) * -1s) infinite linear;
   }

   .pumpkin__flame circle {
     scale: 1;
   }

   @keyframes pumpkin-flicker {
     50% {
       scale: 1;
     }
   }
 }

এই প্রতিকৃতি আপনি দেখছেন?

আপনি পৃষ্ঠা 10 চেক আউট করলে, আপনি কিছু লক্ষ্য করতে পারেন। আপনাকে দেখা হচ্ছে! আপনি পৃষ্ঠার চারপাশে ঘোরাফেরা করার সাথে সাথে প্রতিকৃতির চোখ আপনার পয়েন্টারকে অনুসরণ করবে। এখানে কৌশলটি হল একটি অনুবাদ মানতে পয়েন্টার অবস্থান ম্যাপ করা, এবং এটি CSS-এ পাস করা।

const mapRange = (inputLower, inputUpper, outputLower, outputUpper, value) => {
   const INPUT_RANGE = inputUpper - inputLower
   const OUTPUT_RANGE = outputUpper - outputLower
   return outputLower + (((value - inputLower) / INPUT_RANGE) * OUTPUT_RANGE || 0)
 }

এই কোডটি ইনপুট এবং আউটপুট রেঞ্জ নেয় এবং প্রদত্ত মানগুলিকে ম্যাপ করে। উদাহরণস্বরূপ, এই ব্যবহারটি মান 625 দেবে।

mapRange(0, 100, 250, 1000, 50) // 625

প্রতিকৃতির জন্য, ইনপুট মান হল প্রতিটি চোখের কেন্দ্রবিন্দু, প্লাস বা বিয়োগ কিছু পিক্সেল দূরত্ব। আউটপুট পরিসীমা হল চোখ কতটা পিক্সেলে অনুবাদ করতে পারে। এবং তারপর x বা y অক্ষের পয়েন্টার অবস্থানটি মান হিসাবে পাস হয়। তাদের সরানোর সময় চোখের কেন্দ্রবিন্দু পেতে, চোখ নকল করা হয়। আসলগুলি সরানো হয় না, স্বচ্ছ এবং রেফারেন্সের জন্য ব্যবহৃত হয়।

তারপরে এটি একসাথে বেঁধে দেওয়া এবং চোখের উপর CSS কাস্টম সম্পত্তি মান আপডেট করার একটি কেস যাতে চোখ সরতে পারে। একটি ফাংশন window বিপরীতে pointermove ইভেন্টে আবদ্ধ। এই আগুনের সাথে সাথে, প্রতিটি চোখের সীমা কেন্দ্র বিন্দু গণনা করতে ব্যবহৃত হয়। তারপর পয়েন্টার অবস্থান মান ম্যাপ করা হয় যে চোখের উপর কাস্টম সম্পত্তি মান হিসাবে সেট করা হয়.

const RANGE = 15
const LIMIT = 80
const interact = ({ x, y }) => {
   // map a range against the eyes and pass in via custom properties
   const LEFT_EYE_BOUNDS = LEFT_EYE.getBoundingClientRect()
   const RIGHT_EYE_BOUNDS = RIGHT_EYE.getBoundingClientRect()

   const CENTERS = {
     lx: LEFT_EYE_BOUNDS.left + LEFT_EYE_BOUNDS.width * 0.5,
     rx: RIGHT_EYE_BOUNDS.left + RIGHT_EYE_BOUNDS.width * 0.5,
     ly: LEFT_EYE_BOUNDS.top + LEFT_EYE_BOUNDS.height * 0.5,
     ry: RIGHT_EYE_BOUNDS.top + RIGHT_EYE_BOUNDS.height * 0.5,
   }

   Object.entries(CENTERS)
     .forEach(([key, value]) => {
       const result = mapRange(value - LIMIT, value + LIMIT, -RANGE, RANGE)(key.indexOf('x') !== -1 ? x : y)
       EYES.style.setProperty(`--${key}`, result)
     })
 }

একবার মানগুলি CSS-এ পাস হয়ে গেলে, স্টাইলগুলি তাদের সাথে যা চায় তা করতে পারে। এখানে একটি দুর্দান্ত অংশ হল CSS clamp() ব্যবহার করে প্রতিটি চোখের আচরণ আলাদা করতে, যাতে আপনি জাভাস্ক্রিপ্টে আবার স্পর্শ না করে প্রতিটি চোখকে আলাদাভাবে আচরণ করতে পারেন।

.portrait__eye--mover {
   transition: translate 0.2s;
 }

 .portrait__eye--mover.portrait__eye--left {
   translate:
     clamp(-10px, var(--lx, 0) * 1px, 4px)
     clamp(-4px, var(--ly, 0) * 0.5px, 10px);
 }

 .portrait__eye--mover.portrait__eye--right {
   translate:
     clamp(-4px, var(--rx, 0) * 1px, 10px)
     clamp(-4px, var(--ry, 0) * 0.5px, 10px);
 }

কাস্টিং বানান

আপনি যদি পৃষ্ঠা ছয়টি চেক আউট করেন, আপনি কি মন্ত্রমুগ্ধ বোধ করেন? এই পৃষ্ঠাটি আমাদের চমত্কার জাদুকরী শিয়ালের নকশাকে আলিঙ্গন করে। আপনি যদি আপনার পয়েন্টারটি চারপাশে সরান তবে আপনি একটি কাস্টম কার্সার ট্রেইল প্রভাব দেখতে পাবেন। এটি ক্যানভাস অ্যানিমেশন ব্যবহার করে। একটি <canvas> উপাদান pointer-events: none । এর মানে ব্যবহারকারীরা এখনও নীচের বিষয়বস্তু ব্লক ক্লিক করতে পারেন।

.wand-canvas {
  height: 100%;
  width: 200%;
  pointer-events: none;
  right: 0;
  position: fixed;
}

অনেকটা যেমন আমাদের পোর্ট্রেট window একটি pointermove ইভেন্টের জন্য শোনে, তেমনি আমাদের <canvas> উপাদানও। তবুও প্রতিবার ইভেন্ট ফায়ার হলে, আমরা <canvas> উপাদানে অ্যানিমেট করার জন্য একটি বস্তু তৈরি করছি। এই বস্তুগুলি কার্সার ট্রেইলে ব্যবহৃত আকারগুলিকে উপস্থাপন করে। তাদের স্থানাঙ্ক এবং একটি এলোমেলো রঙ আছে।

আগের থেকে আমাদের mapRange ফাংশনটি আবার ব্যবহার করা হয়েছে, কারণ আমরা এটিকে ব্যবহার করতে পারি পয়েন্টার ডেল্টাকে size এবং rate ম্যাপ করতে। বস্তুগুলিকে একটি অ্যারেতে সংরক্ষণ করা হয় যেটি লুপ হয়ে যায় যখন বস্তুগুলিকে <canvas> উপাদানে আঁকা হয়। প্রতিটি বস্তুর বৈশিষ্ট্য আমাদের <canvas> উপাদানকে বলে যেখানে জিনিসগুলি আঁকতে হবে।

const blocks = []
  const createBlock = ({ x, y, movementX, movementY }) => {
    const LOWER_SIZE = CANVAS.height * 0.05
    const UPPER_SIZE = CANVAS.height * 0.25
    const size = mapRange(0, 100, LOWER_SIZE, UPPER_SIZE, Math.max(Math.abs(movementX), Math.abs(movementY)))
    const rate = mapRange(LOWER_SIZE, UPPER_SIZE, 1, 5, size)
    const { left, top, width, height } = CANVAS.getBoundingClientRect()
    
    const block = {
      hue: Math.random() * 359,
      x: x - left,
      y: y - top,
      size,
      rate,
    }
    
    blocks.push(block)
  }
window.addEventListener('pointermove', createBlock)

ক্যানভাসে আঁকার জন্য, requestAnimationFrame দিয়ে একটি লুপ তৈরি করা হয়। পৃষ্ঠাটি দেখার সময় কার্সার ট্রেইল শুধুমাত্র রেন্ডার করা উচিত। আমাদের একটি IntersectionObserver আছে যা আপডেট করে এবং নির্ধারণ করে যে কোন পৃষ্ঠাগুলি দেখা যাচ্ছে৷ একটি পৃষ্ঠা দৃশ্যমান হলে, বস্তুগুলি ক্যানভাসে চেনাশোনা হিসাবে রেন্ডার করা হয়।

তারপরে আমরা blocks অ্যারের উপর লুপ করি এবং ট্রেইলের প্রতিটি অংশ আঁকি। প্রতিটি ফ্রেম আকার হ্রাস করে এবং rate দ্বারা বস্তুর অবস্থান পরিবর্তন করে। এটি পতনশীল এবং স্কেলিং প্রভাব তৈরি করে। বস্তুটি সম্পূর্ণ সঙ্কুচিত হলে, অবজেক্টটি blocks অ্যারে থেকে সরানো হয়।

let wandFrame
const drawBlocks = () => {
   ctx.clearRect(0, 0, CANVAS.width, CANVAS.height)
  
   if (PAGE_SIX.className.indexOf('in-view') === -1 && wandFrame) {
     blocks.length = 0
     cancelAnimationFrame(wandFrame)
     document.body.removeEventListener('pointermove', createBlock)
     document.removeEventListener('resize', init)
   }
  
   for (let b = 0; b < blocks.length; b++) {
     const block = blocks[b]
     ctx.strokeStyle = ctx.fillStyle = `hsla(${block.hue}, 80%, 80%, 0.5)`
     ctx.beginPath()
     ctx.arc(block.x, block.y, block.size * 0.5, 0, 2 * Math.PI)
     ctx.stroke()
     ctx.fill()

     block.size -= block.rate
     block.y += block.rate

     if (block.size <= 0) {
       blocks.splice(b, 1)
     }

   }
   wandFrame = requestAnimationFrame(drawBlocks)
 }

পৃষ্ঠাটি দৃশ্যের বাইরে চলে গেলে, ইভেন্ট শ্রোতাদের সরানো হয় এবং অ্যানিমেশন ফ্রেম লুপ বাতিল করা হয়। blocks অ্যারেও সাফ করা হয়েছে।

এখানে কার্সার ট্রেইল অ্যাকশন!

অ্যাক্সেসযোগ্যতা পর্যালোচনা

এটি অন্বেষণ করার জন্য একটি মজার অভিজ্ঞতা তৈরি করা ভাল, তবে এটি ব্যবহারকারীদের কাছে অ্যাক্সেসযোগ্য না হলে এটি ভাল নয়৷ রিলিজের আগে একটি অ্যাক্সেসিবিলিটি পর্যালোচনার জন্য Chrometober প্রস্তুত করার ক্ষেত্রে অ্যাডামের দক্ষতা এই ক্ষেত্রে অমূল্য প্রমাণিত হয়েছে৷

কভার করা উল্লেখযোগ্য কিছু এলাকা:

  • ব্যবহৃত HTML শব্দার্থিক ছিল তা নিশ্চিত করা। এটি বইয়ের জন্য উপযুক্ত ল্যান্ডমার্ক উপাদান যেমন <main> মতো বিষয়গুলি অন্তর্ভুক্ত করেছে; প্রতিটি বিষয়বস্তুর ব্লকের জন্য <article> উপাদানের ব্যবহার এবং <abbr> উপাদান যেখানে সংক্ষিপ্ত শব্দগুলি চালু করা হয়েছে। বইটি তৈরি হওয়ার সাথে সাথে সামনের দিকে চিন্তা করা জিনিসগুলিকে আরও অ্যাক্সেসযোগ্য করে তুলেছে। শিরোনাম এবং লিঙ্কগুলির ব্যবহার একজন ব্যবহারকারীকে নেভিগেট করা সহজ করে তোলে। পৃষ্ঠাগুলির জন্য একটি তালিকা ব্যবহার করার অর্থ হল সহায়ক প্রযুক্তি দ্বারা পৃষ্ঠাগুলির সংখ্যা ঘোষণা করা হয়৷
  • নিশ্চিত করা হচ্ছে যে সমস্ত ছবিতে উপযুক্ত alt বৈশিষ্ট্য ব্যবহার করা হয়েছে৷ ইনলাইন SVG-এর জন্য, যেখানে প্রয়োজন সেখানে title উপাদান উপস্থিত থাকে।
  • aria বৈশিষ্ট্য ব্যবহার করে যেখানে তারা অভিজ্ঞতা উন্নত করে। পৃষ্ঠা এবং তাদের পাশের জন্য aria-label ব্যবহার ব্যবহারকারীর কাছে যোগাযোগ করে যে তারা কোন পৃষ্ঠায় রয়েছে। "আরও পড়ুন" লিঙ্কগুলিতে aria-describedBy এর ব্যবহার বিষয়বস্তু ব্লকের পাঠ্যের সাথে যোগাযোগ করে। এটি লিঙ্কটি ব্যবহারকারীকে কোথায় নিয়ে যাবে সে সম্পর্কে অস্পষ্টতা দূর করে।
  • বিষয়বস্তু ব্লকের বিষয়ে, পুরো কার্ডে ক্লিক করার ক্ষমতা এবং শুধুমাত্র "আরও পড়ুন" লিঙ্কটি উপলব্ধ নয়।
  • কোন পৃষ্ঠাগুলি দৃশ্যমান রয়েছে তা ট্র্যাক করতে একটি IntersectionObserver এর ব্যবহার আগে এসেছে৷ এটির অনেক সুবিধা রয়েছে যা শুধুমাত্র কর্মক্ষমতা সম্পর্কিত নয়। যে পৃষ্ঠাগুলি দেখা যাচ্ছে না সেগুলির কোনও অ্যানিমেশন বা ইন্টারঅ্যাকশন বিরাম দেওয়া হবে৷ কিন্তু এই পৃষ্ঠাগুলিতেও inert বৈশিষ্ট্য প্রয়োগ করা হয়েছে। এর মানে হল যে ব্যবহারকারীরা স্ক্রিন রিডার ব্যবহার করে তারা একই বিষয়বস্তু অন্বেষণ করতে পারে যেমনটি দর্শনীয় ব্যবহারকারীদের। যে পৃষ্ঠাটি দেখা যাচ্ছে তার মধ্যেই ফোকাস থাকে এবং ব্যবহারকারীরা অন্য পৃষ্ঠায় ট্যাব করতে পারে না।
  • সবশেষে কিন্তু অন্তত নয়, গতির জন্য ব্যবহারকারীর পছন্দকে সম্মান করতে আমরা মিডিয়া কোয়েরি ব্যবহার করি।

এখানে কিছু ব্যবস্থা তুলে ধরার পর্যালোচনা থেকে একটি স্ক্রিনশট রয়েছে।

উপাদানটিকে পুরো বইয়ের চারপাশে চিহ্নিত করা হয়েছে, এটি নির্দেশ করে যে এটি সহায়ক প্রযুক্তি ব্যবহারকারীদের খুঁজে পাওয়ার জন্য প্রধান ল্যান্ডমার্ক হওয়া উচিত। স্ক্রিনশটে আরও বর্ণনা করা হয়েছে৷" width="800" height="465">৷

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

আমরা যা শিখেছি

Chrometober-এর পিছনে অনুপ্রেরণা শুধুমাত্র সম্প্রদায়ের ওয়েব বিষয়বস্তু হাইলাইট করাই নয়, এটি আমাদের জন্য স্ক্রোল-লিঙ্কড অ্যানিমেশন API পলিফিল পরীক্ষা করার একটি উপায় ছিল যা বিকাশে রয়েছে।

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

CSS, UI, এবং DevTools টিম একটি কনফারেন্স রুমে টেবিলের চারপাশে বসে আছে। উনা একটি সাদা বোর্ডে দাঁড়িয়ে আছে যা স্টিকি নোটে আবৃত। দলের অন্যান্য সদস্যরা খাবার এবং ল্যাপটপ নিয়ে টেবিলের চারপাশে বসে আছে।

উদাহরণস্বরূপ, ডিভাইসে বই পরীক্ষা করা একটি রেন্ডারিং সমস্যা উত্থাপন করেছে। আমাদের বইটি iOS ডিভাইসে প্রত্যাশিতভাবে রেন্ডার করবে না। ভিউপোর্ট ইউনিট পৃষ্ঠার আকার, কিন্তু যখন একটি খাঁজ উপস্থিত ছিল, এটি বইটিকে প্রভাবিত করে। সমাধানটি ছিল meta ভিউপোর্টে viewport-fit=cover ব্যবহার করা:

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

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

Chrome এ খোলা একটি ডেমোর একটি স্ক্রিনশট৷ বিকাশকারী সরঞ্জামগুলি খোলা এবং একটি বেসলাইন কর্মক্ষমতা পরিমাপ দেখায়৷

Chrome এ খোলা একটি ডেমোর একটি স্ক্রিনশট৷ বিকাশকারী সরঞ্জামগুলি খোলা রয়েছে এবং একটি উন্নত কর্মক্ষমতা পরিমাপ দেখায়৷

তাই তো!

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

Chrometober 2022 একটি মোড়ানো।

আমরা আশা করি আপনি এটি উপভোগ করেছেন! আপনার প্রিয় বৈশিষ্ট্য কি? আমাকে টুইট করুন এবং আমাদের জানান!

ক্রোমটোবার থেকে অক্ষরের একটি স্টিকার শীট ধারণ করছে ঝি।

আপনি যদি কোনো ইভেন্টে আমাদের দেখেন তাহলে আপনি হয়তো দলের একজনের কাছ থেকে কিছু স্টিকার নিতে পারবেন।

আনস্প্ল্যাশে ডেভিড মেনিড্রেয়ের হিরো ছবি

,

এই ক্রোমটোবারে মজাদার এবং ভীতিকর টিপস এবং কৌশলগুলি ভাগ করার জন্য স্ক্রোলিং বইটি কীভাবে প্রাণবন্ত হয়েছে৷

Designcember থেকে অনুসরণ করে, আমরা কমিউনিটি এবং Chrome টিমের ওয়েব বিষয়বস্তু হাইলাইট এবং শেয়ার করার উপায় হিসেবে এই বছর আপনার জন্য Chrometober তৈরি করতে চেয়েছিলাম। Designcember কন্টেইনার ক্যোয়ারী ব্যবহার প্রদর্শন করেছে, কিন্তু এই বছর আমরা CSS স্ক্রোল-লিঙ্কড অ্যানিমেশন API প্রদর্শন করছি।

web.dev/chrometober-2022- এ স্ক্রলিং বইয়ের অভিজ্ঞতা দেখুন।

ওভারভিউ

প্রজেক্টের লক্ষ্য ছিল স্ক্রোল-লিঙ্কড অ্যানিমেশন এপিআই হাইলাইট করে একটি অদ্ভুত অভিজ্ঞতা প্রদান করা। কিন্তু, বাতিক থাকাকালীন, অভিজ্ঞতাটি প্রতিক্রিয়াশীল এবং অ্যাক্সেসযোগ্যও হওয়া দরকার। প্রজেক্টটি এপিআই পলিফিল পরীক্ষা করার একটি দুর্দান্ত উপায় যা সক্রিয় বিকাশে রয়েছে; যে, সেইসাথে সংমিশ্রণে বিভিন্ন কৌশল এবং সরঞ্জাম চেষ্টা করে। এবং সব একটি উত্সব হ্যালোইন থিম সঙ্গে!

আমাদের দলের গঠন এই মত দেখায়:

একটি স্ক্রলিটেলিং অভিজ্ঞতার খসড়া তৈরি করা

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

প্রকল্পের সাথে সম্পর্কিত বিভিন্ন ডুডল এবং স্ক্রিবল সহ একটি নোটবুক একটি ডেস্কে রয়েছে।

আমার প্রথম Google প্রকল্পকে একটি অপ্রত্যাশিত দিকে নিয়ে যাওয়ার সৃজনশীল স্বাধীনতা পাওয়াটা উত্তেজনাপূর্ণ ছিল। এটি একটি প্রাথমিক প্রোটোটাইপ ছিল কিভাবে একজন ব্যবহারকারী বিষয়বস্তুর মাধ্যমে নেভিগেট করতে পারে।

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

আমি যে ডেমোগুলি তৈরি করেছি তার মধ্যে একটি ছিল একটি 3D-CSS বই যেখানে আপনি স্ক্রোল করার সাথে সাথে পৃষ্ঠাগুলি উল্টে যায় এবং আমরা Chrometober এর জন্য যা চেয়েছিলাম তার জন্য এটি অনেক বেশি উপযুক্ত মনে হয়েছিল৷ স্ক্রোল-লিঙ্কড অ্যানিমেশন API সেই কার্যকারিতার জন্য একটি নিখুঁত অদলবদল। এটি scroll-snap সাথেও ভাল কাজ করে, যেমন আপনি দেখতে পাবেন!

প্রকল্পের জন্য আমাদের চিত্রকর, Tyler Reed , আমরা ধারণা পরিবর্তন করার সাথে সাথে নকশা পরিবর্তন করতে দুর্দান্ত ছিলেন। টাইলার তার প্রতি নিক্ষিপ্ত সমস্ত সৃজনশীল ধারণা গ্রহণ এবং সেগুলিকে জীবন্ত করার জন্য একটি দুর্দান্ত কাজ করেছিলেন। এটি একসাথে অনেক মজার চিন্তাভাবনা ছিল। আমরা কীভাবে এটি কাজ করতে চেয়েছিলাম তার একটি বড় অংশ ছিল বিচ্ছিন্ন ব্লকগুলিতে বিভক্ত বৈশিষ্ট্যগুলি। এইভাবে, আমরা সেগুলিকে দৃশ্যগুলিতে রচনা করতে পারি এবং তারপরে আমরা যা জীবন্ত এনেছি তা বাছাই করে বেছে নিতে পারি।

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

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

API এর সাথে পরিচিত হচ্ছেন

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

উচ্চ স্তরে, আপনি স্ক্রোল করার জন্য অ্যানিমেশন লিঙ্ক করতে এই API ব্যবহার করতে পারেন। এটা মনে রাখা গুরুত্বপূর্ণ যে আপনি স্ক্রলে একটি অ্যানিমেশন ট্রিগার করতে পারবেন না—এটি এমন কিছু যা পরে আসতে পারে। স্ক্রোল-লিঙ্কড অ্যানিমেশনগুলিও দুটি প্রধান বিভাগে পড়ে:

  1. যারা স্ক্রোল অবস্থানে প্রতিক্রিয়া.
  2. যারা তার স্ক্রলিং পাত্রে একটি উপাদান অবস্থান প্রতিক্রিয়া.

পরবর্তীটি তৈরি করতে, আমরা একটি animation-timeline সম্পত্তির মাধ্যমে প্রয়োগ করা একটি ViewTimeline ব্যবহার করি।

ViewTimeline ব্যবহার করে CSS-এ কেমন দেখায় তার একটি উদাহরণ এখানে দেওয়া হল:

.element-moving-in-viewport {
  view-timeline-name: foo;
  view-timeline-axis: block;
}

.element-scroll-linked {
  animation: rotate both linear;
  animation-timeline: foo;
  animation-delay: enter 0%;
  animation-end-delay: cover 50%;
}

@keyframes rotate {
 to {
   rotate: 360deg;
 }
}

আমরা view-timeline-name সহ একটি ViewTimeline তৈরি করি এবং এটির জন্য অক্ষ নির্ধারণ করি। এই উদাহরণে, block লজিক্যাল block বোঝায়। অ্যানিমেশনটি animation-timeline সম্পত্তির সাথে স্ক্রোল করার সাথে সংযুক্ত হয়। animation-delay এবং animation-end-delay (লেখার সময়) আমরা কীভাবে পর্যায়গুলি সংজ্ঞায়িত করি।

এই পর্যায়গুলি সেই পয়েন্টগুলিকে সংজ্ঞায়িত করে যেখানে অ্যানিমেশনটি তার স্ক্রলিং পাত্রে একটি উপাদানের অবস্থানের সাথে সংযুক্ত হওয়া উচিত। আমাদের উদাহরণে, আমরা বলছি অ্যানিমেশন শুরু করুন যখন উপাদানটি স্ক্রলিং কন্টেইনারে প্রবেশ করে ( enter 0% )। এবং এটি স্ক্রোলিং কন্টেইনারের 50% ( cover 50% ) ঢেকে গেলে শেষ করুন।

এখানে আমাদের কর্মের ডেমো:

আপনি ভিউপোর্টে চলমান উপাদানের সাথে একটি অ্যানিমেশন লিঙ্ক করতে পারেন। আপনি animation-timeline উপাদানটির view-timeline হিসাবে সেট করে এটি করতে পারেন। এটি তালিকা অ্যানিমেশনের মতো পরিস্থিতিগুলির জন্য ভাল। আচরণটি একই রকম যে আপনি IntersectionObserver ব্যবহার করে প্রবেশের সময় উপাদানগুলিকে অ্যানিমেট করতে পারেন।

element-moving-in-viewport {
  view-timeline-name: foo;
  view-timeline-axis: block;
  animation: scale both linear;
  animation-delay: enter 0%;
  animation-end-delay: cover 50%;
  animation-timeline: foo;
}

@keyframes scale {
  0% {
    scale: 0;
  }
}

এটির সাথে "মুভার" স্কেল বাড়ার সাথে সাথে এটি ভিউপোর্টে প্রবেশ করে, "স্পিনার" এর ঘূর্ণনকে ট্রিগার করে।

আমি পরীক্ষা করে যা পেয়েছি তা হল API স্ক্রোল-স্ন্যাপের সাথে খুব ভাল কাজ করে। ViewTimeline সাথে স্ক্রোল-স্ন্যাপ একটি বইতে পৃষ্ঠা ঘুরানোর জন্য একটি দুর্দান্ত ফিট হবে।

মেকানিক্সের প্রোটোটাইপিং

কিছু পরীক্ষা করার পরে, আমি একটি বইয়ের প্রোটোটাইপ কাজ করতে সক্ষম হয়েছি। বইয়ের পাতা উল্টাতে আপনি অনুভূমিকভাবে স্ক্রোল করুন।

ডেমোতে, আপনি ড্যাশ বর্ডার সহ হাইলাইট করা বিভিন্ন ট্রিগার দেখতে পাবেন।

মার্কআপটি কিছুটা এইরকম দেখাচ্ছে:

<body>
  <div class="book-placeholder">
    <ul class="book" style="--count: 7;">
      <li
        class="page page--cover page--cover-front"
        data-scroll-target="1"
        style="--index: 0;"
      >
        <div class="page__paper">
          <div class="page__side page__side--front"></div>
          <div class="page__side page__side--back"></div>
        </div>
      </li>
      <!-- Markup for other pages here -->
    </ul>
  </div>
  <div>
    <p>intro spacer</p>
  </div>
  <div data-scroll-intro>
    <p>scale trigger</p>
  </div>
  <div data-scroll-trigger="1">
    <p>page trigger</p>
  </div>
  <!-- Markup for other triggers here -->
</body>

আপনি স্ক্রোল করার সাথে সাথে বইয়ের পৃষ্ঠাগুলি উল্টে যায়, তবে স্ন্যাপ খোলা বা বন্ধ। এটি ট্রিগারগুলির স্ক্রোল-স্ন্যাপ প্রান্তিককরণের উপর নির্ভরশীল।

html {
  scroll-snap-type: x mandatory;
}

body {
  grid-template-columns: repeat(var(--trigger-count), auto);
  overflow-y: hidden;
  overflow-x: scroll;
  display: grid;
}

body > [data-scroll-trigger] {
  height: 100vh;
  width: clamp(10rem, 10vw, 300px);
}

body > [data-scroll-trigger] {
  scroll-snap-align: end;
}

এইবার, আমরা CSS-এ ViewTimeline সংযোগ করি না, কিন্তু JavaScript-এ ওয়েব অ্যানিমেশন API ব্যবহার করি। এটির অতিরিক্ত সুবিধা রয়েছে উপাদানগুলির একটি সেটের উপর লুপ করতে এবং প্রতিটি হাতে তৈরি করার পরিবর্তে আমাদের প্রয়োজনীয় ViewTimeline তৈরি করতে সক্ষম হওয়ার।

const triggers = document.querySelectorAll("[data-scroll-trigger]")

const commonProps = {
  delay: { phase: "enter", percent: CSS.percent(0) },
  endDelay: { phase: "enter", percent: CSS.percent(100) },
  fill: "both"
}

const setupPage = (trigger, index) => {
  const target = document.querySelector(
    `[data-scroll-target="${trigger.getAttribute("data-scroll-trigger")}"]`
  );

  const viewTimeline = new ViewTimeline({
    subject: trigger,
    axis: 'inline',
  });

  target.animate(
    [
      {
        transform: `translateZ(${(triggers.length - index) * 2}px)`
      },
      {
        transform: `translateZ(${(triggers.length - index) * 2}px)`,
        offset: 0.75
      },
      {
        transform: `translateZ(${(triggers.length - index) * -1}px)`
      }
    ],
    {
      timeline: viewTimeline,
      commonProps,
    }
  );
  target.querySelector(".page__paper").animate(
    [
      {
        transform: "rotateY(0deg)"
      },
      {
        transform: "rotateY(-180deg)"
      }
    ],
    {
      timeline: viewTimeline,
      commonProps,
    }
  );
};

const triggers = document.querySelectorAll('[data-scroll-trigger]')
triggers.forEach(setupPage);

প্রতিটি ট্রিগারের জন্য, আমরা একটি ViewTimeline তৈরি করি। তারপরে আমরা সেই ViewTimeline ব্যবহার করে ট্রিগারের সংশ্লিষ্ট পৃষ্ঠাটিকে অ্যানিমেট করি। এটি স্ক্রোল করার জন্য পৃষ্ঠার অ্যানিমেশনকে লিঙ্ক করে। আমাদের অ্যানিমেশনের জন্য, আমরা পৃষ্ঠার একটি উপাদানকে y-অক্ষে ঘুরিয়ে পৃষ্ঠাটি ঘুরিয়ে দিচ্ছি। এছাড়াও আমরা পৃষ্ঠাটিকে z-অক্ষে অনুবাদ করি যাতে এটি একটি বইয়ের মতো আচরণ করে।

এটা সব একসাথে নির্বাণ

একবার আমি বইটির মেকানিজম তৈরি করে ফেললে, আমি টাইলারের চিত্রগুলিকে জীবন্ত করার দিকে মনোনিবেশ করতে পারতাম।

অ্যাস্ট্রো

দলটি 2021 সালে ডিজাইনসেম্বারের জন্য Astro ব্যবহার করেছিল এবং আমি Chrometober-এর জন্য এটি আবার ব্যবহার করতে আগ্রহী। জিনিসগুলিকে উপাদানগুলিতে বিভক্ত করতে সক্ষম হওয়ার বিকাশকারীর অভিজ্ঞতা এই প্রকল্পের জন্য উপযুক্ত।

বই নিজেই একটি উপাদান. এটি পৃষ্ঠা উপাদানগুলির একটি সংগ্রহও। প্রতিটি পৃষ্ঠার দুটি দিক রয়েছে এবং তাদের ব্যাকড্রপ রয়েছে। একটি পৃষ্ঠার পাশের শিশুরা এমন উপাদান যা যোগ করা, সরানো এবং সহজে অবস্থান করা যায়।

একটি বই নির্মাণ

ব্লকগুলি পরিচালনা করা সহজ করা আমার জন্য গুরুত্বপূর্ণ ছিল। আমি দলের বাকিদের জন্য অবদান রাখা সহজ করতে চেয়েছিলাম।

একটি উচ্চ স্তরের পৃষ্ঠাগুলি একটি কনফিগারেশন অ্যারে দ্বারা সংজ্ঞায়িত করা হয়। অ্যারের প্রতিটি পৃষ্ঠা বস্তু একটি পৃষ্ঠার জন্য বিষয়বস্তু, ব্যাকড্রপ এবং অন্যান্য মেটাডেটা সংজ্ঞায়িত করে।

const pages = [
  {
    front: {
      marked: true,
      content: PageTwo,
      backdrop: spreadOne,
      darkBackdrop: spreadOneDark
    },
    back: {
      content: PageThree,
      backdrop: spreadTwo,
      darkBackdrop: spreadTwoDark
    },
    aria: `page 1`
  },
  /* Obfuscated page objects */
]

এই Book উপাদান পাস করা.

<Book pages={pages} />

Book উপাদান হল যেখানে স্ক্রলিং প্রক্রিয়া প্রয়োগ করা হয় এবং বইয়ের পৃষ্ঠাগুলি তৈরি করা হয়। প্রোটোটাইপ থেকে একই প্রক্রিয়া ব্যবহার করা হয়; কিন্তু আমরা বিশ্বব্যাপী তৈরি ViewTimeline একাধিক উদাহরণ শেয়ার করি।

window.CHROMETOBER_TIMELINES.push(viewTimeline);

এইভাবে, আমরা তাদের পুনরায় তৈরি করার পরিবর্তে অন্য কোথাও ব্যবহার করার জন্য টাইমলাইন শেয়ার করতে পারি। এই বিষয়ে পরে আরো.

পৃষ্ঠা রচনা

প্রতিটি পৃষ্ঠা একটি তালিকার মধ্যে একটি তালিকা আইটেম:

<ul class="book">
  {
    pages.map((page, index) => {
      const FrontSlot = page.front.content
      const BackSlot = page.back.content
      return (
        <Page
          index={index}
          cover={page.cover}
          aria={page.aria}
          backdrop={
            {
              front: {
                light: page.front.backdrop,
                dark: page.front.darkBackdrop
              },
              back: {
                light: page.back.backdrop,
                dark: page.back.darkBackdrop
              }
            }
          }>
          {page.front.content && <FrontSlot slot="front" />}    
          {page.back.content && <BackSlot slot="back" />}    
        </Page>
      )
    })
  }
</ul>

এবং সংজ্ঞায়িত কনফিগারেশন প্রতিটি Page উদাহরণে পাস হয়। পৃষ্ঠাগুলি প্রতিটি পৃষ্ঠায় বিষয়বস্তু সন্নিবেশ করার জন্য Astro এর স্লট বৈশিষ্ট্য ব্যবহার করে।

<li
  class={className}
  data-scroll-target={target}
  style={`--index:${index};`}
  aria-label={aria}
>
  <div class="page__paper">
    <div
      class="page__side page__side--front"
      aria-label={`Right page of ${index}`}
    >
      <picture>
        <source
          srcset={darkFront}
          media="(prefers-color-scheme: dark)"
          height="214"
          width="150"
        >
        <img
          src={lightFront}
          class="page__background page__background--right"
          alt=""
          aria-hidden="true"
          height="214"
          width="150"
        >
      </picture>
      <div class="page__content">
        <slot name="front" />
      </div>
    </div>
    <!-- Markup for back page -->
  </div>
</li>

এই কোডটি বেশিরভাগ কাঠামো সেট আপ করার জন্য। অবদানকারীরা এই কোডটি স্পর্শ না করেই বেশিরভাগ অংশের জন্য বইয়ের বিষয়বস্তুতে কাজ করতে পারে৷

ব্যাকড্রপ

একটি বইয়ের দিকে সৃজনশীল স্থানান্তরটি বিভাগগুলিকে বিভক্ত করা আরও সহজ করে তোলে এবং বইটির প্রতিটি স্প্রেড মূল নকশা থেকে নেওয়া একটি দৃশ্য।

পৃষ্ঠাটি বই থেকে চিত্র ছড়িয়েছে যাতে একটি কবরস্থানে একটি আপেল গাছ রয়েছে। কবরস্থানে একাধিক শিরোনাম রয়েছে এবং একটি বড় চাঁদের সামনে আকাশে একটি বাদুড় রয়েছে।

যেহেতু আমরা বইয়ের জন্য একটি আকৃতির অনুপাতের সিদ্ধান্ত নিয়েছিলাম, প্রতিটি পৃষ্ঠার ব্যাকড্রপে একটি ছবির উপাদান থাকতে পারে। সেই উপাদানটিকে 200% প্রস্থে সেট করা এবং পৃষ্ঠার পাশের উপর ভিত্তি করে object-position ব্যবহার করা কৌশলটি করে।

.page__background {
  height: 100%;
  width: 200%;
  object-fit: cover;
  object-position: 0 0;
  position: absolute;
  top: 0;
  left: 0;
}

.page__background--right {
  object-position: 100% 0;
}

পৃষ্ঠার বিষয়বস্তু

পৃষ্ঠাগুলির একটি তৈরি করা যাক। পৃষ্ঠা তিনে একটি পেঁচা যা একটি গাছে উঠে আসে।

কনফিগারেশনে সংজ্ঞায়িত হিসাবে এটি একটি PageThree উপাদান দিয়ে জনবহুল হয়। এটি একটি অ্যাস্ট্রো উপাদান ( PageThree.astro )। এই উপাদানগুলি এইচটিএমএল ফাইলগুলির মতো দেখায় তবে তাদের ফ্রন্টম্যাটারের অনুরূপ শীর্ষে একটি কোড বেড়া রয়েছে। এটি আমাদের অন্যান্য উপাদানগুলি আমদানি করার মতো জিনিসগুলি করতে সক্ষম করে। পৃষ্ঠার তিনটির উপাদানটি দেখতে এরকম দেখাচ্ছে:

---
import TreeOwl from '../TreeOwl/TreeOwl.astro'
import { contentBlocks } from '../../assets/content-blocks.json'
import ContentBlock from '../ContentBlock/ContentBlock.astro'
---
<TreeOwl/>
<ContentBlock {...contentBlocks[3]} id="four" />

<style is:global>
  .content-block--four {
    left: 30%;
    bottom: 10%;
  }
</style>

আবার পৃষ্ঠাগুলি প্রকৃতির পারমাণবিক। এগুলি বৈশিষ্ট্যগুলির সংগ্রহ থেকে নির্মিত। পৃষ্ঠা তিনটি একটি সামগ্রী ব্লক এবং ইন্টারেক্টিভ আউল বৈশিষ্ট্যযুক্ত, সুতরাং প্রতিটি জন্য একটি উপাদান আছে।

সামগ্রী ব্লকগুলি বইয়ের মধ্যে দেখা সামগ্রীর লিঙ্ক। এগুলি একটি কনফিগারেশন অবজেক্ট দ্বারা চালিত হয়।

{
 "contentBlocks": [
    {
      "id": "one",
      "title": "New in Chrome",
      "blurb": "Lift your spirits with a round up of all the tools and features in Chrome.",
      "link": "https://www.youtube.com/watch?v=qwdN1fJA_d8&list=PLNYkxOF6rcIDfz8XEA3loxY32tYh7CI3m"
    },
    …otherBlocks
  ]
}

এই কনফিগারেশনটি আমদানি করে যেখানে সামগ্রী ব্লকগুলির প্রয়োজন হয়। তারপরে প্রাসঙ্গিক ব্লক কনফিগারেশনটি ContentBlock উপাদানটিতে চলে যায়।

<ContentBlock {...contentBlocks[3]} id="four" />

সামগ্রীটি অবস্থান করার জন্য আমরা কীভাবে পৃষ্ঠার উপাদানটি ব্যবহার করি তার একটি উদাহরণ এখানে রয়েছে। এখানে, একটি সামগ্রী ব্লক অবস্থান পায়।

<style is:global>
  .content-block--four {
    left: 30%;
    bottom: 10%;
  }
</style>

তবে, একটি সামগ্রী ব্লকের জন্য সাধারণ শৈলীগুলি উপাদান কোডের সাথে সহ-অবস্থিত।

.content-block {
  background: hsl(0deg 0% 0% / 70%);
  color: var(--gray-0);
  border-radius:  min(3vh, var(--size-4));
  padding: clamp(0.75rem, 2vw, 1.25rem);
  display: grid;
  gap: var(--size-2);
  position: absolute;
  cursor: pointer;
  width: 50%;
}

আমাদের পেঁচা হিসাবে, এটি একটি ইন্টারেক্টিভ বৈশিষ্ট্য - এই প্রকল্পের অনেকের মধ্যে একটি। এটি একটি দুর্দান্ত ছোট উদাহরণ যা দেখায় যে আমরা কীভাবে ভাগ করা ভিউটাইমলাইনটি তৈরি করেছি তা কীভাবে ব্যবহার করেছি।

একটি উচ্চ স্তরে, আমাদের পেঁচা উপাদান কিছু এসভিজি আমদানি করে এবং এটি অ্যাস্ট্রোর খণ্ড ব্যবহার করে ইনলাইন করে।

---
import { default as Owl } from '../Features/Owl.svg?raw'
---
<Fragment set:html={Owl} />

এবং আমাদের পেঁচা অবস্থানের জন্য শৈলীগুলি উপাদান কোডের সাথে সহ-অবস্থিত।

.owl {
  width: 34%;
  left: 10%;
  bottom: 34%;
}

স্টাইলিংয়ের একটি অতিরিক্ত টুকরো রয়েছে যা পেঁচাগুলির জন্য transform আচরণকে সংজ্ঞায়িত করে।

.owl__owl {
  transform-origin: 50% 100%;
  transform-box: fill-box;
}

transform-box ব্যবহার transform-origin প্রভাবিত করে। এটি এসভিজির মধ্যে অবজেক্টের সীমাবদ্ধ বাক্সের সাথে সম্পর্কিত করে তোলে। পেঁচাটি নীচের কেন্দ্র থেকে স্কেল করে, তাই transform-origin: 50% 100%

মজার অংশটি হ'ল যখন আমরা আমাদের উত্পন্ন ViewTimeline একটিতে পেঁচাটি সংযুক্ত করি:

const setUpOwl = () => {
   const owl = document.querySelector('.owl__owl');

   owl.animate([
     {
       translate: '0% 110%',
     },
     {
       translate: '0% 10%',
     },
   ], {
     timeline: CHROMETOBER_TIMELINES[1],
     delay: { phase: "enter", percent: CSS.percent(80) },
     endDelay: { phase: "enter", percent: CSS.percent(90) },
     fill: 'both' 
   });
 }

 if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches)
   setUpOwl()

কোডের এই ব্লকে, আমরা দুটি জিনিস করি:

  1. ব্যবহারকারীর গতি পছন্দগুলি পরীক্ষা করুন।
  2. যদি তাদের কোনও পছন্দ না থাকে তবে আউলের একটি অ্যানিমেশন স্ক্রোল করার জন্য লিঙ্ক করুন।

দ্বিতীয় অংশের জন্য, পেঁচা ওয়েব অ্যানিমেশনগুলি এপিআই ব্যবহার করে ওয়াই-অক্ষগুলিতে অ্যানিমেট করে। স্বতন্ত্র রূপান্তর সম্পত্তি translate ব্যবহৃত হয় এবং এটি একটি ViewTimeline সাথে যুক্ত। এটি timeline সম্পত্তির মাধ্যমে CHROMETOBER_TIMELINES[1] এর সাথে যুক্ত। এটি একটি ViewTimeline যা পৃষ্ঠার মোড়ের জন্য উত্পন্ন হয়। এটি enter পর্বটি ব্যবহার করে পেঁচার অ্যানিমেশনটিকে পৃষ্ঠার টার্নের সাথে লিঙ্ক করে। এটি সংজ্ঞায়িত করে, যখন পৃষ্ঠাটি 80% পরিণত হয়, তখন পেঁচাটি সরানো শুরু করুন। 90%এ, আউলের অনুবাদটি শেষ করা উচিত।

বইয়ের বৈশিষ্ট্য

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

এটিতে এমন উপাদান রয়েছে যা সিএসএস অ্যানিমেশন দ্বারা চালিত হয়।

একবার সামগ্রী ব্লকগুলি বইটিতে আসার পরে, অন্যান্য বৈশিষ্ট্যগুলির সাথে সৃজনশীল হওয়ার সময় ছিল। এটি কিছু আলাদা ইন্টারঅ্যাকশন তৈরি করার এবং জিনিসগুলি বাস্তবায়নের জন্য বিভিন্ন উপায় চেষ্টা করার একটি সুযোগ সরবরাহ করেছিল।

জিনিস প্রতিক্রিয়াশীল রাখা

প্রতিক্রিয়াশীল ভিউপোর্ট ইউনিটগুলি বই এবং এর বৈশিষ্ট্যগুলি আকার দেয়। তবে, ফন্টগুলি প্রতিক্রিয়াশীল রাখা একটি আকর্ষণীয় চ্যালেঞ্জ ছিল। কনটেইনার ক্যোয়ারী ইউনিটগুলি এখানে একটি ভাল ফিট। যদিও তারা এখনও সর্বত্র সমর্থিত নয়। বইয়ের আকার সেট করা আছে, সুতরাং আমাদের কোনও ধারক ক্যোয়ারির দরকার নেই। একটি ইনলাইন কনটেইনার ক্যোয়ারী ইউনিট সিএসএস calc() দিয়ে তৈরি করা যেতে পারে এবং ফন্ট সাইজিংয়ের জন্য ব্যবহৃত হয়।


.book-placeholder {
  --size: clamp(12rem, 72vw, 80vmin);
  --aspect-ratio: 360 / 504;
  --cqi: calc(0.01 * (var(--size) * (var(--aspect-ratio))));
}

.content-block h2 {
  color: var(--gray-0);
  font-size: clamp(0.6rem, var(--cqi) * 4, 1.5rem);
}

.content-block :is(p, a) {
  font-size: clamp(0.6rem, var(--cqi) * 3, 1.5rem);
}

রাতে কুমড়ো জ্বলজ্বল

আগ্রহী চোখের সাথে যারা পৃষ্ঠা ব্যাকড্রপগুলি আগে আলোচনা করার সময় <source> উপাদানগুলির ব্যবহার লক্ষ্য করেছেন। উনা একটি মিথস্ক্রিয়া করতে আগ্রহী ছিল যা রঙিন স্কিম পছন্দকে প্রতিক্রিয়া জানায়। ফলস্বরূপ, ব্যাকড্রপগুলি বিভিন্ন রূপগুলির সাথে হালকা এবং গা dark ় উভয় মোডকে সমর্থন করে। যেহেতু আপনি <picture> উপাদান সহ মিডিয়া প্রশ্নগুলি ব্যবহার করতে পারেন, এটি দুটি ব্যাকড্রপ স্টাইল সরবরাহ করার দুর্দান্ত উপায়। রঙ স্কিম পছন্দগুলির জন্য <source> উত্স> উপাদান প্রশ্নগুলি এবং উপযুক্ত ব্যাকড্রপটি দেখায়।

<picture>
  <source srcset={darkFront} media="(prefers-color-scheme: dark)" height="214" width="150">
  <img src={lightFront} class="page__background page__background--right" alt="" aria-hidden="true" height="214" width="150">
</picture>

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

.pumpkin__flame,
 .pumpkin__flame circle {
   transform-box: fill-box;
   transform-origin: 50% 100%;
 }

 .pumpkin__flame {
   scale: 0.8;
 }

 .pumpkin__flame circle {
   transition: scale 0.2s;
   scale: 0;
 }

@media(prefers-color-scheme: dark) {
   .pumpkin__flame {
     animation: pumpkin-flicker 3s calc(var(--index, 0) * -1s) infinite linear;
   }

   .pumpkin__flame circle {
     scale: 1;
   }

   @keyframes pumpkin-flicker {
     50% {
       scale: 1;
     }
   }
 }

এই প্রতিকৃতি কি আপনাকে দেখছে?

আপনি যদি পৃষ্ঠা 10 পরীক্ষা করে দেখেন তবে আপনি কিছু লক্ষ্য করতে পারেন। আপনি দেখছেন! আপনি পৃষ্ঠার চারপাশে যাওয়ার সাথে সাথে প্রতিকৃতির চোখগুলি আপনার পয়েন্টারটি অনুসরণ করবে। এখানে কৌশলটি হ'ল পয়েন্টারের অবস্থানটি একটি অনুবাদ মানতে মানচিত্র করা এবং এটি সিএসএসে পাস করা।

const mapRange = (inputLower, inputUpper, outputLower, outputUpper, value) => {
   const INPUT_RANGE = inputUpper - inputLower
   const OUTPUT_RANGE = outputUpper - outputLower
   return outputLower + (((value - inputLower) / INPUT_RANGE) * OUTPUT_RANGE || 0)
 }

এই কোডটি ইনপুট এবং আউটপুট রেঞ্জ নেয় এবং প্রদত্ত মানগুলিকে মানচিত্র করে। উদাহরণস্বরূপ, এই ব্যবহারটি 625 মান দেবে।

mapRange(0, 100, 250, 1000, 50) // 625

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

তারপরে এটি এটি একসাথে বেঁধে এবং চোখের সিএসএস কাস্টম সম্পত্তি মান আপডেট করার ক্ষেত্রে যাতে চোখগুলি সরে যেতে পারে। একটি ফাংশন window বিপরীতে pointermove ইভেন্টের সাথে আবদ্ধ। এই আগুনের সাথে সাথে প্রতিটি চোখের সীমা কেন্দ্রের পয়েন্টগুলি গণনা করতে অভ্যস্ত হয়ে যায়। তারপরে পয়েন্টার অবস্থানটি এমন মানগুলিতে ম্যাপ করা হয় যা চোখের কাস্টম সম্পত্তি মান হিসাবে সেট করা হয়।

const RANGE = 15
const LIMIT = 80
const interact = ({ x, y }) => {
   // map a range against the eyes and pass in via custom properties
   const LEFT_EYE_BOUNDS = LEFT_EYE.getBoundingClientRect()
   const RIGHT_EYE_BOUNDS = RIGHT_EYE.getBoundingClientRect()

   const CENTERS = {
     lx: LEFT_EYE_BOUNDS.left + LEFT_EYE_BOUNDS.width * 0.5,
     rx: RIGHT_EYE_BOUNDS.left + RIGHT_EYE_BOUNDS.width * 0.5,
     ly: LEFT_EYE_BOUNDS.top + LEFT_EYE_BOUNDS.height * 0.5,
     ry: RIGHT_EYE_BOUNDS.top + RIGHT_EYE_BOUNDS.height * 0.5,
   }

   Object.entries(CENTERS)
     .forEach(([key, value]) => {
       const result = mapRange(value - LIMIT, value + LIMIT, -RANGE, RANGE)(key.indexOf('x') !== -1 ? x : y)
       EYES.style.setProperty(`--${key}`, result)
     })
 }

মানগুলি সিএসএসে চলে গেলে, শৈলীগুলি তাদের সাথে যা চায় তা করতে পারে। এখানে দুর্দান্ত অংশটি প্রতিটি চোখের জন্য আচরণকে আলাদা করতে সিএসএস clamp() ব্যবহার করছে, যাতে আপনি জাভাস্ক্রিপ্টটি আবার স্পর্শ না করে প্রতিটি চোখকে আলাদাভাবে আচরণ করতে পারেন।

.portrait__eye--mover {
   transition: translate 0.2s;
 }

 .portrait__eye--mover.portrait__eye--left {
   translate:
     clamp(-10px, var(--lx, 0) * 1px, 4px)
     clamp(-4px, var(--ly, 0) * 0.5px, 10px);
 }

 .portrait__eye--mover.portrait__eye--right {
   translate:
     clamp(-4px, var(--rx, 0) * 1px, 10px)
     clamp(-4px, var(--ry, 0) * 0.5px, 10px);
 }

কাস্টিং বানান

আপনি যদি পৃষ্ঠা ছয়টি পরীক্ষা করে দেখেন তবে আপনি কি বানান অনুভব করছেন? এই পৃষ্ঠাটি আমাদের চমত্কার যাদুকরী শিয়ালের নকশাকে আলিঙ্গন করে। আপনি যদি আপনার পয়েন্টারটিকে চারপাশে সরিয়ে রাখেন তবে আপনি একটি কাস্টম কার্সার ট্রেইল প্রভাব দেখতে পাবেন। এটি ক্যানভাস অ্যানিমেশন ব্যবহার করে। একটি <canvas> উপাদানটি pointer-events: none । এর অর্থ ব্যবহারকারীরা এখনও নীচের বিষয়বস্তু ব্লকগুলিতে ক্লিক করতে পারেন।

.wand-canvas {
  height: 100%;
  width: 200%;
  pointer-events: none;
  right: 0;
  position: fixed;
}

আমাদের প্রতিকৃতিটি window pointermove ইভেন্টের জন্য কীভাবে শোনায় তা অনেকটা পছন্দ করে, তেমনি আমাদের <canvas> উপাদানটিও তাই করে। তবুও প্রতিবার ইভেন্টটি আগুন লাগলে আমরা <canvas> উপাদানটিতে অ্যানিমেট করার জন্য একটি বস্তু তৈরি করছি। এই বস্তুগুলি কার্সার ট্রেইলে ব্যবহৃত আকারগুলি উপস্থাপন করে। তাদের স্থানাঙ্ক এবং একটি এলোমেলো রঙ রয়েছে।

পূর্ব থেকে আমাদের mapRange ফাংশনটি আবার ব্যবহার করা হয়, কারণ আমরা এটি পয়েন্টার ডেল্টাকে size এবং rate মানচিত্র করতে ব্যবহার করতে পারি। অবজেক্টগুলি এমন একটি অ্যারেতে সংরক্ষণ করা হয় যা <canvas> উপাদানটিতে যখন অবজেক্টগুলি আঁকা হয় তখন লুপ হয়ে যায়। প্রতিটি বস্তুর বৈশিষ্ট্যগুলি আমাদের <canvas> উপাদানকে যেখানে জিনিসগুলি আঁকতে হবে তা বলুন।

const blocks = []
  const createBlock = ({ x, y, movementX, movementY }) => {
    const LOWER_SIZE = CANVAS.height * 0.05
    const UPPER_SIZE = CANVAS.height * 0.25
    const size = mapRange(0, 100, LOWER_SIZE, UPPER_SIZE, Math.max(Math.abs(movementX), Math.abs(movementY)))
    const rate = mapRange(LOWER_SIZE, UPPER_SIZE, 1, 5, size)
    const { left, top, width, height } = CANVAS.getBoundingClientRect()
    
    const block = {
      hue: Math.random() * 359,
      x: x - left,
      y: y - top,
      size,
      rate,
    }
    
    blocks.push(block)
  }
window.addEventListener('pointermove', createBlock)

ক্যানভাসে আঁকার জন্য, requestAnimationFrame সাথে একটি লুপ তৈরি করা হয়। কার্সার ট্রেইলটি কেবল তখনই রেন্ডার করা উচিত যখন পৃষ্ঠাটি দেখুন। আমাদের কাছে একটি IntersectionObserver রয়েছে যা আপডেট করে এবং নির্ধারণ করে যে কোন পৃষ্ঠাগুলি দেখুন। যদি কোনও পৃষ্ঠা দেখা হয় তবে অবজেক্টগুলি ক্যানভাসে চেনাশোনা হিসাবে রেন্ডার করা হয়।

এরপরে আমরা blocks অ্যারে লুপ করে ট্রেইলের প্রতিটি অংশ আঁকি। প্রতিটি ফ্রেম আকার হ্রাস করে এবং rate দ্বারা বস্তুর অবস্থানকে পরিবর্তন করে। এটি সেই পতন এবং স্কেলিং প্রভাব উত্পাদন করে। যদি অবজেক্টটি সম্পূর্ণ সঙ্কুচিত হয় তবে অবজেক্টটি blocks অ্যারে থেকে সরানো হয়।

let wandFrame
const drawBlocks = () => {
   ctx.clearRect(0, 0, CANVAS.width, CANVAS.height)
  
   if (PAGE_SIX.className.indexOf('in-view') === -1 && wandFrame) {
     blocks.length = 0
     cancelAnimationFrame(wandFrame)
     document.body.removeEventListener('pointermove', createBlock)
     document.removeEventListener('resize', init)
   }
  
   for (let b = 0; b < blocks.length; b++) {
     const block = blocks[b]
     ctx.strokeStyle = ctx.fillStyle = `hsla(${block.hue}, 80%, 80%, 0.5)`
     ctx.beginPath()
     ctx.arc(block.x, block.y, block.size * 0.5, 0, 2 * Math.PI)
     ctx.stroke()
     ctx.fill()

     block.size -= block.rate
     block.y += block.rate

     if (block.size <= 0) {
       blocks.splice(b, 1)
     }

   }
   wandFrame = requestAnimationFrame(drawBlocks)
 }

যদি পৃষ্ঠাটি দেখার বাইরে চলে যায় তবে ইভেন্ট শ্রোতাদের সরানো হয় এবং অ্যানিমেশন ফ্রেম লুপ বাতিল করা হয়। blocks অ্যারেও সাফ করা হয়েছে।

এখানে কার্সার ট্রেইল অ্যাকশন!

অ্যাক্সেসযোগ্যতা পর্যালোচনা

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

কিছু উল্লেখযোগ্য অঞ্চল আচ্ছাদিত:

  • এইচটিএমএল ব্যবহৃত হয়েছিল তা নিশ্চিত করা শব্দার্থক ছিল। এর মধ্যে বইয়ের জন্য <main> এর মতো উপযুক্ত ল্যান্ডমার্ক উপাদানগুলির মতো জিনিস অন্তর্ভুক্ত ছিল; প্রতিটি সামগ্রী ব্লকের জন্য <article> উপাদানটির ব্যবহার এবং <abbr> উপাদানগুলি যেখানে সংক্ষিপ্ত শব্দগুলি চালু করা হয়। বইটি নির্মিত হওয়ার সাথে সাথে সামনে চিন্তা করা বিষয়গুলিকে আরও অ্যাক্সেসযোগ্য করে তুলেছে। শিরোনাম এবং লিঙ্কগুলির ব্যবহার কোনও ব্যবহারকারীর পক্ষে নেভিগেট করা সহজ করে তোলে। পৃষ্ঠাগুলির জন্য একটি তালিকা ব্যবহারের অর্থ হ'ল সহায়ক প্রযুক্তি দ্বারা পৃষ্ঠাগুলির সংখ্যা ঘোষণা করা হয়।
  • সমস্ত চিত্র উপযুক্ত alt বৈশিষ্ট্যগুলি ব্যবহার করে তা নিশ্চিত করে। ইনলাইন এসভিজিগুলির জন্য, প্রয়োজনীয় যেখানে title উপাদান উপস্থিত রয়েছে।
  • aria বৈশিষ্ট্যগুলি ব্যবহার করে যেখানে তারা অভিজ্ঞতা উন্নত করে। পৃষ্ঠাগুলির জন্য aria-label ব্যবহার এবং তাদের পক্ষগুলি ব্যবহারকারীর সাথে যোগাযোগ করে যে তারা কোন পৃষ্ঠায় রয়েছে। "আরও পড়ুন" লিঙ্কগুলিতে aria-describedBy ব্যবহার করা সামগ্রী ব্লকের পাঠ্যকে যোগাযোগ করে। লিঙ্কটি ব্যবহারকারীকে কোথায় নিয়ে যাবে সে সম্পর্কে এটি অস্পষ্টতা সরিয়ে দেয়।
  • কন্টেন্ট ব্লকগুলির বিষয়ে, পুরো কার্ডটি ক্লিক করার ক্ষমতা এবং কেবল "আরও পড়ুন" লিঙ্কটি উপলব্ধ নয়।
  • কোন পৃষ্ঠাগুলি নজরে রয়েছে তা ট্র্যাক করতে একটি IntersectionObserver ব্যবহার আগে এসেছিল। এটির অনেকগুলি সুবিধা রয়েছে যা কেবল পারফরম্যান্স সম্পর্কিত নয়। দেখার মতো পৃষ্ঠাগুলিতে কোনও অ্যানিমেশন বা মিথস্ক্রিয়া বিরতি থাকবে। তবে এই পৃষ্ঠাগুলিতে inert বৈশিষ্ট্য প্রয়োগ করা হয়েছে। এর অর্থ হ'ল স্ক্রিন রিডার ব্যবহারকারীরা দর্শনীয় ব্যবহারকারীদের মতো একই সামগ্রীটি অন্বেষণ করতে পারেন। ফোকাসটি পৃষ্ঠার মধ্যে রয়েছে যা দেখার জন্য এবং ব্যবহারকারীরা অন্য পৃষ্ঠায় ট্যাব করতে পারবেন না।
  • সর্বশেষে তবে অন্তত নয়, আমরা গতির জন্য কোনও ব্যবহারকারীর পছন্দকে সম্মান করতে মিডিয়া প্রশ্নগুলি ব্যবহার করি।

এখানে পর্যালোচনা থেকে একটি স্ক্রিনশট রয়েছে যেখানে কিছু ব্যবস্থা জায়গায় রয়েছে।

উপাদানটি পুরো বইয়ের চারপাশে চিহ্নিত করা হয়েছে, এটি নির্দেশ করে যে এটি সহায়ক প্রযুক্তি ব্যবহারকারীদের সন্ধানের জন্য প্রধান ল্যান্ডমার্ক হওয়া উচিত। আরও স্ক্রিনশটে বর্ণিত হয়েছে "" প্রস্থ = "800" উচ্চতা = "465">

ক্রোমেটোবার বইয়ের স্ক্রিনশট খোলা। সবুজ বর্ণিত বাক্সগুলি ইউআইয়ের বিভিন্ন দিকের চারপাশে সরবরাহ করা হয়, যা নির্দিষ্ট অ্যাক্সেসযোগ্যতার কার্যকারিতা এবং ব্যবহারকারীর অভিজ্ঞতার ফলাফলগুলি বর্ণনা করে যা পৃষ্ঠাটি সরবরাহ করবে। উদাহরণস্বরূপ, চিত্রগুলিতে ALT পাঠ্য রয়েছে। আরেকটি উদাহরণ হ'ল একটি অ্যাক্সেসযোগ্যতার লেবেল যা ঘোষণা করে যে পৃষ্ঠাগুলি দেখার বাইরে রয়েছে। আরও স্ক্রিনশটে বর্ণিত হয়েছে।

আমরা কি শিখেছি

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

প্রকল্পটি পরীক্ষা করতে এবং উত্থাপিত সমস্যাগুলি মোকাবেলায় আমরা নিউইয়র্কের আমাদের টিম সামিটে থাকাকালীন আমরা একটি অধিবেশন রেখেছি। দলের অবদান অমূল্য ছিল। আমরা লাইভ যাওয়ার আগে যে সমস্ত জিনিসকে মোকাবেলা করার প্রয়োজন তা তালিকাভুক্ত করারও দুর্দান্ত সুযোগ ছিল।

সিএসএস, ইউআই এবং ডেভটুলস দল একটি সম্মেলন কক্ষে টেবিলের চারপাশে বসে। উনা একটি হোয়াইটবোর্ডে দাঁড়িয়ে যা স্টিকি নোটগুলিতে আচ্ছাদিত। অন্যান্য দলের সদস্যরা রিফ্রেশমেন্ট এবং ল্যাপটপ সহ টেবিলের চারপাশে বসে।

উদাহরণস্বরূপ, ডিভাইসগুলিতে বইটি পরীক্ষা করা একটি রেন্ডারিং ইস্যু উত্থাপন করেছে। আমাদের বইটি আইওএস ডিভাইসে প্রত্যাশা অনুযায়ী রেন্ডার করবে না। ভিউপোর্ট ইউনিটগুলি পৃষ্ঠাটি আকার দেয়, তবে যখন একটি খাঁজ উপস্থিত ছিল, তখন এটি বইটিকে প্রভাবিত করে। সমাধানটি ছিল meta ভিউপোর্টে viewport-fit=cover ব্যবহার করা:

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

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

ক্রোমে একটি ডেমো খোলা একটি স্ক্রিনশট। বিকাশকারী সরঞ্জামগুলি উন্মুক্ত এবং একটি বেসলাইন পারফরম্যান্স পরিমাপ দেখায়।

ক্রোমে একটি ডেমো খোলা একটি স্ক্রিনশট। বিকাশকারী সরঞ্জামগুলি উন্মুক্ত এবং একটি উন্নত পারফরম্যান্স পরিমাপ দেখায়।

তাই তো!

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

Chrometober 2022 একটি মোড়ক।

আমরা আশা করি আপনি এটি উপভোগ করেছেন! আপনার প্রিয় বৈশিষ্ট্য কি? আমাকে টুইট করুন এবং আমাদের জানান!

জাই ক্রোমেটোবারের চরিত্রগুলির একটি স্টিকার শীট ধরে।

এমনকি আপনি যদি কোনও ইভেন্টে আমাদের দেখেন তবে আপনি দলের একটির কাছ থেকে কিছু স্টিকার ধরতে সক্ষম হতে পারেন।

আন্প্ল্যাশে ডেভিড মেনিড্রে হিরো ফটো

,

কীভাবে স্ক্রোলিং বইটি মজাদার এবং ভীতিজনক টিপস এবং এই ক্রোমেটোবারকে কৌশলগুলি ভাগ করে নেওয়ার জন্য প্রাণবন্ত হয়েছিল।

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

Web.dev/chrometober-2022 এ স্ক্রোলিং বইয়ের অভিজ্ঞতা দেখুন।

ওভারভিউ

প্রকল্পের লক্ষ্য ছিল স্ক্রোল-লিঙ্কযুক্ত অ্যানিমেশনগুলি এপিআই হাইলাইট করে একটি ছদ্মবেশী অভিজ্ঞতা সরবরাহ করা। তবে, তাত্পর্যপূর্ণ হওয়ার সাথে সাথে অভিজ্ঞতাটিও প্রতিক্রিয়াশীল এবং অ্যাক্সেসযোগ্য হওয়া দরকার। প্রকল্পটি সক্রিয় উন্নয়নে থাকা এপিআই পলিফিলটি চালানোর পরীক্ষা করারও দুর্দান্ত উপায় হয়ে দাঁড়িয়েছে; এটি, পাশাপাশি সংমিশ্রণে বিভিন্ন কৌশল এবং সরঞ্জাম চেষ্টা করে। এবং সমস্ত উত্সব হ্যালোইন থিম সঙ্গে!

আমাদের দলের কাঠামো এর মতো দেখাচ্ছে:

একটি স্ক্রোলিটেলিং অভিজ্ঞতা খসড়া

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

একটি নোটবুক প্রকল্পের সাথে সম্পর্কিত বিভিন্ন ডুডল এবং স্ক্রিবিল সহ একটি ডেস্কে অবস্থিত।

আমার প্রথম গুগল প্রকল্পটি অপ্রত্যাশিত দিকে নিয়ে যাওয়ার সৃজনশীল স্বাধীনতা পাওয়া উত্তেজনাপূর্ণ ছিল। এটি কোনও ব্যবহারকারী কীভাবে সামগ্রীর মাধ্যমে নেভিগেট করতে পারে তার একটি প্রাথমিক প্রোটোটাইপ ছিল।

ব্যবহারকারী পাশের দিকে স্ক্রোল করার সাথে সাথে ব্লকগুলি ঘোরানো এবং স্কেল করে But পরিবর্তে, আমি অতীতে আমি তৈরি করা কিছু ডিজাইনের দিকে ঝুঁকেছি। 2020 সালে, আমি রিলিজ ডেমো তৈরির জন্য গ্রিনসকের স্ক্রোলট্রিগারে অ্যাক্সেস পাওয়ার সৌভাগ্যবান।

আমি যে ডেমোগুলি তৈরি করেছি তার মধ্যে একটি ছিল একটি 3 ডি-সিএসএস বই যেখানে আপনি স্ক্রোল করার সাথে সাথে পৃষ্ঠাগুলি পরিণত হয়েছিল এবং এটি ক্রোমেটোবারের জন্য আমরা যা চেয়েছিলাম তার জন্য এটি আরও বেশি উপযুক্ত বলে মনে হয়েছিল। স্ক্রোল-লিঙ্কযুক্ত অ্যানিমেশন এপিআই সেই কার্যকারিতার জন্য একটি নিখুঁত অদলবদল। এটি scroll-snap সাথেও ভাল কাজ করে, যেমন আপনি দেখতে পাবেন!

প্রকল্পের জন্য আমাদের চিত্রকর, টাইলার রিড , আমরা ধারণাগুলি পরিবর্তন করার সাথে সাথে নকশাটি পরিবর্তন করতে দুর্দান্ত ছিলেন। টাইলার তাঁর দিকে ছুঁড়ে দেওয়া সমস্ত সৃজনশীল ধারণা গ্রহণ এবং তাদেরকে প্রাণবন্ত করার জন্য দুর্দান্ত কাজ করেছিলেন। এটি একসাথে অনেক মজাদার মস্তিষ্কের ধারণা ছিল। আমরা কীভাবে এটি কাজ করতে চেয়েছিলাম তার একটি বড় অংশটি বিচ্ছিন্ন ব্লকগুলিতে বিভক্ত বৈশিষ্ট্যগুলি ছিল। এইভাবে, আমরা তাদের দৃশ্যে রচনা করতে পারি এবং তারপরে আমরা কী জীবনে নিয়ে এসেছি তা বেছে নিতে এবং চয়ন করতে পারি।

একটি সাপের বৈশিষ্ট্যযুক্ত একটি রচনা দৃশ্য, অস্ত্রের সাথে একটি কফিন, একটি কফিন আসছে, একটি ছালযুক্ত একটি শিয়াল, একটি ভুতুড়ে মুখযুক্ত একটি গাছ এবং একটি কুমড়ো লণ্ঠন ধারণ করে একটি গারগোয়েল।

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

এপিআইয়ের সাথে পরিচিত হচ্ছে

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

একটি উচ্চ স্তরে, আপনি স্ক্রোল করতে অ্যানিমেশনগুলি লিঙ্ক করতে এই এপিআই ব্যবহার করতে পারেন। এটি লক্ষ করা গুরুত্বপূর্ণ যে আপনি স্ক্রোলে কোনও অ্যানিমেশন ট্রিগার করতে পারবেন না - এটি এমন কিছু যা পরে আসতে পারে। স্ক্রোল-লিঙ্কযুক্ত অ্যানিমেশনগুলি দুটি প্রধান বিভাগেও পড়ে:

  1. যারা স্ক্রোল পজিশনে প্রতিক্রিয়া জানায়।
  2. যেগুলি তার স্ক্রোলিং পাত্রে কোনও উপাদানটির অবস্থানের প্রতি প্রতিক্রিয়া জানায়।

পরবর্তীটি তৈরি করতে, আমরা animation-timeline সম্পত্তির মাধ্যমে প্রয়োগ করা একটি ViewTimeline ব্যবহার করি।

ViewTimeline ব্যবহার করে সিএসএসে দেখতে কেমন লাগে তার একটি উদাহরণ এখানে:

.element-moving-in-viewport {
  view-timeline-name: foo;
  view-timeline-axis: block;
}

.element-scroll-linked {
  animation: rotate both linear;
  animation-timeline: foo;
  animation-delay: enter 0%;
  animation-end-delay: cover 50%;
}

@keyframes rotate {
 to {
   rotate: 360deg;
 }
}

আমরা view-timeline-name সহ একটি ViewTimeline তৈরি করি এবং এর জন্য অক্ষটি সংজ্ঞায়িত করি। এই উদাহরণে, block লজিকাল block বোঝায়। অ্যানিমেশনটি animation-timeline সম্পত্তির সাথে স্ক্রোলের সাথে সংযুক্ত হয়ে যায়। animation-delay এবং animation-end-delay (লেখার সময়) হ'ল আমরা কীভাবে পর্যায়গুলি সংজ্ঞায়িত করি।

এই পর্যায়গুলি এমন পয়েন্টগুলি সংজ্ঞায়িত করে যেখানে অ্যানিমেশনটি তার স্ক্রোলিং ধারকটিতে কোনও উপাদানটির অবস্থানের সাথে যুক্ত হওয়া উচিত। আমাদের উদাহরণে, আমরা বলছি যখন উপাদানটি প্রবেশ করে ( enter 0% ) স্ক্রোলিং ধারকটিতে অ্যানিমেশন শুরু করুন। এবং এটি যখন স্ক্রোলিং ধারকটির 50% ( cover 50% ) কভার করে তখন শেষ করুন।

এখানে আমাদের ডেমো কর্মে রয়েছে:

আপনি ভিউপোর্টে চলমান উপাদানটির সাথে একটি অ্যানিমেশনকেও সংযুক্ত করতে পারেন। উপাদানটির view-timeline হিসাবে animation-timeline সেট করে আপনি এটি করতে পারেন। এটি তালিকা অ্যানিমেশনগুলির মতো দৃশ্যের জন্য ভাল। আচরণটি কীভাবে আপনি IntersectionObserver ব্যবহার করে প্রবেশের সময় উপাদানগুলিকে অ্যানিমেট করতে পারেন তার অনুরূপ।

element-moving-in-viewport {
  view-timeline-name: foo;
  view-timeline-axis: block;
  animation: scale both linear;
  animation-delay: enter 0%;
  animation-end-delay: cover 50%;
  animation-timeline: foo;
}

@keyframes scale {
  0% {
    scale: 0;
  }
}

এটির সাথে, "স্পিনার" এর ঘূর্ণনকে ট্রিগার করে ভিউপোর্টে প্রবেশের সাথে সাথে "মুভার" স্কেল করে।

পরীক্ষা থেকে আমি যা পেয়েছি তা হ'ল এপিআই স্ক্রোল-স্ন্যাপের সাথে খুব ভাল কাজ করে। ViewTimeline সাথে মিলিত স্ক্রোল-এসএনএপি কোনও বইতে পৃষ্ঠাগুলি স্ন্যাপিংয়ের জন্য দুর্দান্ত ফিট হবে।

মেকানিক্স প্রোটোটাইপিং

কিছু পরীক্ষার পরে, আমি একটি বই প্রোটোটাইপ কাজ করতে সক্ষম হয়েছি। আপনি বইয়ের পৃষ্ঠাগুলি ঘুরিয়ে দেওয়ার জন্য অনুভূমিকভাবে স্ক্রোল করুন।

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

মার্কআপটি দেখতে কিছুটা দেখতে:

<body>
  <div class="book-placeholder">
    <ul class="book" style="--count: 7;">
      <li
        class="page page--cover page--cover-front"
        data-scroll-target="1"
        style="--index: 0;"
      >
        <div class="page__paper">
          <div class="page__side page__side--front"></div>
          <div class="page__side page__side--back"></div>
        </div>
      </li>
      <!-- Markup for other pages here -->
    </ul>
  </div>
  <div>
    <p>intro spacer</p>
  </div>
  <div data-scroll-intro>
    <p>scale trigger</p>
  </div>
  <div data-scroll-trigger="1">
    <p>page trigger</p>
  </div>
  <!-- Markup for other triggers here -->
</body>

আপনি স্ক্রোল করার সাথে সাথে বইয়ের পাতাগুলি ঘুরিয়ে দেয় তবে স্ন্যাপ খোলা বা বন্ধ। এটি ট্রিগারগুলির স্ক্রোল-এসএনএপি সারিবদ্ধকরণের উপর নির্ভরশীল।

html {
  scroll-snap-type: x mandatory;
}

body {
  grid-template-columns: repeat(var(--trigger-count), auto);
  overflow-y: hidden;
  overflow-x: scroll;
  display: grid;
}

body > [data-scroll-trigger] {
  height: 100vh;
  width: clamp(10rem, 10vw, 300px);
}

body > [data-scroll-trigger] {
  scroll-snap-align: end;
}

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

const triggers = document.querySelectorAll("[data-scroll-trigger]")

const commonProps = {
  delay: { phase: "enter", percent: CSS.percent(0) },
  endDelay: { phase: "enter", percent: CSS.percent(100) },
  fill: "both"
}

const setupPage = (trigger, index) => {
  const target = document.querySelector(
    `[data-scroll-target="${trigger.getAttribute("data-scroll-trigger")}"]`
  );

  const viewTimeline = new ViewTimeline({
    subject: trigger,
    axis: 'inline',
  });

  target.animate(
    [
      {
        transform: `translateZ(${(triggers.length - index) * 2}px)`
      },
      {
        transform: `translateZ(${(triggers.length - index) * 2}px)`,
        offset: 0.75
      },
      {
        transform: `translateZ(${(triggers.length - index) * -1}px)`
      }
    ],
    {
      timeline: viewTimeline,
      commonProps,
    }
  );
  target.querySelector(".page__paper").animate(
    [
      {
        transform: "rotateY(0deg)"
      },
      {
        transform: "rotateY(-180deg)"
      }
    ],
    {
      timeline: viewTimeline,
      commonProps,
    }
  );
};

const triggers = document.querySelectorAll('[data-scroll-trigger]')
triggers.forEach(setupPage);

প্রতিটি ট্রিগার জন্য, আমরা একটি ViewTimeline উত্পন্ন করি। তারপরে আমরা সেই ViewTimeline ব্যবহার করে ট্রিগারের সম্পর্কিত পৃষ্ঠাটি অ্যানিমেট করি। এটি পৃষ্ঠার অ্যানিমেশনটিকে স্ক্রোলের সাথে সংযুক্ত করে। আমাদের অ্যানিমেশনের জন্য, আমরা পৃষ্ঠাটি ঘুরিয়ে দেওয়ার জন্য ওয়াই-অক্ষের পৃষ্ঠার একটি উপাদানকে ঘোরান। আমরা পৃষ্ঠাটি নিজেই জেড-অক্ষে অনুবাদ করি যাতে এটি কোনও বইয়ের মতো আচরণ করে।

এটা সব একসাথে নির্বাণ

একবার আমি বইটির প্রক্রিয়াটি কাজ করার পরে, আমি টাইলারের চিত্রগুলি জীবনে আনার দিকে মনোনিবেশ করতে পারি।

অ্যাস্ট্রো

দলটি 2021 সালে ডিজাইনসেম্বারের জন্য অ্যাস্ট্রো ব্যবহার করেছিল এবং আমি ক্রোমেটোবারের জন্য এটি আবার ব্যবহার করতে আগ্রহী ছিলাম। উপাদানগুলিতে জিনিসগুলিকে ভাঙতে সক্ষম হওয়ার বিকাশকারী অভিজ্ঞতা এই প্রকল্পের পক্ষে উপযুক্ত।

বইটি নিজেই একটি উপাদান। এটি পৃষ্ঠার উপাদানগুলির সংগ্রহও। প্রতিটি পৃষ্ঠায় দুটি পক্ষ রয়েছে এবং তাদের ব্যাকড্রপ রয়েছে। একটি পৃষ্ঠার পাশের বাচ্চারা এমন উপাদান যা যোগ করা, অপসারণ এবং স্বাচ্ছন্দ্যের সাথে অবস্থান করা যায়।

একটি বই নির্মাণ

ব্লকগুলি পরিচালনা করা সহজ করা আমার পক্ষে গুরুত্বপূর্ণ ছিল। আমি দলের বাকি সদস্যদের অবদান রাখতে সহজ করে তুলতে চেয়েছিলাম।

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

const pages = [
  {
    front: {
      marked: true,
      content: PageTwo,
      backdrop: spreadOne,
      darkBackdrop: spreadOneDark
    },
    back: {
      content: PageThree,
      backdrop: spreadTwo,
      darkBackdrop: spreadTwoDark
    },
    aria: `page 1`
  },
  /* Obfuscated page objects */
]

এগুলি Book উপাদানটিতে চলে যায়।

<Book pages={pages} />

Book উপাদানটি যেখানে স্ক্রোলিং প্রক্রিয়া প্রয়োগ করা হয় এবং বইয়ের পৃষ্ঠাগুলি তৈরি করা হয়। প্রোটোটাইপ থেকে একই প্রক্রিয়া ব্যবহৃত হয়; তবে আমরা বিশ্বব্যাপী তৈরি হওয়া ViewTimeline একাধিক উদাহরণ ভাগ করি।

window.CHROMETOBER_TIMELINES.push(viewTimeline);

এইভাবে, আমরা টাইমলাইনগুলি পুনরুদ্ধার করার পরিবর্তে অন্য কোথাও ব্যবহার করার জন্য ভাগ করতে পারি। এই বিষয়ে পরে আরো.

পৃষ্ঠা রচনা

প্রতিটি পৃষ্ঠা একটি তালিকার ভিতরে একটি তালিকা আইটেম:

<ul class="book">
  {
    pages.map((page, index) => {
      const FrontSlot = page.front.content
      const BackSlot = page.back.content
      return (
        <Page
          index={index}
          cover={page.cover}
          aria={page.aria}
          backdrop={
            {
              front: {
                light: page.front.backdrop,
                dark: page.front.darkBackdrop
              },
              back: {
                light: page.back.backdrop,
                dark: page.back.darkBackdrop
              }
            }
          }>
          {page.front.content && <FrontSlot slot="front" />}    
          {page.back.content && <BackSlot slot="back" />}    
        </Page>
      )
    })
  }
</ul>

এবং সংজ্ঞায়িত কনফিগারেশন প্রতিটি Page উদাহরণে চলে যায়। পৃষ্ঠাগুলি প্রতিটি পৃষ্ঠায় সামগ্রী সন্নিবেশ করতে অ্যাস্ট্রোর স্লট বৈশিষ্ট্য ব্যবহার করে।

<li
  class={className}
  data-scroll-target={target}
  style={`--index:${index};`}
  aria-label={aria}
>
  <div class="page__paper">
    <div
      class="page__side page__side--front"
      aria-label={`Right page of ${index}`}
    >
      <picture>
        <source
          srcset={darkFront}
          media="(prefers-color-scheme: dark)"
          height="214"
          width="150"
        >
        <img
          src={lightFront}
          class="page__background page__background--right"
          alt=""
          aria-hidden="true"
          height="214"
          width="150"
        >
      </picture>
      <div class="page__content">
        <slot name="front" />
      </div>
    </div>
    <!-- Markup for back page -->
  </div>
</li>

এই কোডটি বেশিরভাগ কাঠামো স্থাপনের জন্য। অবদানকারীরা এই কোডটি স্পর্শ না করে বেশিরভাগ অংশের জন্য বইয়ের সামগ্রীতে কাজ করতে পারেন।

ব্যাকড্রপ

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

পৃষ্ঠাটি বইটি থেকে চিত্র ছড়িয়ে দিন যা একটি কবরস্থানে একটি আপেল ট্রি বৈশিষ্ট্যযুক্ত। কবরস্থানের একাধিক হেডস্টোন রয়েছে এবং একটি বড় চাঁদের সামনে আকাশে একটি ব্যাট রয়েছে।

যেমনটি আমরা বইটির জন্য একটি দিক অনুপাতের বিষয়ে সিদ্ধান্ত নিয়েছিলাম, প্রতিটি পৃষ্ঠার পটভূমিতে একটি চিত্রের উপাদান থাকতে পারে। সেই উপাদানটিকে 200% প্রস্থে সেট করা এবং পৃষ্ঠা পক্ষের উপর ভিত্তি করে object-position ব্যবহার করা কৌশলটি করে।

.page__background {
  height: 100%;
  width: 200%;
  object-fit: cover;
  object-position: 0 0;
  position: absolute;
  top: 0;
  left: 0;
}

.page__background--right {
  object-position: 100% 0;
}

পৃষ্ঠার বিষয়বস্তু

আসুন একটি পৃষ্ঠাগুলি তৈরি করার দিকে নজর দিন। পৃষ্ঠা তিনটি বৈশিষ্ট্যযুক্ত একটি পেঁচা যা একটি গাছে পপ আপ করে।

এটি কনফিগারেশনে সংজ্ঞায়িত হিসাবে একটি PageThree উপাদান দিয়ে পপুলেট হয়ে যায়। এটি একটি অ্যাস্ট্রো উপাদান ( PageThree.astro )। এই উপাদানগুলি এইচটিএমএল ফাইলগুলির মতো দেখায় তবে তাদের ফ্রন্টম্যাটারের অনুরূপ শীর্ষে একটি কোড বেড়া রয়েছে। এটি আমাদের অন্যান্য উপাদানগুলি আমদানি করার মতো জিনিসগুলি করতে সক্ষম করে। পৃষ্ঠার তিনটির উপাদানটি দেখতে এরকম দেখাচ্ছে:

---
import TreeOwl from '../TreeOwl/TreeOwl.astro'
import { contentBlocks } from '../../assets/content-blocks.json'
import ContentBlock from '../ContentBlock/ContentBlock.astro'
---
<TreeOwl/>
<ContentBlock {...contentBlocks[3]} id="four" />

<style is:global>
  .content-block--four {
    left: 30%;
    bottom: 10%;
  }
</style>

আবার পৃষ্ঠাগুলি প্রকৃতির পারমাণবিক। এগুলি বৈশিষ্ট্যগুলির সংগ্রহ থেকে নির্মিত। পৃষ্ঠা তিনটি একটি সামগ্রী ব্লক এবং ইন্টারেক্টিভ আউল বৈশিষ্ট্যযুক্ত, সুতরাং প্রতিটি জন্য একটি উপাদান আছে।

সামগ্রী ব্লকগুলি বইয়ের মধ্যে দেখা সামগ্রীর লিঙ্ক। এগুলি একটি কনফিগারেশন অবজেক্ট দ্বারা চালিত হয়।

{
 "contentBlocks": [
    {
      "id": "one",
      "title": "New in Chrome",
      "blurb": "Lift your spirits with a round up of all the tools and features in Chrome.",
      "link": "https://www.youtube.com/watch?v=qwdN1fJA_d8&list=PLNYkxOF6rcIDfz8XEA3loxY32tYh7CI3m"
    },
    …otherBlocks
  ]
}

এই কনফিগারেশনটি আমদানি করে যেখানে সামগ্রী ব্লকগুলির প্রয়োজন হয়। তারপরে প্রাসঙ্গিক ব্লক কনফিগারেশনটি ContentBlock উপাদানটিতে চলে যায়।

<ContentBlock {...contentBlocks[3]} id="four" />

সামগ্রীটি অবস্থান করার জন্য আমরা কীভাবে পৃষ্ঠার উপাদানটি ব্যবহার করি তার একটি উদাহরণ এখানে রয়েছে। এখানে, একটি সামগ্রী ব্লক অবস্থান পায়।

<style is:global>
  .content-block--four {
    left: 30%;
    bottom: 10%;
  }
</style>

তবে, একটি সামগ্রী ব্লকের জন্য সাধারণ শৈলীগুলি উপাদান কোডের সাথে সহ-অবস্থিত।

.content-block {
  background: hsl(0deg 0% 0% / 70%);
  color: var(--gray-0);
  border-radius:  min(3vh, var(--size-4));
  padding: clamp(0.75rem, 2vw, 1.25rem);
  display: grid;
  gap: var(--size-2);
  position: absolute;
  cursor: pointer;
  width: 50%;
}

আমাদের পেঁচা হিসাবে, এটি একটি ইন্টারেক্টিভ বৈশিষ্ট্য - এই প্রকল্পের অনেকের মধ্যে একটি। এটি একটি দুর্দান্ত ছোট উদাহরণ যা দেখায় যে আমরা কীভাবে ভাগ করা ভিউটাইমলাইনটি তৈরি করেছি তা কীভাবে ব্যবহার করেছি।

একটি উচ্চ স্তরে, আমাদের পেঁচা উপাদান কিছু এসভিজি আমদানি করে এবং এটি অ্যাস্ট্রোর খণ্ড ব্যবহার করে ইনলাইন করে।

---
import { default as Owl } from '../Features/Owl.svg?raw'
---
<Fragment set:html={Owl} />

এবং আমাদের পেঁচা অবস্থানের জন্য শৈলীগুলি উপাদান কোডের সাথে সহ-অবস্থিত।

.owl {
  width: 34%;
  left: 10%;
  bottom: 34%;
}

স্টাইলিংয়ের একটি অতিরিক্ত টুকরো রয়েছে যা পেঁচাগুলির জন্য transform আচরণকে সংজ্ঞায়িত করে।

.owl__owl {
  transform-origin: 50% 100%;
  transform-box: fill-box;
}

transform-box ব্যবহার transform-origin প্রভাবিত করে। এটি এসভিজির মধ্যে অবজেক্টের সীমাবদ্ধ বাক্সের সাথে সম্পর্কিত করে তোলে। পেঁচাটি নীচের কেন্দ্র থেকে স্কেল করে, তাই transform-origin: 50% 100%

মজার অংশটি হ'ল যখন আমরা আমাদের উত্পন্ন ViewTimeline একটিতে পেঁচাটি সংযুক্ত করি:

const setUpOwl = () => {
   const owl = document.querySelector('.owl__owl');

   owl.animate([
     {
       translate: '0% 110%',
     },
     {
       translate: '0% 10%',
     },
   ], {
     timeline: CHROMETOBER_TIMELINES[1],
     delay: { phase: "enter", percent: CSS.percent(80) },
     endDelay: { phase: "enter", percent: CSS.percent(90) },
     fill: 'both' 
   });
 }

 if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches)
   setUpOwl()

কোডের এই ব্লকে, আমরা দুটি জিনিস করি:

  1. ব্যবহারকারীর গতি পছন্দগুলি পরীক্ষা করুন।
  2. যদি তাদের কোনও পছন্দ না থাকে তবে আউলের একটি অ্যানিমেশন স্ক্রোল করার জন্য লিঙ্ক করুন।

দ্বিতীয় অংশের জন্য, পেঁচা ওয়েব অ্যানিমেশনগুলি এপিআই ব্যবহার করে ওয়াই-অক্ষগুলিতে অ্যানিমেট করে। স্বতন্ত্র রূপান্তর সম্পত্তি translate ব্যবহৃত হয় এবং এটি একটি ViewTimeline সাথে যুক্ত। এটি timeline সম্পত্তির মাধ্যমে CHROMETOBER_TIMELINES[1] এর সাথে যুক্ত। এটি একটি ViewTimeline যা পৃষ্ঠার মোড়ের জন্য উত্পন্ন হয়। এটি enter পর্বটি ব্যবহার করে পেঁচার অ্যানিমেশনটিকে পৃষ্ঠার টার্নের সাথে লিঙ্ক করে। এটি সংজ্ঞায়িত করে, যখন পৃষ্ঠাটি 80% পরিণত হয়, তখন পেঁচাটি সরানো শুরু করুন। 90%এ, আউলের অনুবাদটি শেষ করা উচিত।

বইয়ের বৈশিষ্ট্য

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

এটিতে এমন উপাদান রয়েছে যা সিএসএস অ্যানিমেশন দ্বারা চালিত হয়।

একবার সামগ্রী ব্লকগুলি বইটিতে আসার পরে, অন্যান্য বৈশিষ্ট্যগুলির সাথে সৃজনশীল হওয়ার সময় ছিল। এটি কিছু আলাদা ইন্টারঅ্যাকশন তৈরি করার এবং জিনিসগুলি বাস্তবায়নের জন্য বিভিন্ন উপায় চেষ্টা করার একটি সুযোগ সরবরাহ করেছিল।

জিনিস প্রতিক্রিয়াশীল রাখা

প্রতিক্রিয়াশীল ভিউপোর্ট ইউনিটগুলি বই এবং এর বৈশিষ্ট্যগুলি আকার দেয়। তবে, ফন্টগুলি প্রতিক্রিয়াশীল রাখা একটি আকর্ষণীয় চ্যালেঞ্জ ছিল। কনটেইনার ক্যোয়ারী ইউনিটগুলি এখানে একটি ভাল ফিট। যদিও তারা এখনও সর্বত্র সমর্থিত নয়। বইয়ের আকার সেট করা আছে, সুতরাং আমাদের কোনও ধারক ক্যোয়ারির দরকার নেই। একটি ইনলাইন কনটেইনার ক্যোয়ারী ইউনিট সিএসএস calc() দিয়ে তৈরি করা যেতে পারে এবং ফন্ট সাইজিংয়ের জন্য ব্যবহৃত হয়।


.book-placeholder {
  --size: clamp(12rem, 72vw, 80vmin);
  --aspect-ratio: 360 / 504;
  --cqi: calc(0.01 * (var(--size) * (var(--aspect-ratio))));
}

.content-block h2 {
  color: var(--gray-0);
  font-size: clamp(0.6rem, var(--cqi) * 4, 1.5rem);
}

.content-block :is(p, a) {
  font-size: clamp(0.6rem, var(--cqi) * 3, 1.5rem);
}

রাতে কুমড়ো জ্বলজ্বল

আগ্রহী চোখের সাথে যারা পৃষ্ঠা ব্যাকড্রপগুলি আগে আলোচনা করার সময় <source> উপাদানগুলির ব্যবহার লক্ষ্য করেছেন। উনা একটি মিথস্ক্রিয়া করতে আগ্রহী ছিল যা রঙিন স্কিম পছন্দকে প্রতিক্রিয়া জানায়। ফলস্বরূপ, ব্যাকড্রপগুলি বিভিন্ন রূপগুলির সাথে হালকা এবং গা dark ় উভয় মোডকে সমর্থন করে। যেহেতু আপনি <picture> উপাদান সহ মিডিয়া প্রশ্নগুলি ব্যবহার করতে পারেন, এটি দুটি ব্যাকড্রপ স্টাইল সরবরাহ করার দুর্দান্ত উপায়। রঙ স্কিম পছন্দগুলির জন্য <source> উত্স> উপাদান প্রশ্নগুলি এবং উপযুক্ত ব্যাকড্রপটি দেখায়।

<picture>
  <source srcset={darkFront} media="(prefers-color-scheme: dark)" height="214" width="150">
  <img src={lightFront} class="page__background page__background--right" alt="" aria-hidden="true" height="214" width="150">
</picture>

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

.pumpkin__flame,
 .pumpkin__flame circle {
   transform-box: fill-box;
   transform-origin: 50% 100%;
 }

 .pumpkin__flame {
   scale: 0.8;
 }

 .pumpkin__flame circle {
   transition: scale 0.2s;
   scale: 0;
 }

@media(prefers-color-scheme: dark) {
   .pumpkin__flame {
     animation: pumpkin-flicker 3s calc(var(--index, 0) * -1s) infinite linear;
   }

   .pumpkin__flame circle {
     scale: 1;
   }

   @keyframes pumpkin-flicker {
     50% {
       scale: 1;
     }
   }
 }

এই প্রতিকৃতি কি আপনাকে দেখছে?

আপনি যদি পৃষ্ঠা 10 পরীক্ষা করে দেখেন তবে আপনি কিছু লক্ষ্য করতে পারেন। আপনি দেখছেন! আপনি পৃষ্ঠার চারপাশে যাওয়ার সাথে সাথে প্রতিকৃতির চোখগুলি আপনার পয়েন্টারটি অনুসরণ করবে। এখানে কৌশলটি হ'ল পয়েন্টারের অবস্থানটি একটি অনুবাদ মানতে মানচিত্র করা এবং এটি সিএসএসে পাস করা।

const mapRange = (inputLower, inputUpper, outputLower, outputUpper, value) => {
   const INPUT_RANGE = inputUpper - inputLower
   const OUTPUT_RANGE = outputUpper - outputLower
   return outputLower + (((value - inputLower) / INPUT_RANGE) * OUTPUT_RANGE || 0)
 }

এই কোডটি ইনপুট এবং আউটপুট রেঞ্জ নেয় এবং প্রদত্ত মানগুলিকে মানচিত্র করে। উদাহরণস্বরূপ, এই ব্যবহারটি 625 মান দেবে।

mapRange(0, 100, 250, 1000, 50) // 625

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

তারপরে এটি এটি একসাথে বেঁধে এবং চোখের সিএসএস কাস্টম সম্পত্তি মান আপডেট করার ক্ষেত্রে যাতে চোখগুলি সরে যেতে পারে। একটি ফাংশন window বিপরীতে pointermove ইভেন্টের সাথে আবদ্ধ। এই আগুনের সাথে সাথে প্রতিটি চোখের সীমা কেন্দ্রের পয়েন্টগুলি গণনা করতে অভ্যস্ত হয়ে যায়। তারপরে পয়েন্টার অবস্থানটি এমন মানগুলিতে ম্যাপ করা হয় যা চোখের কাস্টম সম্পত্তি মান হিসাবে সেট করা হয়।

const RANGE = 15
const LIMIT = 80
const interact = ({ x, y }) => {
   // map a range against the eyes and pass in via custom properties
   const LEFT_EYE_BOUNDS = LEFT_EYE.getBoundingClientRect()
   const RIGHT_EYE_BOUNDS = RIGHT_EYE.getBoundingClientRect()

   const CENTERS = {
     lx: LEFT_EYE_BOUNDS.left + LEFT_EYE_BOUNDS.width * 0.5,
     rx: RIGHT_EYE_BOUNDS.left + RIGHT_EYE_BOUNDS.width * 0.5,
     ly: LEFT_EYE_BOUNDS.top + LEFT_EYE_BOUNDS.height * 0.5,
     ry: RIGHT_EYE_BOUNDS.top + RIGHT_EYE_BOUNDS.height * 0.5,
   }

   Object.entries(CENTERS)
     .forEach(([key, value]) => {
       const result = mapRange(value - LIMIT, value + LIMIT, -RANGE, RANGE)(key.indexOf('x') !== -1 ? x : y)
       EYES.style.setProperty(`--${key}`, result)
     })
 }

মানগুলি সিএসএসে চলে গেলে, শৈলীগুলি তাদের সাথে যা চায় তা করতে পারে। এখানে দুর্দান্ত অংশটি প্রতিটি চোখের জন্য আচরণকে আলাদা করতে সিএসএস clamp() ব্যবহার করছে, যাতে আপনি জাভাস্ক্রিপ্টটি আবার স্পর্শ না করে প্রতিটি চোখকে আলাদাভাবে আচরণ করতে পারেন।

.portrait__eye--mover {
   transition: translate 0.2s;
 }

 .portrait__eye--mover.portrait__eye--left {
   translate:
     clamp(-10px, var(--lx, 0) * 1px, 4px)
     clamp(-4px, var(--ly, 0) * 0.5px, 10px);
 }

 .portrait__eye--mover.portrait__eye--right {
   translate:
     clamp(-4px, var(--rx, 0) * 1px, 10px)
     clamp(-4px, var(--ry, 0) * 0.5px, 10px);
 }

কাস্টিং বানান

আপনি যদি পৃষ্ঠা ছয়টি পরীক্ষা করে দেখেন তবে আপনি কি বানান অনুভব করছেন? এই পৃষ্ঠাটি আমাদের চমত্কার যাদুকরী শিয়ালের নকশাকে আলিঙ্গন করে। আপনি যদি আপনার পয়েন্টারটিকে চারপাশে সরিয়ে রাখেন তবে আপনি একটি কাস্টম কার্সার ট্রেইল প্রভাব দেখতে পাবেন। এটি ক্যানভাস অ্যানিমেশন ব্যবহার করে। একটি <canvas> উপাদানটি pointer-events: none । এর অর্থ ব্যবহারকারীরা এখনও নীচের বিষয়বস্তু ব্লকগুলিতে ক্লিক করতে পারেন।

.wand-canvas {
  height: 100%;
  width: 200%;
  pointer-events: none;
  right: 0;
  position: fixed;
}

আমাদের প্রতিকৃতিটি window pointermove ইভেন্টের জন্য কীভাবে শোনায় তা অনেকটা পছন্দ করে, তেমনি আমাদের <canvas> উপাদানটিও তাই করে। তবুও প্রতিবার ইভেন্টটি আগুন লাগলে আমরা <canvas> উপাদানটিতে অ্যানিমেট করার জন্য একটি বস্তু তৈরি করছি। এই বস্তুগুলি কার্সার ট্রেইলে ব্যবহৃত আকারগুলি উপস্থাপন করে। তাদের স্থানাঙ্ক এবং একটি এলোমেলো রঙ রয়েছে।

পূর্ব থেকে আমাদের mapRange ফাংশনটি আবার ব্যবহার করা হয়, কারণ আমরা এটি পয়েন্টার ডেল্টাকে size এবং rate মানচিত্র করতে ব্যবহার করতে পারি। অবজেক্টগুলি এমন একটি অ্যারেতে সংরক্ষণ করা হয় যা <canvas> উপাদানটিতে যখন অবজেক্টগুলি আঁকা হয় তখন লুপ হয়ে যায়। প্রতিটি বস্তুর বৈশিষ্ট্যগুলি আমাদের <canvas> উপাদানকে যেখানে জিনিসগুলি আঁকতে হবে তা বলুন।

const blocks = []
  const createBlock = ({ x, y, movementX, movementY }) => {
    const LOWER_SIZE = CANVAS.height * 0.05
    const UPPER_SIZE = CANVAS.height * 0.25
    const size = mapRange(0, 100, LOWER_SIZE, UPPER_SIZE, Math.max(Math.abs(movementX), Math.abs(movementY)))
    const rate = mapRange(LOWER_SIZE, UPPER_SIZE, 1, 5, size)
    const { left, top, width, height } = CANVAS.getBoundingClientRect()
    
    const block = {
      hue: Math.random() * 359,
      x: x - left,
      y: y - top,
      size,
      rate,
    }
    
    blocks.push(block)
  }
window.addEventListener('pointermove', createBlock)

ক্যানভাসে আঁকার জন্য, requestAnimationFrame সাথে একটি লুপ তৈরি করা হয়। কার্সার ট্রেইলটি কেবল তখনই রেন্ডার করা উচিত যখন পৃষ্ঠাটি দেখুন। আমাদের কাছে একটি IntersectionObserver রয়েছে যা আপডেট করে এবং নির্ধারণ করে যে কোন পৃষ্ঠাগুলি দেখুন। যদি কোনও পৃষ্ঠা দেখা হয় তবে অবজেক্টগুলি ক্যানভাসে চেনাশোনা হিসাবে রেন্ডার করা হয়।

এরপরে আমরা blocks অ্যারে লুপ করে ট্রেইলের প্রতিটি অংশ আঁকি। প্রতিটি ফ্রেম আকার হ্রাস করে এবং rate দ্বারা বস্তুর অবস্থানকে পরিবর্তন করে। এটি সেই পতন এবং স্কেলিং প্রভাব উত্পাদন করে। যদি অবজেক্টটি সম্পূর্ণ সঙ্কুচিত হয় তবে অবজেক্টটি blocks অ্যারে থেকে সরানো হয়।

let wandFrame
const drawBlocks = () => {
   ctx.clearRect(0, 0, CANVAS.width, CANVAS.height)
  
   if (PAGE_SIX.className.indexOf('in-view') === -1 && wandFrame) {
     blocks.length = 0
     cancelAnimationFrame(wandFrame)
     document.body.removeEventListener('pointermove', createBlock)
     document.removeEventListener('resize', init)
   }
  
   for (let b = 0; b < blocks.length; b++) {
     const block = blocks[b]
     ctx.strokeStyle = ctx.fillStyle = `hsla(${block.hue}, 80%, 80%, 0.5)`
     ctx.beginPath()
     ctx.arc(block.x, block.y, block.size * 0.5, 0, 2 * Math.PI)
     ctx.stroke()
     ctx.fill()

     block.size -= block.rate
     block.y += block.rate

     if (block.size <= 0) {
       blocks.splice(b, 1)
     }

   }
   wandFrame = requestAnimationFrame(drawBlocks)
 }

যদি পৃষ্ঠাটি দেখার বাইরে চলে যায় তবে ইভেন্ট শ্রোতাদের সরানো হয় এবং অ্যানিমেশন ফ্রেম লুপ বাতিল করা হয়। blocks অ্যারেও সাফ করা হয়েছে।

এখানে কার্সার ট্রেইল অ্যাকশন!

অ্যাক্সেসযোগ্যতা পর্যালোচনা

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

কিছু উল্লেখযোগ্য অঞ্চল আচ্ছাদিত:

  • এইচটিএমএল ব্যবহৃত হয়েছিল তা নিশ্চিত করা শব্দার্থক ছিল। এর মধ্যে বইয়ের জন্য <main> এর মতো উপযুক্ত ল্যান্ডমার্ক উপাদানগুলির মতো জিনিস অন্তর্ভুক্ত ছিল; প্রতিটি সামগ্রী ব্লকের জন্য <article> উপাদানটির ব্যবহার এবং <abbr> উপাদানগুলি যেখানে সংক্ষিপ্ত শব্দগুলি চালু করা হয়। বইটি নির্মিত হওয়ার সাথে সাথে সামনে চিন্তা করা বিষয়গুলিকে আরও অ্যাক্সেসযোগ্য করে তুলেছে। শিরোনাম এবং লিঙ্কগুলির ব্যবহার কোনও ব্যবহারকারীর পক্ষে নেভিগেট করা সহজ করে তোলে। পৃষ্ঠাগুলির জন্য একটি তালিকা ব্যবহারের অর্থ হ'ল সহায়ক প্রযুক্তি দ্বারা পৃষ্ঠাগুলির সংখ্যা ঘোষণা করা হয়।
  • সমস্ত চিত্র উপযুক্ত alt বৈশিষ্ট্যগুলি ব্যবহার করে তা নিশ্চিত করে। ইনলাইন এসভিজিগুলির জন্য, প্রয়োজনীয় যেখানে title উপাদান উপস্থিত রয়েছে।
  • aria বৈশিষ্ট্যগুলি ব্যবহার করে যেখানে তারা অভিজ্ঞতা উন্নত করে। পৃষ্ঠাগুলির জন্য aria-label ব্যবহার এবং তাদের পক্ষগুলি ব্যবহারকারীর সাথে যোগাযোগ করে যে তারা কোন পৃষ্ঠায় রয়েছে। "আরও পড়ুন" লিঙ্কগুলিতে aria-describedBy ব্যবহার করা সামগ্রী ব্লকের পাঠ্যকে যোগাযোগ করে। লিঙ্কটি ব্যবহারকারীকে কোথায় নিয়ে যাবে সে সম্পর্কে এটি অস্পষ্টতা সরিয়ে দেয়।
  • সামগ্রী ব্লকগুলির বিষয়ে, পুরো কার্ডটি ক্লিক করার ক্ষমতা এবং কেবল "আরও পড়ুন" লিঙ্কটি উপলব্ধ নয়।
  • কোন পৃষ্ঠাগুলি নজরে রয়েছে তা ট্র্যাক করতে একটি IntersectionObserver ব্যবহার আগে এসেছিল। এটির অনেকগুলি সুবিধা রয়েছে যা কেবল পারফরম্যান্স সম্পর্কিত নয়। দেখার মতো পৃষ্ঠাগুলিতে কোনও অ্যানিমেশন বা মিথস্ক্রিয়া বিরতি থাকবে। তবে এই পৃষ্ঠাগুলিতে inert বৈশিষ্ট্য প্রয়োগ করা হয়েছে। এর অর্থ হ'ল স্ক্রিন রিডার ব্যবহারকারীরা দর্শনীয় ব্যবহারকারীদের মতো একই সামগ্রীটি অন্বেষণ করতে পারেন। ফোকাসটি পৃষ্ঠার মধ্যে রয়েছে যা দেখার জন্য এবং ব্যবহারকারীরা অন্য পৃষ্ঠায় ট্যাব করতে পারবেন না।
  • সর্বশেষে তবে অন্তত নয়, আমরা গতির জন্য কোনও ব্যবহারকারীর পছন্দকে সম্মান করতে মিডিয়া প্রশ্নগুলি ব্যবহার করি।

এখানে পর্যালোচনা থেকে একটি স্ক্রিনশট রয়েছে যেখানে কিছু ব্যবস্থা জায়গায় রয়েছে।

উপাদানটি পুরো বইয়ের চারপাশে চিহ্নিত করা হয়েছে, এটি নির্দেশ করে যে এটি সহায়ক প্রযুক্তি ব্যবহারকারীদের সন্ধানের জন্য প্রধান ল্যান্ডমার্ক হওয়া উচিত। আরও স্ক্রিনশটে বর্ণিত হয়েছে "" প্রস্থ = "800" উচ্চতা = "465">

ক্রোমেটোবার বইয়ের স্ক্রিনশট খোলা। সবুজ বর্ণিত বাক্সগুলি ইউআইয়ের বিভিন্ন দিকের চারপাশে সরবরাহ করা হয়, যা নির্দিষ্ট অ্যাক্সেসযোগ্যতার কার্যকারিতা এবং ব্যবহারকারীর অভিজ্ঞতার ফলাফলগুলি বর্ণনা করে যা পৃষ্ঠাটি সরবরাহ করবে। উদাহরণস্বরূপ, চিত্রগুলিতে ALT পাঠ্য রয়েছে। আরেকটি উদাহরণ হ'ল একটি অ্যাক্সেসযোগ্যতার লেবেল যা ঘোষণা করে যে পৃষ্ঠাগুলি দেখার বাইরে রয়েছে। আরও স্ক্রিনশটে বর্ণিত হয়েছে।

আমরা কি শিখেছি

The motivation behind Chrometober was not only to highlight web content from the community, but was also a way for us to test drive the scroll-linked animations API polyfill that's in development.

We set aside a session while on our team summit in New York to test the project and tackle issues that arose. The team's contribution was invaluable. It was also a great opportunity to list all the things that needed tackling before we could go live.

CSS, UI, and DevTools team sit around the table in a conference room. Una stands at a whiteboard which is covered in sticky notes. Other team members sit around the table with refreshments and laptops.

For example, testing out the book on devices raised a rendering issue. Our book wouldn't render as expected on iOS devices. Viewport units size the page, but when a notch was present, it affected the book. The solution was to use viewport-fit=cover in the meta viewport:

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

This session also raised some issues with the API polyfill. Bramus raised these issues in the polyfill repository. He subsequently found solutions to those issues and got them merged into the polyfill. For example, this pull request made a performance gain by adding caching to part of the polyfill.

A screenshot of a demo open in Chrome. The Developer Tools are open and show a baseline performance measurement.

A screenshot of a demo open in Chrome. The Developer Tools are open and show an improved performance measurement.

তাই তো!

This has been a real fun project to work on, resulting in a whimsical scrolling experience that highlights amazing content from the community. Not only that, it's been great for testing the polyfill, as well as providing feedback to the engineering team to help improve the polyfill.

Chrometober 2022 is a wrap.

আমরা আশা করি আপনি এটি উপভোগ করেছেন! আপনার প্রিয় বৈশিষ্ট্য কি? Tweet me and let us know!

Jhey holding a sticker sheet of the characters from Chrometober.

You might even be able to grab some stickers from one of the team if you see us at an event .

Hero Photo by David Menidrey on Unsplash