लाइट-एलिमेंट की मदद से स्टोरी कॉम्पोनेंट बनाएं

1. परिचय

इन दिनों स्टोरीज़, यूज़र इंटरफ़ेस (यूआई) का एक लोकप्रिय कॉम्पोनेंट है. सोशल और समाचार ऐप्लिकेशन इन्हें अपने फ़ीड में जोड़ रहे हैं. इस कोडलैब में, हम lit-element और TypeScript के साथ कहानी का एक कॉम्पोनेंट बनाएंगे.

आखिर में, स्टोरी वाला कॉम्पोनेंट कुछ इस तरह दिखेगा:

कॉफ़ी की तीन इमेज दिखाता हुआ एक पूरी कहानी दिखाने वाला कॉम्पोनेंट

हम सोशल मीडिया या समाचार "कहानी" सोच सकते हैं क्रम में चलने वाले कार्ड के कलेक्शन की तरह होता है. यह एक तरह का स्लाइड शो होता है. असल में, कहानियां सिर्फ़ स्लाइड शो होती हैं. आम तौर पर, इन कार्ड में कोई इमेज या अपने-आप चलने वाला वीडियो होता है. इनमें सबसे ऊपर अतिरिक्त टेक्स्ट भी मौजूद हो सकता है. हम क्या बनाएंगे:

सुविधाओं की सूची

  • किसी इमेज या वीडियो बैकग्राउंड वाले कार्ड.
  • कहानी में नेविगेट करने के लिए बाएं या दाएं स्वाइप करें.
  • वीडियो अपने-आप चलने की सुविधा.
  • टेक्स्ट जोड़ने या कार्ड को पसंद के मुताबिक बनाने की सुविधा.

इस कॉम्पोनेंट के डेवलपर अनुभव की बात करें, तो स्टोरी कार्ड को सादे एचटीएमएल मार्कअप में बताना अच्छा होगा, जैसे कि:

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

तो चलिए, इसे सुविधाओं की सूची में भी जोड़ते हैं.

सुविधाओं की सूची

  • एचटीएमएल मार्कअप में कार्ड की सीरीज़ स्वीकार करना.

इस तरह कोई भी बस एचटीएमएल लिखकर हमारे स्टोरी कॉम्पोनेंट का इस्तेमाल कर सकता है. यह प्रोग्रामर और नॉन-प्रोग्रामर, दोनों के लिए बहुत बढ़िया है. यह हर उस जगह काम करता है जहां एचटीएमएल काम करता है: कॉन्टेंट मैनेजमेंट सिस्टम, फ़्रेमवर्क वगैरह.

ज़रूरी शर्तें

  • ऐसा शेल, जिस पर आप git और npm चला सकते हैं
  • टेक्स्ट एडिटर

2. सेटअप करना

इस रेपो को क्लोन करके शुरुआत करें: स्टोरी-व्यूअर-स्टार्टर

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

एनवायरमेंट को पहले ही lit-element और TypeScript के साथ सेट अप किया जा चुका है. बस डिपेंडेंसी इंस्टॉल करें:

npm i

वीएस कोड इस्तेमाल करने वालों के लिए, lit-plugin एक्सटेंशन इंस्टॉल करें. इससे आपको lit-html टेंप्लेट के अपने-आप पूरा होने, टाइप-चेकिंग, और लिंटिंग की सुविधा मिलेगी.

इसे चलाकर डेवलपमेंट एनवायरमेंट शुरू करें:

npm run dev

अब आप कोडिंग शुरू करने के लिए तैयार हैं!

3. <story-card> घटक

कंपाउंड कॉम्पोनेंट बनाते समय, कभी-कभी आसान सब-कॉम्पोनेंट के साथ शुरुआत करना और उन्हें बनाना आसान होता है. आइए, <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 के साथ टैग की गई टेंप्लेट स्ट्रिंग दिखाते हैं. यहां जो भी सीएसएस लिखा गया है वह सिर्फ़ हमारे कस्टम एलिमेंट पर लागू होता है! शैडो डीओएम वाला सीएसएस इस तरह से वाकई अच्छा है.

चलिए, <story-card> को कवर करने के लिए स्लॉट किए गए मीडिया एलिमेंट की स्टाइल तैयार करते हैं. फ़िलहाल, हम दूसरे स्लॉट के एलिमेंट के लिए कुछ अच्छे फ़ॉर्मैट उपलब्ध करा सकते हैं. इस तरह, कॉम्पोनेंट उपयोगकर्ता कुछ <h1>, <p> या कुछ भी छोड़ सकते हैं और डिफ़ॉल्ट रूप से कुछ अच्छा देख सकते हैं.

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

