লিট-এলিমেন্ট সহ একটি গল্পের উপাদান তৈরি করুন

1. ভূমিকা

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

গল্পের কম্পোনেন্টটি শেষের দিকে কেমন দেখাবে:

একটি সম্পূর্ণ গল্প-দর্শক উপাদান কফির তিনটি ছবি প্রদর্শন করছে

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

বৈশিষ্ট্য তালিকা

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

এই উপাদানটির বিকাশকারীর অভিজ্ঞতা যতদূর, প্লেইন এইচটিএমএল মার্কআপে স্টোরি কার্ড নির্দিষ্ট করা ভালো হবে, যেমন:

<story-viewer>
  <story-card>
    <img slot="media" src="some/image.jpg" />
    <h1>Title</h1>
  </story-card>
  <story-card>
    <video slot="media" src="some/video.mp4" loop playsinline></video>
    <h1>Whatever</h1>
    <p>I want!</p>
  </story-card>
</story-viewer>

সুতরাং এর বৈশিষ্ট্য তালিকায় এটিও যোগ করা যাক।

বৈশিষ্ট্য তালিকা

  • HTML মার্কআপে কার্ডের একটি সিরিজ গ্রহণ করুন।

এইভাবে যে কেউ HTML লিখে আমাদের গল্পের উপাদান ব্যবহার করতে পারে। এটি একইভাবে প্রোগ্রামার এবং নন-প্রোগ্রামারদের জন্য দুর্দান্ত, এবং HTML যেখানেই কাজ করে: বিষয়বস্তু ব্যবস্থাপনা সিস্টেম, ফ্রেমওয়ার্ক ইত্যাদি।

পূর্বশর্ত

  • একটি শেল যেখানে আপনি git এবং npm চালাতে পারেন
  • একজন টেক্সট এডিটর

2. সেট আপ করা

এই রেপো ক্লোন করে শুরু করুন: গল্প-দর্শক-স্টার্টার

git clone git@github.com:PolymerLabs/story-viewer-starter.git

পরিবেশ ইতিমধ্যে lit-element এবং TypeScript দিয়ে সেট আপ করা হয়েছে। শুধু নির্ভরতা ইনস্টল করুন:

npm i

VS কোড ব্যবহারকারীদের জন্য, lit-html টেমপ্লেটগুলির স্বয়ংসম্পূর্ণতা, টাইপ-চেকিং এবং লিন্টিং পেতে লিট-প্লাগইন এক্সটেনশন ইনস্টল করুন।

চালানোর মাধ্যমে উন্নয়ন পরিবেশ শুরু করুন:

npm run dev

আপনি কোডিং শুরু করতে প্রস্তুত!

3. <গল্প-কার্ড> উপাদান

যৌগিক উপাদানগুলি তৈরি করার সময়, কখনও কখনও সহজ সাব-কম্পোনেন্টগুলি দিয়ে শুরু করা এবং তৈরি করা সহজ। তো, চলুন শুরু করা যাক <story-card> তৈরি করে। এটি একটি সম্পূর্ণ-ব্লিড ভিডিও বা একটি চিত্র প্রদর্শন করতে সক্ষম হওয়া উচিত। ব্যবহারকারীদের এটিকে আরও কাস্টমাইজ করতে সক্ষম হওয়া উচিত, উদাহরণস্বরূপ, ওভারলে পাঠ্য সহ।

প্রথম ধাপ হল আমাদের কম্পোনেন্টের ক্লাস সংজ্ঞায়িত করা, যা LitElement প্রসারিত করে। customElement ডেকোরেটর আমাদের জন্য কাস্টম উপাদান নিবন্ধন করার যত্ন নেয়। experimentalDecorators পতাকার সাথে আপনি আপনার tsconfig-এ ডেকোরেটর সক্ষম করেছেন তা নিশ্চিত করার জন্য এখন একটি ভাল সময় (যদি আপনি স্টার্টার রেপো ব্যবহার করেন তবে এটি ইতিমধ্যেই সক্ষম)।

story-card.ts-এ নিম্নলিখিত কোডটি রাখুন:

import { LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('story-card')
export class StoryCard extends LitElement {
}

এখন <story-card> একটি ব্যবহারযোগ্য কাস্টম উপাদান, কিন্তু এখনও প্রদর্শন করার মতো কিছুই নেই। উপাদানের অভ্যন্তরীণ কাঠামো সংজ্ঞায়িত করতে, render ইনস্ট্যান্স পদ্ধতিটি সংজ্ঞায়িত করুন। এখানেই আমরা lit-html এর html ট্যাগ ব্যবহার করে উপাদানটির জন্য টেমপ্লেট প্রদান করব।

এই উপাদান এর টেমপ্লেট কি হওয়া উচিত? ব্যবহারকারী দুটি জিনিস প্রদান করতে সক্ষম হওয়া উচিত: একটি মিডিয়া উপাদান এবং একটি ওভারলে৷ সুতরাং, আমরা তাদের প্রত্যেকের জন্য একটি <slot> যোগ করব।

স্লট হল যেভাবে আমরা নির্দিষ্ট করি একটি কাস্টম উপাদানের বাচ্চাদের রেন্ডার করা উচিত। আরও তথ্যের জন্য, এখানে স্লট ব্যবহার করার জন্য একটি দুর্দান্ত ওয়াকথ্রু রয়েছে৷

import { html } from 'lit';

export class StoryCard extends LitElement {
  render() {
    return html`
      <div id="media">
        <slot name="media"></slot>
      </div>
      <div id="content">
        <slot></slot>
      </div>
    `;
  }
}

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

<story-card> উপাদানটি এখন এভাবে ব্যবহার করা যেতে পারে:

<story-card>
  <img slot="media" src="some/image.jpg" />
  <h1>My Title</h1>
  <p>my description</p>
</story-card>

কিন্তু, এটা ভয়ানক দেখায়:

একটি স্টাইলবিহীন গল্প-দর্শক কফির একটি ছবি প্রদর্শন করছে

শৈলী যোগ করা হচ্ছে

এর কিছু শৈলী যোগ করা যাক. লিট-এলিমেন্টের সাথে, আমরা একটি স্ট্যাটিক styles বৈশিষ্ট্য সংজ্ঞায়িত করে এবং css এর সাথে ট্যাগ করা একটি টেমপ্লেট স্ট্রিং ফিরিয়ে দিয়ে এটি করি। এখানে যা লেখা CSS শুধুমাত্র আমাদের কাস্টম উপাদানের জন্য প্রযোজ্য! ছায়া DOM সঙ্গে CSS এই ভাবে সত্যিই চমৎকার.

আসুন <story-card> কভার করার জন্য স্লটেড মিডিয়া উপাদানটিকে স্টাইল করি। আমরা এখানে থাকাকালীন, আমরা দ্বিতীয় স্লটের উপাদানগুলির জন্য কিছু সুন্দর বিন্যাস প্রদান করতে পারি। এইভাবে, উপাদান ব্যবহারকারীরা কিছু <h1> s, <p> s, বা যাই হোক না কেন ড্রপ করতে পারেন এবং ডিফল্টরূপে সুন্দর কিছু দেখতে পারেন।

import { css } from 'lit';

export class StoryCard extends LitElement {
  static styles = css`
    #media {
      height: 100%;
    }
    #media ::slotted(*) {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }

    /* Default styles for content */
    #content {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      padding: 48px;
      font-family: sans-serif;
      color: white;
      font-size: 24px;
    }
    #content > slot::slotted(*) {
      margin: 0;
    }
  `;
}

একটি স্টাইল করা গল্প-দর্শক কফির একটি ছবি প্রদর্শন করছে

এখন আমাদের কাছে ব্যাকগ্রাউন্ড মিডিয়া সহ স্টোরি কার্ড রয়েছে এবং আমরা যা চাই তা উপরে রাখতে পারি। চমৎকার! অটোপ্লেয়িং ভিডিওগুলি বাস্তবায়ন করতে আমরা কিছুক্ষণের মধ্যে StoryCard ক্লাসে ফিরে যাব।

4. <গল্প-দর্শক> উপাদান

আমাদের <story-viewer> উপাদান হল <story-card> > এর মূল। এটি কার্ডগুলিকে অনুভূমিকভাবে সাজানোর জন্য এবং আমাদের তাদের মধ্যে সোয়াইপ করতে দেওয়ার জন্য দায়ী। আমরা StoryCard জন্য যেভাবে করেছি সেভাবে আমরা এটি বন্ধ করে দেব। আমরা <story-viewer> উপাদানের শিশু হিসাবে গল্প কার্ড যোগ করতে চাই, তাই সেই শিশুদের জন্য একটি স্লট যোগ করুন।

story-viewer.ts-এ নিম্নলিখিত কোডটি রাখুন:

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('story-viewer')
export class StoryViewer extends LitElement {
  render() {
    return html`<slot></slot>`;
  }
}

পরবর্তীতে একটি অনুভূমিক বিন্যাস রয়েছে। আমরা সমস্ত স্লটেড <story-card> > এর পরম অবস্থান প্রদান করে এবং তাদের সূচক অনুসারে অনুবাদ করে এটির কাছে যেতে পারি। আমরা :host নির্বাচক ব্যবহার করে নিজেই <story-viewer> উপাদানটিকে লক্ষ্য করতে পারি।

static styles = css`
  :host {
    display: block;
    position: relative;
    /* Default size */
    width: 300px;
    height: 800px;
  }
  ::slotted(*) {
    position: absolute;
    width: 100%;
    height: 100%;
  }`;

ব্যবহারকারী হোস্টে ডিফল্ট উচ্চতা এবং প্রস্থ বাহ্যিকভাবে ওভাররাইড করে আমাদের স্টোরি কার্ডের আকার নিয়ন্ত্রণ করতে পারে। এই মত:

story-viewer {
  width: 400px;
  max-width: 100%;
  height: 80%;
}

বর্তমানে দেখা কার্ডের ট্র্যাক রাখতে, StoryViewer ক্লাসে একটি ইনস্ট্যান্স ভেরিয়েবল index যোগ করা যাক। এটিকে LitElement-এর @property দিয়ে সাজানোর ফলে কম্পোনেন্টের মান পরিবর্তন হলে সেটি পুনরায় রেন্ডার হবে।

import { property } from 'lit/decorators.js';

export class StoryViewer extends LitElement {
  @property({type: Number}) index: number = 0;
}

প্রতিটি কার্ড অবস্থানে অনুভূমিকভাবে অনুবাদ করা প্রয়োজন। আসুন lit-element-এর update লাইফসাইকেল পদ্ধতিতে এই অনুবাদগুলি প্রয়োগ করি। যখনই এই উপাদানটির একটি পর্যবেক্ষিত বৈশিষ্ট্য পরিবর্তন হবে তখনই আপডেট পদ্ধতিটি চলবে। সাধারণত, আমরা slot.assignedElements() এর উপর স্লট এবং লুপ অনুসন্ধান করব। যাইহোক, যেহেতু আমাদের শুধুমাত্র একটি নামবিহীন স্লট আছে, এটি this.children ব্যবহার করার মতই। এর সুবিধার জন্য this.children .children ব্যবহার করা যাক.

import { PropertyValues } from 'lit';

export class StoryViewer extends LitElement {
  update(changedProperties: PropertyValues) {
    const width = this.clientWidth;
    Array.from(this.children).forEach((el: Element, i) => {
      const x = (i - this.index) * width;
      (el as HTMLElement).style.transform = `translate3d(${x}px,0,0)`;
    });
    super.update(changedProperties);
  }
}

আমাদের <story-card> >গুলি এখন এক সারিতে। এটি এখনও শিশু হিসাবে অন্যান্য উপাদানগুলির সাথে কাজ করে, যতক্ষণ না আমরা তাদের যথাযথভাবে স্টাইল করার যত্ন নিই:

<story-viewer>
  <!-- A regular story-card child... -->
  <story-card>
    <video slot="media" src="some/video.mp4"></video>
    <h1>This video</h1>
    <p>is so cool.</p>
  </story-card>
  <!-- ...and other elements work too! -->
  <img style="object-fit: cover" src="some/img.png" />
</story-viewer>

build/index.html এ যান এবং স্টোরি-কার্ডের বাকি উপাদানগুলোকে মন্তব্য করুন । এখন, আসুন এটি তৈরি করি যাতে আমরা তাদের কাছে নেভিগেট করতে পারি!

5. অগ্রগতি বার এবং নেভিগেশন

এর পরে, আমরা কার্ডগুলির মধ্যে নেভিগেট করার একটি উপায় এবং একটি অগ্রগতি বার যোগ করব৷

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

story-viewer.ts-এ, StoryViewer ক্লাসে, যোগ করুন:

/** Advance to the next story card if possible **/
next() {
  this.index = Math.max(0, Math.min(this.children.length - 1, this.index + 1));
}

/** Go back to the previous story card if possible **/
previous() {
  this.index = Math.max(0, Math.min(this.children.length - 1, this.index - 1));
}

শেষ-ব্যবহারকারীর কাছে নেভিগেশন প্রকাশ করতে, আমরা <story-viewer> -এ "আগের" এবং "পরবর্তী" বোতাম যোগ করব। যখন যে কোনও বোতামে ক্লিক করা হয়, আমরা next বা previous সহায়ক ফাংশনটি কল করতে চাই। lit-html উপাদানগুলিতে ইভেন্ট শ্রোতাদের যোগ করা সহজ করে তোলে; আমরা বোতামগুলি রেন্ডার করতে পারি এবং একই সময়ে একটি ক্লিক শ্রোতা যোগ করতে পারি।

নিম্নলিখিত render পদ্ধতি আপডেট করুন:

export class StoryViewer extends LitElement {
  render() {
    return html`
      <slot></slot>

      <svg id="prev" viewBox="0 0 10 10" @click=${() => this.previous()}>
        <path d="M 6 2 L 4 5 L 6 8" stroke="#fff" fill="none" />
      </svg>
      <svg id="next" viewBox="0 0 10 10" @click=${() => this.next()}>
        <path d="M 4 2 L 6 5 L 4 8" stroke="#fff" fill="none" />
      </svg>
    `;
  }
}

render পদ্ধতিতে আমরা কীভাবে আমাদের নতুন svg বোতামে ইভেন্ট শ্রোতাদের ইনলাইনে যোগ করতে পারি তা দেখুন। এটি যে কোনও ইভেন্টের জন্য কাজ করে। শুধু একটি উপাদানে @eventname=${handler} ফর্মের একটি বাঁধাই যোগ করুন।

বোতাম স্টাইল করতে static styles সম্পত্তিতে নিম্নলিখিত যোগ করুন:

svg {
  position: absolute;
  top: calc(50% - 25px);
  height: 50px;
  cursor: pointer;
}
#next {
  right: 0;
}

