लाइट-एलिमेंट की मदद से ब्रिक व्यूअर बनाएं

1. परिचय

वेब कॉम्पोनेंट

वेब कॉम्पोनेंट, वेब स्टैंडर्ड का एक कलेक्शन है. इसकी मदद से डेवलपर, एचटीएमएल को कस्टम एलिमेंट के साथ एक्सटेंड कर सकते हैं. इस कोडलैब में, आपको <brick-viewer> एलिमेंट को तय करना होगा. इससे ब्रिक मॉडल दिखाए जा सकेंगे!

lit-element

हम अपने कस्टम एलिमेंट <brick-viewer> को तय करने के लिए, lit-element का इस्तेमाल करेंगे. lit-element एक लाइटवेट बेस क्लास है. यह वेब कॉम्पोनेंट स्टैंडर्ड में कुछ सिंटैक्टिक शुगर जोड़ता है. इससे हमें कस्टम एलिमेंट को सेट अप करने और इस्तेमाल करने में आसानी होगी.

शुरू करें

हम ऑनलाइन Stackblitz एनवायरमेंट में कोडिंग करेंगे. इसलिए, इस लिंक को नई विंडो में खोलें:

stackblitz.com/edit/brick-viewer

आइए, शुरू करें!

2. कस्टम एलिमेंट तय करना

क्लास की परिभाषा

कस्टम एलिमेंट तय करने के लिए, LitElement को बढ़ाने वाली क्लास बनाएं और उसे @customElement से सजाएं. @customElement का आर्ग्युमेंट, कस्टम एलिमेंट का नाम होगा.

brick-viewer.ts में यह कोड डालें:

@customElement('brick-viewer')
export class BrickViewer extends LitElement {
}

अब <brick-viewer></brick-viewer> एलिमेंट को एचटीएमएल में इस्तेमाल किया जा सकता है. हालांकि, इसे आज़माने पर कुछ भी रेंडर नहीं होगा. चलिए, इसे ठीक करते हैं।

रेंडर करने का तरीका

कंपोनेंट का व्यू लागू करने के लिए, render नाम का एक तरीका तय करें. इस तरीके से, html फ़ंक्शन के साथ टैग किया गया टेंप्लेट लिटरल दिखना चाहिए. टैग किए गए टेंप्लेट लिटरल में, अपनी पसंद का एचटीएमएल डालें. <brick-viewer> का इस्तेमाल करने पर, यह रेंडर होगा.

render तरीका जोड़ें:

export class BrickViewer extends LitElement {
  render() {
    return html`<div>Brick viewer</div>`;
  }
}

3. LDraw फ़ाइल तय करना

प्रॉपर्टी तय करना

अगर <brick-viewer> का इस्तेमाल करने वाला कोई व्यक्ति, किसी एट्रिब्यूट का इस्तेमाल करके यह तय कर सके कि कौनसे ब्रिक मॉडल दिखाने हैं, तो यह बहुत अच्छा होगा. जैसे:

<brick-viewer src="path/to/model.ldraw"></brick-viewer>

हम एक एचटीएमएल एलिमेंट बना रहे हैं. इसलिए, हम डिक्लेरेटिव एपीआई का फ़ायदा ले सकते हैं और <img> या <video> टैग की तरह, सोर्स एट्रिब्यूट तय कर सकते हैं. lit-element के साथ, किसी क्लास प्रॉपर्टी को @property से सजाना उतना ही आसान है. type विकल्प की मदद से, यह तय किया जा सकता है कि lit-element, एचटीएमएल एट्रिब्यूट के तौर पर इस्तेमाल करने के लिए प्रॉपर्टी को कैसे पार्स करे.

src प्रॉपर्टी और एट्रिब्यूट तय करें:

export class BrickViewer extends LitElement {
  @property({type: String})
  src: string|null = null;
}

<brick-viewer> में अब src एट्रिब्यूट है, जिसे एचटीएमएल में सेट किया जा सकता है! lit-element की वजह से, इसकी वैल्यू को हमारी BrickViewer क्लास में पहले से ही पढ़ा जा सकता है.

