สร้าง Brick Viewer ที่มีองค์ประกอบที่สว่าง

1. บทนำ

คอมโพเนนต์ของเว็บ

Web Components คือชุดมาตรฐานเว็บที่ช่วยให้นักพัฒนาซอฟต์แวร์ขยาย HTML ด้วยองค์ประกอบที่กำหนดเองได้ ใน Codelab นี้ คุณจะได้กำหนดองค์ประกอบ <brick-viewer> ซึ่งจะแสดงโมเดลอิฐได้

lit-element

เราจะใช้ lit-element เพื่อช่วยกำหนดองค์ประกอบที่กำหนดเอง <brick-viewer> 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> พร้อมใช้งานใน HTML แล้ว แต่หากคุณลองใช้ จะไม่มีการแสดงผลใดๆ เรามาแก้ปัญหานั้นกันดีกว่า

วิธีการแสดงผล

หากต้องการใช้มุมมองของคอมโพเนนต์ ให้กำหนดเมธอดชื่อ render เมธอดนี้ควรแสดงผลเทมเพลตสตริงที่ติดแท็กด้วยฟังก์ชัน html ใส่ 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>

เนื่องจากเรากำลังสร้างองค์ประกอบ HTML เราจึงใช้ประโยชน์จาก Declarative API และกำหนดแอตทริบิวต์แหล่งที่มาได้เหมือนกับแท็ก <img> หรือ <video> lit-element ช่วยให้การตกแต่งพร็อพเพอร์ตี้ของคลาสด้วย @property เป็นเรื่องง่าย ตัวเลือก type ช่วยให้คุณระบุวิธีที่ LitElement แยกวิเคราะห์พร็อพเพอร์ตี้เพื่อใช้เป็นแอตทริบิวต์ HTML ได้

กําหนดพร็อพเพอร์ตี้และแอตทริบิวต์ src ดังนี้

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

<brick-viewer> มีแอตทริบิวต์ src ที่เราตั้งค่าใน HTML ได้แล้ว ค่าของพร็อพเพอร์ตี้นี้สามารถอ่านได้จากภายในคลาส BrickViewer ของเราอยู่แล้วด้วย lit-element

การแสดงค่า

เราสามารถแสดงค่าของแอตทริบิวต์ src ได้โดยใช้ค่าดังกล่าวในเทมเพลตสตริงของเมธอดการแสดงผล ประมาณค่าในช่วงลงในเทมเพลตสตริงโดยใช้ไวยากรณ์ ${value}

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

ตอนนี้เราเห็นค่าของแอตทริบิวต์ src ในองค์ประกอบ <brick-viewer> ในหน้าต่าง ลองเปิดเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ของเบราว์เซอร์ แล้วเปลี่ยนแอตทริบิวต์ src ด้วยตนเอง ลองเลย...

...คุณสังเกตไหมว่าข้อความในองค์ประกอบอัปเดตโดยอัตโนมัติ lit-element จะสังเกตพร็อพเพอร์ตี้ของคลาสที่ตกแต่งด้วย @property และแสดงผลมุมมองอีกครั้งให้คุณ lit-element จะจัดการงานหนักๆ ให้คุณเอง

4. ตั้งค่าฉากด้วย Three.js

ไฟพร้อม กล้องพร้อม แสดงผลได้เลย

Custom Element ของเราจะใช้ three.js เพื่อแสดงโมเดลอิฐ 3 มิติ มีบางอย่างที่เราต้องการทำเพียงครั้งเดียวสำหรับแต่ละอินสแตนซ์ขององค์ประกอบ <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}
    `;
  }
}

หากต้องการอนุญาตให้แสดงองค์ประกอบ 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 ที่เรนเดอร์แล้ว

องค์ประกอบ brick-viewer แสดงฉากที่เรนเดอร์แล้วแต่ว่างเปล่า

แต่...มันว่างเปล่า มาใส่โมเดลกัน

ตัวโหลดอิฐ

เราจะส่งพร็อพเพอร์ตี้ src ที่เรากำหนดไว้ก่อนหน้านี้ไปยัง LDrawLoader ซึ่งมาพร้อมกับ three.js

ไฟล์ LDraw สามารถแยกโมเดล Brick ออกเป็นขั้นตอนการสร้างที่แยกต่างหากได้ คุณเข้าถึงจำนวนขั้นตอนทั้งหมดและการมองเห็นตัวต่อแต่ละชิ้นได้ผ่าน 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> และควรเห็นลักษณะของโมเดลอิฐในขั้นตอนการก่อสร้างที่ 5 โดยเราจะทำให้พร็อพเพอร์ตี้ 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 ลงในแท็ก ดังนี้

โค้ด HTML ขององค์ประกอบ brick-viewer โดยตั้งค่าแอตทริบิวต์ step เป็น 10

ดูว่าเกิดอะไรขึ้นกับโมเดลที่เรนเดอร์ เราสามารถใช้แอตทริบิวต์ step เพื่อควบคุมปริมาณของโมเดลที่จะแสดง ซึ่งจะมีลักษณะดังนี้เมื่อตั้งค่าแอตทริบิวต์ step เป็น "10"

โมเดลตัวต่อที่สร้างขึ้นโดยมีขั้นตอนการต่อเพียง 10 ขั้นตอน

6. การไปยังส่วนต่างๆ ของชุดอิฐ

mwc-icon-button

ผู้ใช้ปลายทางของ <brick-viewer> ควรไปยังขั้นตอนการสร้างผ่าน UI ได้ด้วย มาเพิ่มปุ่มสำหรับไปที่ขั้นตอนถัดไป ขั้นตอนก่อนหน้า และขั้นตอนแรกกัน เราจะใช้คอมโพเนนต์เว็บปุ่มของ Material Design เพื่อให้ใช้งานได้ง่าย เนื่องจากเราได้นำเข้า @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>
    `;
  }
}

