লিট-এলিমেন্ট সহ একটি ইট ভিউয়ার তৈরি করুন

১. ভূমিকা

ওয়েব উপাদান

ওয়েব কম্পোনেন্ট হলো ওয়েব স্ট্যান্ডার্ডের একটি সংগ্রহ যা ডেভেলপারদের কাস্টম এলিমেন্ট দিয়ে HTML-কে সম্প্রসারিত করার সুযোগ দেয়। এই কোডল্যাবে, আপনি <brick-viewer> এলিমেন্টটি সংজ্ঞায়িত করবেন, যা ইটের মডেল প্রদর্শন করতে সক্ষম হবে!

আলোকিত উপাদান

আমাদের কাস্টম এলিমেন্ট <brick-viewer> সংজ্ঞায়িত করতে, আমরা lit-element ব্যবহার করব। lit-element হলো একটি লাইটওয়েট বেস ক্লাস যা ওয়েব কম্পোনেন্ট স্ট্যান্ডার্ডে কিছু সিনট্যাকটিক সুগার যোগ করে। এটি আমাদের কাস্টম এলিমেন্ট নিয়ে কাজ শুরু করা সহজ করে দেবে।

শুরু করুন

আমরা একটি অনলাইন স্ট্যাকব্লিটজ পরিবেশে কোডিং করব, তাই এই লিঙ্কটি একটি নতুন উইন্ডোতে খুলুন:

stackblitz.com/edit/brick-viewer

চলুন শুরু করা যাক!

২. একটি কাস্টম এলিমেন্ট সংজ্ঞায়িত করুন

শ্রেণী সংজ্ঞা

একটি কাস্টম এলিমেন্ট নির্ধারণ করতে, LitElement এক্সটেন্ড করে এমন একটি ক্লাস তৈরি করুন এবং সেটিকে @customElement দিয়ে ডেকোরেট করুন। @customElement এর আর্গুমেন্টটি হবে কাস্টম এলিমেন্টটির নাম।

brick-viewer.ts ফাইলে নিম্নলিখিতটি যোগ করুন:

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

এখন, <brick-viewer></brick-viewer> এলিমেন্টটি HTML-এ ব্যবহারের জন্য প্রস্তুত। কিন্তু, এটি ব্যবহার করার চেষ্টা করলে কিছুই রেন্ডার হবে না। চলুন, এর সমাধান করা যাক।

রেন্ডার পদ্ধতি

কম্পোনেন্টের ভিউ বাস্তবায়ন করতে, render নামের একটি মেথড সংজ্ঞায়িত করুন। এই মেথডটি html ফাংশন দিয়ে ট্যাগ করা একটি টেমপ্লেট লিটারেল রিটার্ন করবে। ট্যাগ করা টেমপ্লেট লিটারেলটির মধ্যে আপনার ইচ্ছামত HTML রাখুন। যখন আপনি <brick-viewer> ব্যবহার করবেন, তখন এটি রেন্ডার হবে।

render মেথডটি যোগ করুন:

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

৩. LDraw ফাইল নির্দিষ্ট করা

একটি বৈশিষ্ট্য সংজ্ঞায়িত করুন

খুব ভালো হতো যদি <brick-viewer> এর কোনো ব্যবহারকারী একটি অ্যাট্রিবিউট ব্যবহার করে কোন ইটের মডেলটি প্রদর্শন করা হবে তা নির্দিষ্ট করে দিতে পারতেন, যেমনটা এখানে দেখানো হয়েছে:

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

যেহেতু আমরা একটি HTML এলিমেন্ট তৈরি করছি, তাই আমরা ডিক্লারেটিভ API-এর সুবিধা নিতে পারি এবং একটি <img> বা <video> ট্যাগের মতোই একটি সোর্স অ্যাট্রিবিউট সংজ্ঞায়িত করতে পারি। lit-element-এর সাহায্যে, @property দিয়ে একটি ক্লাস প্রপার্টিকে ডেকোরেট করার মতোই এটি সহজ। type অপশনটি আপনাকে নির্দিষ্ট করে দেয় যে, একটি HTML অ্যাট্রিবিউট হিসেবে ব্যবহারের জন্য lit-element কীভাবে প্রপার্টিটি পার্স করবে।

src প্রপার্টি এবং অ্যাট্রিবিউট নির্ধারণ করুন:

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

<brick-viewer> এ এখন একটি src অ্যাট্রিবিউট আছে যা আমরা HTML-এ সেট করতে পারি! 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 এই কঠিন কাজটি করে দেয়, তাই আপনাকে তা করতে হয় না।

৪. Three.js দিয়ে প্রেক্ষাপট তৈরি করুন

লাইট, ক্যামেরা, রেন্ডার!