हर कार्ड को हॉरिज़ॉन्टल तौर पर अनुवाद किया जाना चाहिए. चलिए, इन अनुवादों को लिट-एलिमेंट के update लाइफ़साइकल तरीके में लागू करते हैं. जब भी इस कॉम्पोनेंट की निगरानी की जा रही प्रॉपर्टी में बदलाव होगा, तब अपडेट करने का तरीका चलेगा. आम तौर पर, हम स्लॉट और लूप के लिए slot.assignedElements() के बारे में क्वेरी करते हैं. हालांकि, हमारे पास बिना नाम वाला सिर्फ़ एक स्लॉट है, इसलिए यह this.children का इस्तेमाल करने के जैसा ही है. सुविधा के लिए, आइए this.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;
}

प्रोग्रेस बार में, हम छोटे बॉक्स को स्टाइल देने के लिए सीएसएस ग्रिड का इस्तेमाल करेंगे. यह हर स्टोरी कार्ड के लिए एक बॉक्स होगा. हम 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 जेस्चर से कंट्रोल लाइब्रेरी का इस्तेमाल करेंगे. हैमर, पैन जैसे खास जेस्चर का पता लगाता है और इवेंट के बारे में काम की ऐसी जानकारी (जैसे कि डेल्टा X) के बारे में बताता है जिसका हम इस्तेमाल कर सकते हैं.

यहां बताया गया है कि हम पैन का पता लगाने के लिए हैमर का इस्तेमाल कैसे कर सकते हैं. साथ ही, जब भी पैन इवेंट होता है, तो हमारा एलिमेंट अपने-आप अपडेट हो जाता है:

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 है. फिर, हैमर के एपीआई का इस्तेमाल करके हम उसे "पैन" का पता लगाने के लिए कहते हैं जेस्चर का इस्तेमाल करें और पैन की जानकारी को नई _panData प्रॉपर्टी पर सेट करें.

_panData प्रॉपर्टी को @state से सजाए जाने पर, LitElement को _panData में होने वाले बदलावों के बारे में पता चलेगा और वह अपडेट करेगा. हालांकि, प्रॉपर्टी से जुड़ा कोई एचटीएमएल एट्रिब्यूट नहीं होगा.

अब पैन डेटा का इस्तेमाल करने के लिए, 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 पर वापस जाएं और ::slotted(*) सिलेक्टर में transition: transform 0.35s ease-out; जोड़ें:

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

अब आसान स्वाइपिंग की सुविधा उपलब्ध है:

आसानी से स्वाइप करने की सुविधा से, स्टोरी कार्ड के बीच नेविगेट करें

7. ऑटोप्ले

इसके बाद, हम एक नई सुविधा जोड़ेंगे, वह है अपने-आप वीडियो चलने की सुविधा. जब किसी स्टोरी कार्ड पर फ़ोकस होता है, तो हम चाहते हैं कि बैकग्राउंड वीडियो चले. हालांकि, इसके लिए ज़रूरी है कि वीडियो मौजूद हो. जब किसी स्टोरी कार्ड से फ़ोकस हट जाता है, तो हमें उसका वीडियो रोक देना चाहिए.

हम इसे 'entered' डिस्पैच करके लागू करेंगे और ‘बाहर निकल गए' जब भी इंडेक्स बदलता है, तो सही चाइल्ड पर कस्टम इवेंट जोड़े जाते हैं. StoryCard में, हम उन इवेंट को जानेंगे और सभी मौजूदा वीडियो को चलाएंगे या रोक देंगे. 'enter' के तौर पर कॉल करने के बजाय, बच्चों के लिए इवेंट के बारे में जानकारी भेजने का विकल्प क्यों चुना जाए और ‘बाहर निकल गए' Storyकार्ड पर तय किए गए इंस्टेंस के तरीके? तरीकों से, कॉम्पोनेंट उपयोगकर्ताओं के पास कस्टम एलिमेंट लिखने के अलावा कोई विकल्प नहीं होता. अगर वे कस्टम ऐनिमेशन की मदद से अपना स्टोरी कार्ड लिखना चाहते हैं, तो वे ऐसा कर सकते हैं. इवेंट की मदद से, वे सिर्फ़ इवेंट लिसनर को अटैच कर सकते हैं!

एक सेटर का इस्तेमाल करने के लिए, 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> को मीडिया स्लॉट में वीडियो एलिमेंट दे सकता है और नहीं भी. यहां तक कि वे मीडिया स्लॉट में कोई एलिमेंट भी मुहैया नहीं करा सकते. हमें इस बात का ध्यान रखना चाहिए कि 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 की सुविधाएं, एचटीएमएल स्लॉट एलिमेंट, और जेस्चर कंट्रोल.

इस कॉम्पोनेंट का पूरा वर्शन देखने के लिए, https://github.com/PolymerLabs/story-viewer पर जाएं.