การใช้ Material Design ในหน้าเว็บของเรานั้นง่ายมากด้วยคอมโพเนนต์ของเว็บ

การเชื่อมโยงเหตุการณ์

ปุ่มเหล่านี้ควรทำงานได้จริง ปุ่ม "ตอบ" จะรีเซ็ตขั้นตอนการสร้างเป็น 1 ปุ่ม "navigate_before" ควรลดขั้นตอนการสร้าง และปุ่ม "navigate_next" ควรเพิ่มขั้นตอนการสร้าง lit-element ช่วยให้เพิ่มฟังก์ชันนี้ได้ง่ายๆ ด้วยการเชื่อมโยงเหตุการณ์ ในสตริงเทมเพลต HTML ให้ใช้ไวยากรณ์ @eventname=${eventHandler} เป็นแอตทริบิวต์ขององค์ประกอบ ตอนนี้ eventHandler จะทำงานเมื่อตรวจพบเหตุการณ์ eventname ในองค์ประกอบนั้น ตัวอย่างเช่น มาเพิ่มตัวแฮนเดิลกิจกรรมการคลิกให้กับปุ่มไอคอน 3 ปุ่มกัน

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 ที่มีปุ่มรีสตาร์ท ย้อนกลับ และไปข้างหน้า

ปุ่มรีเซ็ตกล้อง

ผู้ใช้ปลายทางของ <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> ค่าเหล่านี้ผ่านแอตทริบิวต์ของค่า นอกจากนี้ เรายังใช้ifDefinedคำสั่ง lit-html เพื่อหลีกเลี่ยงการตั้งค่าแอตทริบิวต์ max ได้หากไม่ได้กำหนดพร็อพเพอร์ตี้ _numConstructionSteps

เพิ่ม <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>
    `;
  }
}

เราเกือบจะเสร็จสิ้นการใช้แถบเลื่อนแล้ว เพิ่มสไตล์ Flex เพื่อให้ทำงานร่วมกับตัวควบคุมอื่นๆ ได้อย่างราบรื่น

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

นอกจากนี้ เรายังต้องเรียก layout ในองค์ประกอบแถบเลื่อนด้วย เราจะทำเช่นนั้นในfirstUpdatedเมธอดวงจร ซึ่งจะเรียกใช้เมื่อ DOM จัดวางเป็นครั้งแรก query Decorator จะช่วยให้เราอ้างอิงองค์ประกอบแถบเลื่อนในเทมเพลตได้

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

และนี่คือผลงานชิ้นสุดท้าย

การไปยังส่วนต่างๆ ของโมเดลอิฐรถยนต์ด้วยองค์ประกอบ brick-viewer

7. บทสรุป

เราได้เรียนรู้มากมายเกี่ยวกับวิธีใช้ lit-element เพื่อสร้างองค์ประกอบ HTML ของเราเอง เราได้เรียนรู้วิธีการต่อไปนี้

  • กำหนดองค์ประกอบที่กำหนดเอง
  • ประกาศ API แอตทริบิวต์
  • แสดงผลมุมมองสำหรับองค์ประกอบที่กำหนดเอง
  • แคปซูลสไตล์
  • ใช้เหตุการณ์และพร็อพเพอร์ตี้เพื่อส่งข้อมูล

หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับ LitElement โปรดอ่านข้อมูลเพิ่มเติมที่เว็บไซต์อย่างเป็นทางการ

คุณดูองค์ประกอบ Brick-Viewer ที่เสร็จสมบูรณ์ได้ที่ stackblitz.com/edit/brick-viewer-complete

นอกจากนี้ brick-viewer ยังจัดส่งใน NPM และคุณดูแหล่งที่มาได้ที่ที่เก็บ Github