প্রোগ্রেস বারের জন্য, আমরা ছোট ছোট বাক্স স্টাইল করার জন্য CSS গ্রিড ব্যবহার করব, প্রতিটি স্টোরি কার্ডের জন্য একটি। আমরা শর্তসাপেক্ষে বক্সগুলিতে ক্লাস যোগ করার জন্য index সম্পত্তি ব্যবহার করতে পারি যে সেগুলি "দেখা" হয়েছে কিনা তা নির্দেশ করতে। আমরা একটি শর্তসাপেক্ষ অভিব্যক্তি ব্যবহার করতে পারি যেমন i <= this.index : 'watched': '' , কিন্তু আমরা যদি আরও ক্লাস যোগ করি তবে জিনিসগুলি ভার্বস হতে পারে। সৌভাগ্যবশত, lit-html সাহায্য করার জন্য classMap নামে একটি নির্দেশিকা বিক্রি করে। প্রথমে, classMap আমদানি করুন:

import { classMap } from 'lit/directives/class-map';

এবং render পদ্ধতির নীচে নিম্নলিখিত মার্কআপ যোগ করুন:

<div id="progress">
  ${Array.from(this.children).map((_, i) => html`
    <div
      class=${classMap({watched: i <= this.index})}
      @click=${() => this.index = i}
    ></div>`
  )}