वैल्यू दिखाना

हम src एट्रिब्यूट की वैल्यू को रेंडर करने के तरीके के टेंप्लेट लिटरल में इस्तेमाल करके दिखा सकते हैं. ${value} सिंटैक्स का इस्तेमाल करके, टेंप्लेट लिटरल में वैल्यू इंटरपोलेट करें.

export class BrickViewer extends LitElement {
  render() {
    return html`<div>Brick model: ${this.src}</div>`;
  }
}

अब हमें विंडो में, <brick-viewer> एलिमेंट में src एट्रिब्यूट की वैल्यू दिखती है. यह तरीका आज़माएं: अपने ब्राउज़र के डेवलपर टूल खोलें और src एट्रिब्यूट को मैन्युअल तरीके से बदलें. इसे आज़माकर देखें...

...क्या आपने ध्यान दिया कि एलिमेंट में मौजूद टेक्स्ट अपने-आप अपडेट हो जाता है? lit-element, @property से डेकोरेट की गई क्लास प्रॉपर्टी को मॉनिटर करता है और आपके लिए व्यू को फिर से रेंडर करता है! lit-element, मुश्किल काम को आसान बना देता है, ताकि आपको ज़्यादा मेहनत न करनी पड़े.

4. Three.js की मदद से सीन सेट करना

लाइट, कैमरा, रेंडर!

हमारा कस्टम एलिमेंट, 3D ब्रिक मॉडल को रेंडर करने के लिए three.js का इस्तेमाल करेगा. हमें <brick-viewer> एलिमेंट के हर इंस्टेंस के लिए, कुछ चीज़ें सिर्फ़ एक बार करनी होती हैं. जैसे, three.js सीन, कैमरा, और लाइटिंग सेट अप करना. हम इन्हें BrickViewer क्लास के कंस्ट्रक्टर में जोड़ेंगे. हम कुछ ऑब्जेक्ट को क्लास प्रॉपर्टी के तौर पर सेव करेंगे, ताकि हम उनका इस्तेमाल बाद में कर सकें: कैमरा, सीन, कंट्रोल, और रेंडरर.

इसे three.js सीन सेटअप में जोड़ें:

export class BrickViewer extends LitElement {

  private _camera: THREE.PerspectiveCamera;
  private _scene: THREE.Scene;
  private _controls: OrbitControls;
  private _renderer: THREE.WebGLRenderer;

  constructor() {
    super();

    this._camera = new THREE.PerspectiveCamera(45, this.clientWidth/this.clientHeight, 1, 10000);
    this._camera.position.set(150, 200, 250);

    this._scene = new THREE.Scene();
    this._scene.background = new THREE.Color(0xdeebed);

    const ambientLight = new THREE.AmbientLight(0xdeebed, 0.4);
    this._scene.add( ambientLight );

    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(-1000, 1200, 1500);
    this._scene.add(directionalLight);

    this._renderer = new THREE.WebGLRenderer({antialias: true});
    this._renderer.setPixelRatio(window.devicePixelRatio);
    this._renderer.setSize(this.offsetWidth, this.offsetHeight);

    this._controls = new OrbitControls(this._camera, this._renderer.domElement);
    this._controls.addEventListener("change", () =>
      requestAnimationFrame(this._animate)
    );

    this._animate();

    const resizeObserver = new ResizeObserver(this._onResize);
    resizeObserver.observe(this);
  }

  private _onResize = (entries: ResizeObserverEntry[]) => {
    const { width, height } = entries[0].contentRect;
    this._renderer.setSize(width, height);
    this._camera.aspect = width / height;
    this._camera.updateProjectionMatrix();
    requestAnimationFrame(this._animate);
  };

  private _animate = () => {
    this._renderer.render(this._scene, this._camera);
  };
}

