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. सेटअप करना
इस रेपो को क्लोन करके शुरू करें: story-viewer-starter
git clone git@github.com:PolymerLabs/story-viewer-starter.git
एनवायरमेंट में lit-element और TypeScript पहले से ही सेट अप है. सिर्फ़ डिपेंडेंसी इंस्टॉल करें:
npm i
VS Code का इस्तेमाल करने वाले लोग, 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>
हालांकि, यह बहुत खराब दिखता है:

स्टाइल जोड़ना
चलिए, कुछ स्टाइल जोड़ते हैं. lit-element की मदद से, हम ऐसा स्टैटिक 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>s का पैरंट है. इसकी ज़िम्मेदारी, कार्ड को हॉरिज़ॉन्टल तरीके से लेआउट करने और उनके बीच स्वाइप करने की सुविधा देने की होगी. हम इसे उसी तरह से शुरू करेंगे जैसे हमने 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 का इस्तेमाल करते हैं.
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>
`;
}
}
देखें कि हम अपने नए SVG बटन पर, render तरीके में इवेंट लिसनर को इनलाइन कैसे जोड़ सकते हैं. यह सुविधा किसी भी इवेंट के लिए काम करती है. बस फ़ॉर्म @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 का इस्तेमाल करेंगे. Hammer, पैन जैसे खास जेस्चर का पता लगाता है. साथ ही, काम की जानकारी (जैसे, डेल्टा X) के साथ इवेंट भेजता है, जिसका हम इस्तेमाल कर सकते हैं.
पैन का पता लगाने के लिए, Hammer का इस्तेमाल इस तरह किया जा सकता है. साथ ही, पैन इवेंट होने पर, एलिमेंट को अपने-आप अपडेट किया जा सकता है:
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 क्लास का कंस्ट्रक्टर, होस्ट एलिमेंट पर इवेंट लिसनर अटैच करने के लिए एक और बेहतरीन जगह है. Hammer कंस्ट्रक्टर, जेस्चर (हाव-भाव) की पहचान करने के लिए एक एलिमेंट लेता है. हमारे मामले में, यह StoryViewer या this है. इसके बाद, हम Hammer के एपीआई का इस्तेमाल करके, उसे "पैन" जेस्चर का पता लगाने के लिए कहते हैं. साथ ही, पैन की जानकारी को नई _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' और 'exited' कस्टम इवेंट भेजेंगे. StoryCard में, हमें वे इवेंट मिलेंगे. साथ ही, हम मौजूदा वीडियो को चला या रोक सकेंगे. StoryCard पर तय किए गए ‘entered' और ‘exited' इंस्टेंस के तरीकों को कॉल करने के बजाय, बच्चों के लिए इवेंट क्यों भेजे जाते हैं? तरीकों के साथ, कॉम्पोनेंट इस्तेमाल करने वाले लोगों के पास कस्टम एलिमेंट लिखने के अलावा कोई विकल्प नहीं होगा. ऐसा तब होगा, जब उन्हें कस्टम ऐनिमेशन के साथ अपना स्टोरी कार्ड लिखना होगा. इवेंट की मदद से, वे सिर्फ़ एक इवेंट लिसनर अटैच कर सकते हैं!
आइए, 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 कंस्ट्रक्टर में "entered" और "exited" के लिए इवेंट लिसनर जोड़ेंगे. ये वीडियो को चलाने और रोकने का काम करते हैं.
ध्यान रखें कि कॉम्पोनेंट का इस्तेमाल करने वाला व्यक्ति, मीडिया स्लॉट में वीडियो एलिमेंट दे भी सकता है और नहीं भी.<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. स्केल को झुकाएं
अब हमारे पास सभी ज़रूरी सुविधाएं हैं. आइए, एक और सुविधा जोड़ते हैं: ज़ूम इन/ज़ूम आउट करने का इफ़ेक्ट. आइए, एक बार फिर से update के StoryViewer तरीके पर वापस जाएं. 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.