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

1. บทนำ

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

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

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

เราจะใช้องค์ประกอบ "แสงสว่าง" เพื่อช่วยเรากำหนดองค์ประกอบที่กำหนดเอง <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 เราจึงใช้ประโยชน์จาก API แบบประกาศและกำหนดแอตทริบิวต์แหล่งที่มาได้ เช่นเดียวกับแท็ก <img> หรือ <video> เมื่อมีองค์ประกอบส่องสว่างจะช่วยให้ตกแต่งที่พักได้ง่ายดายพอๆ กับการตกแต่งที่พักระดับชั้นเรียนด้วย @property ตัวเลือก type ให้คุณระบุวิธีที่องค์ประกอบที่มีแสงสว่างเพียงพอจะแยกวิเคราะห์พร็อพเพอร์ตี้เพื่อใช้เป็นแอตทริบิวต์ HTML

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

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

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

กำลังแสดงค่า

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

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

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

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

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

แสงพร้อม กล้องพร้อม เรนเดอร์!

องค์ประกอบที่กำหนดเองของเราจะใช้ third.js ในการแสดงผลโมเดลตัวต่อ 3 มิติของเรา เราต้องการทำบางสิ่งเพียงครั้งเดียวสำหรับแต่ละอินสแตนซ์ขององค์ประกอบ <brick-viewer> เช่น ตั้งค่าฉาก 3.js, กล้อง และการจัดแสง เราจะเพิ่มข้อมูลเหล่านี้ลงในคลาส BrickViewer เราจะเก็บวัตถุบางอย่างไว้เป็นคุณสมบัติของคลาสเพื่อใช้ในภายหลัง ได้แก่ กล้อง ฉาก การควบคุม และโหมดแสดงภาพ

เพิ่มการตั้งค่าฉาก third.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 ที่แสดงโหมด 3.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> กำลังแสดงโหมด 3.js ที่แสดงผล:

องค์ประกอบของผู้ดูอิฐที่แสดงฉากที่แสดงผลแล้ว แต่ยังว่างเปล่า

แต่... ว่างเปล่า เราจะลองระบุโมเดล

รถตักอิฐ

เราจะส่งพร็อพเพอร์ตี้ src ที่เรากำหนดไว้ก่อนหน้านี้ไปยัง LDrawLoader ซึ่งจัดส่งด้วย 3.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 เราได้เลือกใช้วงจรการอัปเดตองค์ประกอบที่มีแสงสว่าง เมื่อหนึ่งในสถานที่ตกแต่งเหล่านี้ การเปลี่ยนแปลงค่า เรียกชุดวิธีการว่าสามารถเข้าถึงค่าใหม่และค่าเก่าของพร็อพเพอร์ตี้ได้ วิธีการใช้วงจรที่เราสนใจชื่อว่า 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 ขององค์ประกอบผู้ดูอิฐที่กำหนดแอตทริบิวต์ขั้นตอนเป็น 10

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

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

6. การนำทางชุดอิฐ

ปุ่มไอคอน mwc

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

การผูกกิจกรรม

ปุ่มเหล่านี้ควรทำหน้าที่บางอย่าง "ตอบกลับ" ควรรีเซ็ตขั้นตอนการสร้างเป็น 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> สามารถหมุนฉากโดยใช้ตัวควบคุมด้วยเมาส์ได้ ระหว่างที่เราเพิ่มปุ่ม ให้เพิ่มปุ่มสำหรับรีเซ็ตกล้องไปยังตำแหน่งเริ่มต้น <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

องค์ประกอบแถบเลื่อนต้องมีข้อมูลสำคัญ 2-3 อย่าง เช่น ค่าแถบเลื่อนต่ำสุดและสูงสุด ค่าแถบเลื่อนต่ำสุดจะเป็น "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 จะช่วยให้เรารับการอ้างอิงองค์ประกอบแถบเลื่อนในเทมเพลตได้

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. บทสรุป

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

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

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

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

เรามีการจัดส่งเครื่องมือตรวจสอบอิฐใน NPM ด้วย และคุณสามารถดูแหล่งที่มาได้ที่ที่เก็บของ GitHub