WebGLRenderer ऑब्जेक्ट, एक डीओएम एलिमेंट उपलब्ध कराता है. यह रेंडर किए गए three.js सीन को दिखाता है. इसे domElement प्रॉपर्टी के ज़रिए ऐक्सेस किया जाता है. हम ${value} सिंटैक्स का इस्तेमाल करके, इस वैल्यू को रेंडर टेंप्लेट लिटरल में इंटरपोलेट कर सकते हैं.

टेंप्लेट में मौजूद src मैसेज को हटाएं और रेंडरर का डीओएम एलिमेंट डालें:

export class BrickViewer extends LitElement {
  render() {
    return html`
      ${this._renderer.domElement}
    `;
  }
}

रेंडरर के DOM एलिमेंट को पूरा दिखाने के लिए, हमें <brick-viewer> एलिमेंट को भी display: block पर सेट करना होगा. हम styles नाम की स्टैटिक प्रॉपर्टी में स्टाइल दे सकते हैं. इसे css टेंप्लेट लिटरल पर सेट किया जाता है.

इस स्टाइल को क्लास में जोड़ें:

export class BrickViewer extends LitElement {
  static styles = css`
    /* The :host selector styles the brick-viewer itself! */
    :host {
      display: block;
    }
  `;
}

अब <brick-viewer> में रेंडर किया गया three.js सीन दिख रहा है:

ब्रिक-व्यूअर एलिमेंट, जिसमें रेंडर किया गया, लेकिन खाली सीन दिख रहा है.

लेकिन... यह खाली है. आइए, इसे एक मॉडल के साथ उपलब्ध कराएं.

ईंटें लोड करने वाला

हम LDrawLoader को src प्रॉपर्टी पास करेंगे. इसे हमने पहले तय किया था. यह three.js के साथ शिप किया जाता है.

LDraw फ़ाइलों की मदद से, ब्रिक मॉडल को अलग-अलग चरणों में बांटा जा सकता है. LDrawLoader API के ज़रिए, चरणों की कुल संख्या और हर ब्रिक की जानकारी ऐक्सेस की जा सकती है.

इन प्रॉपर्टी, नए _loadModel तरीके, और कंस्ट्रक्टर में नई लाइन को कॉपी करें:

@customElement('brick-viewer')
export class BrickViewer extends LitElement {
  private _loader = new LDrawLoader();
  private _model: any;
  private _numConstructionSteps?: number;
  step?: number;

  constructor() {
    // ...
    // Add this line right before this._animate();
    (this._loader as any).separateObjects = true;
    this._animate();
  }

  private _loadModel() {
    if (this.src === null) {
      return;
    }
    this._loader
        .setPath('')
        // Using our src property!
        .load(this.src, (newModel) => {

          if (this._model !== undefined) {
            this._scene.remove(this._model);
            this._model = undefined;
          }

          this._model = newModel;

          // Convert from LDraw coordinates: rotate 180 degrees around OX
          this._model.rotation.x = Math.PI;
          this._scene.add(this._model);

          this._numConstructionSteps = this._model.userData.numConstructionSteps;
          this.step = this._numConstructionSteps!;

          const bbox = new THREE.Box3().setFromObject(this._model);
          this._controls.target.copy(bbox.getCenter(new THREE.Vector3()));
          this._controls.update();
          this._controls.saveState();
        });
  }
}

_loadModel को कब कॉल किया जाना चाहिए? जब भी src एट्रिब्यूट बदलता है, तब इसे लागू करना ज़रूरी होता है. src प्रॉपर्टी को @property से डेकोरेट करके, हमने प्रॉपर्टी को lit-element अपडेट लाइफ़साइकल में शामिल कर लिया है. जब भी इन डेकोरेट की गई प्रॉपर्टी की वैल्यू बदलती है, तो कई तरीकों को कॉल किया जाता है. ये तरीके, प्रॉपर्टी की नई और पुरानी वैल्यू को ऐक्सेस कर सकते हैं. जिस लाइफ़साइकल तरीके में हमारी दिलचस्पी है उसे update कहा जाता है. update तरीके में PropertyValues आर्ग्युमेंट होता है. इसमें उन प्रॉपर्टी के बारे में जानकारी होती है जिनमें अभी-अभी बदलाव हुआ है. यह _loadModel को कॉल करने के लिए सबसे सही जगह है.