</div>

আমরা আরও কিছু ক্লিক হ্যান্ডলারও ছুড়ে দিয়েছি যাতে ব্যবহারকারীরা চাইলে সরাসরি একটি নির্দিষ্ট স্টোরি কার্ডে চলে যেতে পারে।

static styles যোগ করার জন্য এখানে নতুন শৈলী রয়েছে:

::slotted(*) {
  position: absolute;
  width: 100%;
  /* Changed this line! */
  height: calc(100% - 20px);
}

#progress {
  position: relative;
  top: calc(100% - 20px);
  height: 20px;
  width: 50%;
  margin: 0 auto;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  grid-gap: 10px;
  align-content: center;
}
#progress > div {
  background: grey;
  height: 4px;
  transition: background 0.3s linear;
  cursor: pointer;
}
#progress > div.watched {
  background: white;
}

নেভিগেশন এবং অগ্রগতি বার সম্পূর্ণ. এখন কিছু ফ্লেয়ার যোগ করা যাক!

6. সোয়াইপ

সোয়াইপিং বাস্তবায়ন করতে, আমরা Hammer.js জেসচার কন্ট্রোল লাইব্রেরি ব্যবহার করব। হাতুড়ি প্যানের মতো বিশেষ অঙ্গভঙ্গি সনাক্ত করে এবং প্রাসঙ্গিক তথ্য (যেমন ডেল্টা এক্স) সহ ইভেন্টগুলি প্রেরণ করে যা আমরা ব্যবহার করতে পারি।