আমাদের কাস্টম এলিমেন্টটি আমাদের ৩ডি ইটের মডেলগুলো রেন্ডার করার জন্য 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 অবজেক্টটি একটি DOM এলিমেন্ট প্রদান করে যা রেন্ডার করা three.js সিন প্রদর্শন করে। এটি domElement প্রপার্টির মাধ্যমে অ্যাক্সেস করা হয়। আমরা ${value} সিনট্যাক্স ব্যবহার করে এই মানটিকে রেন্ডার টেমপ্লেট লিটারেলের মধ্যে অন্তর্ভুক্ত করতে পারি।

টেমপ্লেটে থাকা src মেসেজটি সরিয়ে দিন এবং রেন্ডারারের DOM এলিমেন্টটি যুক্ত করুন:

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

রেন্ডারারের ডোম এলিমেন্টটি সম্পূর্ণরূপে দেখানোর জন্য, আমাদের <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 সিন প্রদর্শন করছে:

একটি ব্রিক-ভিউয়ার এলিমেন্ট যা একটি রেন্ডার করা, কিন্তু খালি, দৃশ্য প্রদর্শন করে।

কিন্তু... এটা তো খালি। চলো এটাকে একটা মডেল দিই।

ইট লোডার

আমরা আগে সংজ্ঞায়িত করা src প্রপার্টিটি LDrawLoader-এ পাস করব, যা 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-এর আপডেট লাইফসাইকেলে অন্তর্ভুক্ত করেছি। যখনই এই ডেকোরেটেড প্রপার্টিগুলোর কোনোটির মান পরিবর্তিত হয়, তখন এমন কিছু মেথড কল করা হয় যেগুলো প্রপার্টিগুলোর নতুন এবং পুরোনো মান অ্যাক্সেস করতে পারে। আমরা যে লাইফসাইকেল মেথডটি নিয়ে আগ্রহী, তার নাম হলো updateupdate মেথডটি একটি PropertyValues ​​আর্গুমেন্ট গ্রহণ করে, যাতে সদ্য পরিবর্তিত হওয়া প্রপার্টিগুলোর তথ্য থাকে। _loadModel কল করার জন্য এটিই উপযুক্ত জায়গা।

update পদ্ধতি যোগ করুন:

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

আমাদের <brick-viewer> এলিমেন্টটি এখন src অ্যাট্রিবিউটের মাধ্যমে নির্দিষ্ট করা একটি ব্রিক ফাইল প্রদর্শন করতে পারে।

একটি ব্রিক-ভিউয়ার এলিমেন্ট, যা একটি গাড়ির মডেল প্রদর্শন করছে।

৫. আংশিক মডেল প্রদর্শন করা

এখন, চলুন বর্তমান নির্মাণ ধাপটিকে কনফিগারযোগ্য করে তুলি। আমরা <brick-viewer step="5"></brick-viewer> নির্দিষ্ট করতে চাই, এবং এর মাধ্যমে পঞ্চম নির্মাণ ধাপে ইটের মডেলটি কেমন দেখায় তা দেখতে চাই। এটি করার জন্য, step প্রপার্টিটিকে @property দিয়ে ডেকোরেট করে একটি observed প্রপার্টিতে পরিণত করা যাক।

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

আচ্ছা, এবার আপনার ব্রাউজারের ডেভটুলস খুলুন এবং <brick-viewer> এলিমেন্টটি ইন্সপেক্ট করুন। এতে একটি step অ্যাট্রিবিউট যোগ করুন, এইভাবে:

brick-viewer এলিমেন্টের HTML কোড, যেখানে step অ্যাট্রিবিউটের মান 10 সেট করা আছে।

রেন্ডার করা মডেলটির কী হয় দেখুন! মডেলটির কতটুকু অংশ দেখানো হবে তা নিয়ন্ত্রণ করতে আমরা ' step ' অ্যাট্রিবিউট ব্যবহার করতে পারি। step ' অ্যাট্রিবিউটটি "10" -এ সেট করা হলে এটি দেখতে এইরকম হবে:

মাত্র দশটি নির্মাণ ধাপ অনুসরণ করে ইট দিয়ে একটি মডেল তৈরি করা হয়েছে।

৬. ইটের সেট নেভিগেশন

mwc-আইকন-বোতাম