update तरीका जोड़ें:

export class BrickViewer extends LitElement {
  update(changedProperties: PropertyValues) {
    if (changedProperties.has('src')) {
      this._loadModel();
    }
    super.update(changedProperties);
  }
}

अब हमारा <brick-viewer> एलिमेंट, src एट्रिब्यूट के साथ दी गई ब्रिक फ़ाइल दिखा सकता है.

ब्रिक-व्यूअर एलिमेंट में, कार का मॉडल दिखाया गया है.

5. कुछ मॉडल दिखाना

अब, कंस्ट्रक्शन के मौजूदा चरण को कॉन्फ़िगर किया जा सकता है. हमें <brick-viewer step="5"></brick-viewer> के बारे में जानकारी देनी है. साथ ही, हमें यह देखना है कि पांचवें चरण में ब्रिक मॉडल कैसा दिखता है. इसके लिए, step प्रॉपर्टी को @property से सजाकर, इसे ऑब्ज़र्व की गई प्रॉपर्टी बनाते हैं.

step प्रॉपर्टी को डेकोरेट करें:

export class BrickViewer extends LitElement {
  @property({type: Number})
  step?: number;
}

अब हम एक हेल्पर मेथड जोड़ेंगे. इससे सिर्फ़ मौजूदा बिल्ड स्टेप तक के ब्रिक दिखेंगे. हम अपडेट करने के तरीके में हेल्पर को कॉल करेंगे, ताकि step प्रॉपर्टी में बदलाव होने पर, यह हर बार चले.

update तरीका अपडेट करें और नया _updateBricksVisibility तरीका जोड़ें:

export class BrickViewer extends LitElement {
  update(changedProperties: PropertyValues) {
    if (changedProperties.has('src')) {
      this._loadModel();
    }
    if (changedProperties.has('step')) {
      this._updateBricksVisibility();
    }
    super.update(changedProperties);
  }

  private _updateBricksVisibility() {
    this._model && this._model.traverse((c: any) => {
      if (c.isGroup && this.step) {
        c.visible = c.userData.constructionStep <= this.step;
      }
    });
    requestAnimationFrame(this._animate);
  }
}

ठीक है, अब अपने ब्राउज़र के devtools खोलें और <brick-viewer> एलिमेंट की जांच करें. इसमें step एट्रिब्यूट जोड़ें. जैसे:

ब्रिक-व्यूअर एलिमेंट का एचटीएमएल कोड. इसमें स्टेप एट्रिब्यूट को 10 पर सेट किया गया है.

देखें कि रेंडर किए गए मॉडल में क्या बदलाव होता है! step एट्रिब्यूट का इस्तेमाल करके, यह कंट्रोल किया जा सकता है कि मॉडल का कितना हिस्सा दिखाया जाए. step एट्रिब्यूट को "10" पर सेट करने पर, यह इस तरह दिखना चाहिए:

ईंटों से बना एक मॉडल, जिसमें सिर्फ़ 10 चरण पूरे किए गए हैं.

6. ब्रिक सेट नेविगेशन

mwc-icon-button

हमारे <brick-viewer> के असली उपयोगकर्ता को, यूज़र इंटरफ़ेस (यूआई) के ज़रिए बिल्ड के चरणों पर भी नेविगेट करने की सुविधा मिलनी चाहिए. आइए, अगले चरण, पिछले चरण, और पहले चरण पर जाने के लिए बटन जोड़ें. इसे आसान बनाने के लिए, हम मटीरियल डिज़ाइन के बटन वेब कॉम्पोनेंट का इस्तेमाल करेंगे. @material/mwc-icon-button पहले से ही इंपोर्ट किया जा चुका है. इसलिए, अब हम <mwc-icon-button></mwc-icon-button> को ड्रॉप करने के लिए तैयार हैं. हमें आइकॉन एट्रिब्यूट के साथ उस आइकॉन को तय करना होगा जिसका हमें इस्तेमाल करना है. जैसे: <mwc-icon-button icon="thumb_up"></mwc-icon-button>. सभी आइकॉन यहां देखे जा सकते हैं: material.io/resources/icons.