প্যান শনাক্ত করতে আমরা কীভাবে হ্যামার ব্যবহার করতে পারি তা এখানে দেওয়া হল এবং যখনই কোনও প্যান ইভেন্ট ঘটে তখন স্বয়ংক্রিয়ভাবে আমাদের উপাদান আপডেট করতে পারি:

import { state } from 'lit/decorators.js';
import 'hammerjs';

export class StoryViewer extends LitElement {
  // Data emitted by Hammer.js
  @state() _panData: {isFinal?: boolean, deltaX?: number} = {};

  constructor() {
    super();
    this.index = 0;
    new Hammer(this).on('pan', (e: HammerInput) => this._panData = e);
  }
}

একটি LitElement ক্লাসের কনস্ট্রাক্টর হল হোস্ট এলিমেন্টে ইভেন্ট শ্রোতাদের সংযুক্ত করার আরেকটি দুর্দান্ত জায়গা। হাতুড়ি কনস্ট্রাক্টর একটি উপাদান নেয় অঙ্গভঙ্গি সনাক্ত করতে. আমাদের ক্ষেত্রে, এটি StoryViewer নিজেই বা this । তারপর, হ্যামারের API ব্যবহার করে, আমরা এটিকে "প্যান" অঙ্গভঙ্গি সনাক্ত করতে বলি এবং প্যান তথ্যটিকে একটি নতুন _panData বৈশিষ্ট্যে সেট করি।

_panData প্রপার্টি @state দিয়ে সাজানোর মাধ্যমে, LitElement _panData এ পরিবর্তনগুলি পর্যবেক্ষণ করবে এবং একটি আপডেট করবে, কিন্তু সম্পত্তির জন্য কোনও যুক্ত HTML অ্যাট্রিবিউট থাকবে না।

এর পরে, প্যান ডেটা ব্যবহার করার জন্য update লজিকটি বৃদ্ধি করা যাক:

// Update is called whenever an observed property changes.
update(changedProperties: PropertyValues) {
  // deltaX is the distance of the current pan gesture.
  // isFinal is whether the pan gesture is ending.
  let { deltaX = 0, isFinal = false } = this._panData;
  // When the pan gesture finishes, navigate.
  if (!changedProperties.has('index') && isFinal) {
    deltaX > 0 ? this.previous() : this.next();
  }
  // We don't want any deltaX when releasing a pan.
  deltaX = isFinal ? 0 : deltaX;
  const width = this.clientWidth;
  Array.from(this.children).forEach((el: Element, i) => {
    // Updated this line to utilize deltaX.
    const x = (i - this.index) * width + deltaX;
    (el as HTMLElement).style.transform = `translate3d(${x}px,0,0)`;
  });

  // Don't forget to call super!
  super.update(changedProperties);
}

আমরা এখন আমাদের গল্পের কার্ডগুলিকে সামনে পিছনে টেনে আনতে পারি। জিনিসগুলিকে মসৃণ করতে, আসুন static get styles ফিরে যাই এবং transition: transform 0.35s ease-out; ::slotted(*) নির্বাচককে:

::slotted(*) {
  ...
  transition: transform 0.35s ease-out;
}

এখন আমাদের মসৃণ সোয়াইপিং আছে:

মসৃণ সোয়াইপ করে স্টোরি-কার্ডের মধ্যে নেভিগেট করা