আমাদের <brick-viewer> -এর ব্যবহারকারীকে UI-এর মাধ্যমে বিল্ডের ধাপগুলোতে নেভিগেট করতে সক্ষম হতে হবে। চলুন, পরবর্তী ধাপ, পূর্ববর্তী ধাপ এবং প্রথম ধাপে যাওয়ার জন্য বাটন যোগ করি। কাজটি সহজ করার জন্য আমরা ম্যাটেরিয়াল ডিজাইনের বাটন ওয়েব কম্পোনেন্ট ব্যবহার করব। যেহেতু @material/mwc-icon-button ইতিমধ্যে ইম্পোর্ট করা আছে, তাই আমরা <mwc-icon-button></mwc-icon-button> যোগ করার জন্য প্রস্তুত। আমরা icon অ্যাট্রিবিউটের মাধ্যমে আমাদের পছন্দের আইকনটি নির্দিষ্ট করে দিতে পারি, যেমন: <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>
    `;
  }
}

ওয়েব কম্পোনেন্টগুলোর কল্যাণে আমাদের পেজে ম্যাটেরিয়াল ডিজাইন ব্যবহার করা এতটাই সহজ!

ইভেন্ট বাইন্ডিং

এই বাটনগুলোর আসলে কিছু একটা করা উচিত। 'reply' বাটনটি কনস্ট্রাকশন স্টেপকে ১-এ রিসেট করবে। 'navigate_before' বাটনটি কনস্ট্রাকশন স্টেপ এক কমাবে, এবং 'navigate_next' বাটনটি তা এক বাড়াবে। lit-element ইভেন্ট বাইন্ডিংয়ের মাধ্যমে এই কার্যকারিতা যোগ করা সহজ করে তোলে। আপনার html টেমপ্লেট লিটারেল-এ, এলিমেন্ট অ্যাট্রিবিউট হিসেবে @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 প্রপার্টিতে ফিরে যাই। এই স্টাইলগুলো স্কোপড, যার মানে হলো এগুলো শুধুমাত্র এই ওয়েব কম্পোনেন্টের ভেতরের এলিমেন্টগুলোতেই প্রয়োগ হবে। ওয়েব কম্পোনেন্ট লেখার এটাই একটা সুবিধা: সিলেক্টরগুলো আরও সরল হতে পারে, এবং CSS পড়া ও লেখা আরও সহজ হয়ে যায়। বিদায়, 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-স্লাইডার

স্লাইডার এলিমেন্টের জন্য কিছু গুরুত্বপূর্ণ ডেটা প্রয়োজন, যেমন সর্বনিম্ন এবং সর্বোচ্চ স্লাইডার মান। সর্বনিম্ন স্লাইডার মান সর্বদা "1" হতে পারে। মডেলটি লোড হয়ে থাকলে, সর্বোচ্চ স্লাইডার মান this._numConstructionSteps হওয়া উচিত। আমরা <mwc-slider> তার অ্যাট্রিবিউটের মাধ্যমে এই মানগুলো জানাতে পারি। এছাড়াও, যদি _numConstructionSteps প্রপার্টিটি সংজ্ঞায়িত না করা হয়ে থাকে, তাহলে max অ্যাট্রিবিউট সেট করা এড়ানোর জন্য আমরা ifDefined lit-html ডিরেক্টিভটি ব্যবহার করতে পারি।

'back' এবং 'forward' বাটনের মধ্যে একটি <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>
    `;
  }
}

বাহ! কোন ধাপটি দেখানো হবে তা পরিবর্তন করতে আমরা স্লাইডারটি ব্যবহার করতে পারি।

ডেটা "ডাউন"

আরও একটি বিষয় আছে। যখন ধাপ পরিবর্তন করার জন্য 'back' এবং 'next' বাটন ব্যবহার করা হয়, তখন স্লাইডার হ্যান্ডেলটি আপডেট করতে হবে। <mwc-slider> এর value অ্যাট্রিবিউটকে 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 লাইফসাইকেল মেথডে করব, যেটি DOM প্রথমবার লেআউট হওয়ার পর কল করা হয়। 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>
   `;
 }
}

এই হলো চূড়ান্ত ফলাফল!

ব্রিক-ভিউয়ার এলিমেন্ট ব্যবহার করে একটি গাড়ির ব্রিক মডেল নেভিগেট করা

৭. উপসংহার

কীভাবে লিট-এলিমেন্ট ব্যবহার করে আমাদের নিজস্ব এইচটিএমএল এলিমেন্ট তৈরি করা যায়, সে সম্পর্কে আমরা অনেক কিছু শিখেছি। আমরা শিখেছি কীভাবে:

  • একটি কাস্টম উপাদান সংজ্ঞায়িত করুন
  • একটি অ্যাট্রিবিউট এপিআই ঘোষণা করুন
  • একটি কাস্টম এলিমেন্টের জন্য একটি ভিউ রেন্ডার করুন
  • শৈলীগুলিকে আবদ্ধ করুন
  • ডেটা পাস করতে ইভেন্ট এবং প্রপার্টি ব্যবহার করুন

আপনি যদি লিট-এলিমেন্ট সম্পর্কে আরও জানতে চান, তাহলে এর অফিসিয়াল সাইটে আরও পড়তে পারেন।

আপনি stackblitz.com/edit/brick-viewer-complete -এ একটি সম্পূর্ণ ব্রিক-ভিউয়ার এলিমেন্ট দেখতে পারেন।

brick-viewer NPM-এও পাওয়া যায়, এবং আপনি এর সোর্স এখানে দেখতে পারেন: গিটহাব রিপো