आइए, रेंडर करने के तरीके में कुछ आइकॉन बटन जोड़ें:

export class BrickViewer extends LitElement {
  render() {
    return html`
      ${this._renderer.domElement}
      <div id="controls">
        <mwc-icon-button icon="replay"></mwc-icon-button>
        <mwc-icon-button icon="navigate_before"></mwc-icon-button>
        <mwc-icon-button icon="navigate_next"></mwc-icon-button>
      </div>
    `;
  }
}

वेब कॉम्पोनेंट की मदद से, हमारे पेज पर मटीरियल डिज़ाइन का इस्तेमाल करना इतना आसान है!

इवेंट बाइंडिंग

इन बटन से कोई काम होना चाहिए. "जवाब दें" बटन दबाने पर, जवाब बनाने का चरण 1 पर रीसेट हो जाना चाहिए. "navigate_before" बटन से, कंस्ट्रक्शन के चरण को कम किया जाना चाहिए. वहीं, "navigate_next" बटन से, इसे बढ़ाया जाना चाहिए. lit-element, इवेंट बाइंडिंग की मदद से इस सुविधा को आसानी से जोड़ने की सुविधा देता है. अपने एचटीएमएल टेंप्लेट लिटरल में, @eventname=${eventHandler} सिंटैक्स का इस्तेमाल एलिमेंट एट्रिब्यूट के तौर पर करें. अब उस एलिमेंट पर eventname इवेंट का पता चलने पर, eventHandler चलेगा! उदाहरण के लिए, हम अपने तीन आइकॉन बटन में क्लिक इवेंट हैंडलर जोड़ते हैं:

export class BrickViewer extends LitElement {
  private _restart() {
    this.step! = 1;
  }

  private _stepBack() {
    this.step! -= 1;
  }

  private _stepForward() {
    this.step! += 1;
  }

  render() {
    return html`
      ${this._renderer.domElement}
      <div id="controls">
        <mwc-icon-button @click=${this._restart} icon="replay"></mwc-icon-button>
        <mwc-icon-button @click=${this._stepBack} icon="navigate_before"></mwc-icon-button>
        <mwc-icon-button @click=${this._stepForward} icon="navigate_next"></mwc-icon-button>
      </div>
    `;
  }
}

अब बटन पर क्लिक करके देखें. बहुत बढ़िया!

स्टाइल

बटन काम करते हैं, लेकिन वे अच्छे नहीं दिखते. ये सभी सबसे नीचे मौजूद हैं. आइए, इन्हें सीन पर ओवरले करने के लिए स्टाइल करें.

इन बटन पर स्टाइल लागू करने के लिए, हम static styles प्रॉपर्टी पर वापस जाते हैं. ये स्टाइल स्कोप की गई हैं. इसका मतलब है कि ये सिर्फ़ इस वेब कॉम्पोनेंट के एलिमेंट पर लागू होंगी. वेब कॉम्पोनेंट लिखने का एक फ़ायदा यह है कि सिलेक्टर आसान हो सकते हैं. साथ ही, सीएसएस को पढ़ना और लिखना आसान हो जाता है. अलविदा, BEM!

स्टाइल को इस तरह अपडेट करें:

export class BrickViewer extends LitElement {
  static styles = css`
    :host {
      display: block;
      position: relative;
    }
    #controls {
      position: absolute;
      bottom: 0;
      width: 100%;
      display: flex;
    }
  `;
}

ब्रिक-व्यूअर एलिमेंट. इसमें रीस्टार्ट, पीछे जाएं, और आगे जाएं बटन मौजूद हैं.

कैमरा रीसेट करने का बटन