7. অটোপ্লে

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

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

সেটার ব্যবহার করার জন্য StoryViewer এর index সম্পত্তির রিফ্যাক্টর করি, যা ইভেন্টগুলি পাঠানোর জন্য একটি সুবিধাজনক কোড পথ প্রদান করে:

class StoryViewer extends LitElement {
  @state() private _index: number = 0
  get index() {
    return this._index
  }
  set index(value: number) {
    this.children[this._index].dispatchEvent(new CustomEvent('exited'));
    this.children[value].dispatchEvent(new CustomEvent('entered'));
    this._index = value;
  }
}

অটোপ্লে বৈশিষ্ট্যটি শেষ করতে, আমরা StoryCard কনস্ট্রাক্টরে "প্রবেশ করা" এবং "প্রস্থান করা" এর জন্য ইভেন্ট শ্রোতাদের যোগ করব যারা ভিডিওটি চালায় এবং বিরতি দেয়।

মনে রাখবেন যে উপাদান ব্যবহারকারী মিডিয়া স্লটে <story-card> একটি ভিডিও উপাদান দিতে পারে বা নাও দিতে পারে। তারা এমনকি মিডিয়া স্লটে একটি উপাদান প্রদান নাও হতে পারে. আমাদের সতর্কতা অবলম্বন করতে হবে যেন কোনো img-এ বা নাল-এ play না হয়।

story-card.ts-এ ফিরে, নিম্নলিখিত যোগ করুন:

import { query } from 'lit/decorators.js';

class StoryCard extends LitElement {
  constructor() {
    super();

    this.addEventListener("entered", () => {
      if (this._slottedMedia) {
        this._slottedMedia.currentTime = 0;
        this._slottedMedia.play();
      }
    });

    this.addEventListener("exited", () => {
      if (this._slottedMedia) {
        this._slottedMedia.pause();
      }
    });
  }

 /**
  * The element in the "media" slot, ONLY if it is an
  * HTMLMediaElement, such as <video>.
  */
 private get _slottedMedia(): HTMLMediaElement|null {
   const el = this._mediaSlot && this._mediaSlot.assignedNodes()[0];
   return el instanceof HTMLMediaElement ? el : null;
 }

  /**
   * @query(selector) is shorthand for
   * this.renderRoot.querySelector(selector)
   */
  @query("slot[name=media]")
  private _mediaSlot!: HTMLSlotElement;
}

অটোপ্লে সম্পূর্ণ। ✅

8. দাঁড়িপাল্লা টিপ

এখন যেহেতু আমাদের কাছে সমস্ত প্রয়োজনীয় বৈশিষ্ট্য রয়েছে, আসুন আরও একটি যোগ করি: একটি মিষ্টি স্কেলিং প্রভাব৷ StoryViewer এর update পদ্ধতিতে আরও একবার ফিরে যাওয়া যাক। scale ধ্রুবক মান পেতে কিছু গণিত করা হয়। এটি সক্রিয় শিশুর জন্য 1.0 সমান হবে এবং minScale অন্যথায়, এই দুটি মানের মধ্যেও ইন্টারপোলেটিং।

story-viewer.ts-এ update পদ্ধতিতে লুপ পরিবর্তন করুন:

update(changedProperties: PropertyValues) {
  // ...
  const minScale = 0.8;
  Array.from(this.children).forEach((el: Element, i) => {
    const x = (i - this.index) * width + deltaX;

    // Piecewise scale(deltaX), looks like: __/\__
    const u = deltaX / width + (i - this.index);
    const v = -Math.abs(u * (1 - minScale)) + 1;
    const scale = Math.max(v, minScale);
    // Include the scale transform
    (el as HTMLElement).style.transform = `translate3d(${x}px,0,0) scale(${scale})`;
  });
  // ...
}

যে সব, লোকেরা! এই পোস্টে আমরা কিছু LitElement এবং lit-html বৈশিষ্ট্য, HTML স্লট উপাদান এবং অঙ্গভঙ্গি নিয়ন্ত্রণ সহ অনেক কিছু কভার করেছি।

এই উপাদানটির একটি সম্পূর্ণ সংস্করণের জন্য, এখানে যান: https://github.com/PolymerLabs/story-viewer