हमारे <brick-viewer> के असली उपयोगकर्ता, माउस कंट्रोल का इस्तेमाल करके सीन को घुमा सकते हैं. बटन जोड़ने के साथ-साथ, कैमरे को उसकी डिफ़ॉल्ट पोज़िशन पर रीसेट करने के लिए भी एक बटन जोड़ते हैं. क्लिक इवेंट बाइंडिंग वाला दूसरा <mwc-icon-button> टैग, इस काम को पूरा कर देगा.

export class BrickViewer extends LitElement {
  private _resetCamera() {
    this._controls.reset();
  }

  render() {
    return html`
      <div id="controls">
        <!-- ... -->
        <!-- Add this button: -->
        <mwc-icon-button @click=${this._resetCamera} icon="center_focus_strong"></mwc-icon-button>
      </div>
    `;
  }
}

ज़्यादा तेज़ नेविगेशन

कुछ ब्रिक सेट में कई चरण होते हैं. ऐसा हो सकता है कि कोई व्यक्ति किसी खास चरण पर जाना चाहे. स्टेप नंबर वाला स्लाइडर जोड़ने से, तेज़ी से नेविगेट करने में मदद मिल सकती है. इसके लिए, हम <mwc-slider> एलिमेंट का इस्तेमाल करेंगे.

mwc-slider

स्लाइडर एलिमेंट के लिए, कुछ ज़रूरी डेटा की ज़रूरत होती है. जैसे, स्लाइडर की कम से कम और ज़्यादा से ज़्यादा वैल्यू. स्लाइडर की कम से कम वैल्यू हमेशा "1" हो सकती है. अगर मॉडल लोड हो गया है, तो स्लाइडर की ज़्यादा से ज़्यादा वैल्यू this._numConstructionSteps होनी चाहिए. हम <mwc-slider> के एट्रिब्यूट के ज़रिए, इन वैल्यू के बारे में बता सकते हैं. अगर _numConstructionSteps प्रॉपर्टी को तय नहीं किया गया है, तो max एट्रिब्यूट को सेट करने से बचने के लिए, हम ifDefined lit-html डायरेक्टिव का भी इस्तेमाल कर सकते हैं.

"वापस जाएं" और "आगे बढ़ें" बटन के बीच <mwc-slider> जोड़ें:

export class BrickViewer extends LitElement {
  render() {
    return html`
      <div id="controls">
        <!-- ... backwards button -->
        <!-- Add this slider: -->
        <mwc-slider
            step="1"
            pin
            markers
            min="1"
            max=${ifDefined(this._numConstructionSteps)}
        ></mwc-slider>
        <!-- ... forwards button -->
      </div>
    `;
  }
}

डेटा "अप"

जब कोई उपयोगकर्ता स्लाइडर को घुमाता है, तो कंस्ट्रक्शन का मौजूदा चरण बदल जाना चाहिए. साथ ही, मॉडल की दृश्यता को भी उसी के मुताबिक अपडेट किया जाना चाहिए. जब भी स्लाइडर को खींचा जाएगा, तब स्लाइडर एलिमेंट एक इनपुट इवेंट भेजेगा. इस इवेंट को कैप्चर करने और कंस्ट्रक्शन के चरण को बदलने के लिए, स्लाइडर पर इवेंट बाइंडिंग जोड़ें.

इवेंट बाइंडिंग जोड़ें:

export class BrickViewer extends LitElement {
  render() {
    return html`
      <div id="controls">
        <!-- ...  -->
        <!-- Add the @input event binding: -->
        <mwc-slider
            ...
            @input=${(e: CustomEvent) => this.step = e.detail.value}
        ></mwc-slider>
        <!-- ... -->
      </div>
    `;
  }
}

वाह! स्लाइडर का इस्तेमाल करके, यह बदला जा सकता है कि कौनसा चरण दिखाया जाए.

डेटा "डाउन"

एक और बात है. जब चरण बदलने के लिए "वापस जाएं" और "आगे बढ़ें" बटन का इस्तेमाल किया जाता है, तो स्लाइडर हैंडल को अपडेट करना ज़रूरी होता है. <mwc-slider> एट्रिब्यूट की वैल्यू को this.step एट्रिब्यूट से बाइंड करें.

value बाइंडिंग जोड़ें:

export class BrickViewer extends LitElement {
  render() {
    return html`
      <div id="controls">
        <!-- ...  -->
        <!-- Add the value property binding: -->
        <mwc-slider
            ...
            value=${ifDefined(this.step)}
        ></mwc-slider>
        <!-- ... -->
      </div>
    `;
  }
}

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

export class BrickViewer extends LitElement {
  static styles = css`
    /* ... */
    mwc-slider {
      flex-grow: 1;
    }
  `;
}

इसके अलावा, हमें स्लाइडर एलिमेंट पर ही layout को कॉल करना होगा. हम ऐसा firstUpdated लाइफ़साइकल के तरीके से करेंगे. इसे तब कॉल किया जाता है, जब डीओएम को पहली बार लेआउट किया जाता है. query डेकोरेटर की मदद से, हमें टेंप्लेट में मौजूद स्लाइडर एलिमेंट का रेफ़रंस मिल सकता है.

export class BrickViewer extends LitElement {
  @query('mwc-slider')
  slider!: Slider|null;

  async firstUpdated() {
    if (this.slider) {
      await this.slider.updateComplete
      this.slider.layout();
    }
  }
}

यहां स्लाइडर में जोड़ी गई सभी चीज़ें एक साथ दी गई हैं. साथ ही, स्लाइडर को शानदार बनाने के लिए, इसमें अतिरिक्त pin और markers एट्रिब्यूट जोड़े गए हैं:

export class BrickViewer extends LitElement {
 @query('mwc-slider')
 slider!: Slider|null;

 static styles = css`
   /* ... */
   mwc-slider {
     flex-grow: 1;
   }
 `;

 async firstUpdated() {
   if (this.slider) {
     await this.slider.updateComplete
     this.slider.layout();
   }
 }

 render() {
   return html`
     ${this._renderer.domElement}
     <div id="controls">
       <mwc-icon-button @click=${this._restart} icon="replay"></mwc-icon-button>
       <mwc-icon-button @click=${this._stepBack} icon="navigate_before"></mwc-icon-button>
       <mwc-slider
         step="1"
         pin
         markers
         min="1"
         max=${ifDefined(this._numConstructionSteps)}
         ?disabled=${this._numConstructionSteps === undefined}
         value=${ifDefined(this.step)}
         @input=${(e: CustomEvent) => this.constructionStep = e.detail.value}
       ></mwc-slider>
       <mwc-icon-button @click=${this._stepForward} icon="navigate_next"></mwc-icon-button>
       <mwc-icon-button @click=${this._resetCamera} icon="center_focus_strong"></mwc-icon-button>
     </div>
   `;
 }
}

यह रहा फ़ाइनल प्रॉडक्ट!

ब्रिक-व्यूअर एलिमेंट की मदद से, कार के ब्रिक मॉडल को नेविगेट करना

7. नतीजा

हमें lit-element का इस्तेमाल करके, अपना एचटीएमएल एलिमेंट बनाने के बारे में बहुत कुछ सीखने को मिला. हमने इन विषयों पर चर्चा की:

  • कस्टम एलिमेंट तय करना
  • किसी एट्रिब्यूट एपीआई का एलान करना
  • कस्टम एलिमेंट के लिए व्यू रेंडर करना
  • स्टाइल को एनकैप्सुलेट करना
  • डेटा पास करने के लिए इवेंट और प्रॉपर्टी का इस्तेमाल करना

अगर आपको lit-element के बारे में ज़्यादा जानना है, तो इसकी आधिकारिक साइट पर जाकर ज़्यादा जानकारी पढ़ें.

पूरा किया गया ब्रिक-व्यूअर एलिमेंट, stackblitz.com/edit/brick-viewer-complete पर देखा जा सकता है.

brick-viewer को NPM पर भी शिप किया जाता है. इसका सोर्स यहां देखा जा सकता है: Github repo.