1. Giriş
Web bileşenleri
Web bileşenleri, geliştiricilerin HTML'yi özel öğelerle genişletmesine olanak tanıyan web standartları koleksiyonudur. Bu codelab'de, tuğla modelleri görüntüleyebilen <brick-viewer>
öğesini tanımlayacaksınız.
ışık-öğesi
<brick-viewer>
özel öğemizi tanımlamamıza yardımcı olmak için "lit-element" ifadesini kullanacağız. lit-element, web bileşenleri standardına sentetik şeker ekleyen hafif bir temel sınıftır. Bu yöntem, özel öğemizi kullanmaya başlamamızı kolaylaştıracaktır.
Başlayın
Online Stackblitz ortamında kod yazacağız, bu nedenle şu bağlantıyı yeni bir pencerede açın:
stackblitz.com/edit/brick-viewer
Haydi, başlayalım.
2. Özel Öğe Tanımlayın
Sınıf tanımı
Özel öğe tanımlamak için LitElement
öğesini genişleten bir sınıf oluşturun ve bunu @customElement
ile süsleyin. @customElement
bağımsız değişkeni, özel öğenin adı olacaktır.
brick-viewer.ts içine şunu koyun:
@customElement('brick-viewer')
export class BrickViewer extends LitElement {
}
Artık <brick-viewer></brick-viewer>
öğesi, HTML'de kullanılmaya hazırdır. Ancak denerseniz hiçbir şey oluşturulmaz. Gelin bu sorunu çözelim.
Oluşturma yöntemi
Bileşenin görünümünü uygulamak için oluşturucu adlı bir yöntem tanımlayın. Bu yöntem, html
işleviyle etiketlenmiş bir değişmez şablon döndürmelidir. Etiketli şablon değişmez değerine istediğiniz HTML'yi girin. Bu, <brick-viewer>
kullandığınızda oluşturulur.
render
yöntemini ekleyin:
export class BrickViewer extends LitElement {
render() {
return html`<div>Brick viewer</div>`;
}
}
3. LDraw Dosyasını Belirtme
Bir mülk tanımlayın
Bir <brick-viewer>
kullanıcısı, aşağıdaki gibi bir özellik kullanarak hangi tuğla modelinin gösterileceğini belirtebilse harika olurdu:
<brick-viewer src="path/to/model.ldraw"></brick-viewer>
Bir HTML öğesi oluşturduğumuz için bildirim temelli API'den yararlanabilir ve <img>
veya <video>
etiketinde olduğu gibi bir kaynak özelliği tanımlayabiliriz. Işıklandırma ile sınıfınızı @property
ile dekore etmek kadar kolay. type
seçeneği, "lit" öğesinin özelliği bir HTML özelliği olarak kullanmak üzere nasıl ayrıştıracağını belirtebilmenizi sağlar.
src
özelliğini ve özelliğini tanımlayın:
export class BrickViewer extends LitElement {
@property({type: String})
src: string|null = null;
}
<brick-viewer>
artık HTML'de ayarlayabileceğimiz bir src
özelliğine sahip. Değeri, lit-element sayesinde BrickViewer
sınıfımızdan okunabiliyor.
Değerleri görüntüleme
src
özelliğinin değerini, oluşturma yönteminin şablon değişmez değerinde kullanarak görüntüleyebiliriz. ${value}
söz dizimini kullanarak değerleri şablon değişmez değerlerine dönüştürün.
export class BrickViewer extends LitElement {
render() {
return html`<div>Brick model: ${this.src}</div>`;
}
}
Şimdi, src özelliğinin değerini penceredeki <brick-viewer>
öğesinde görüyoruz. Şunu deneyin: Tarayıcınızın geliştirici araçlarını açın ve src özelliğini manuel olarak değiştirin. Haydi, deneyin...
Öğedeki metnin otomatik olarak güncellendiğini fark ettiniz mi? lit-element, @property
ile dekore edilmiş sınıf özelliklerini gözlemler ve görünümü sizin için yeniden oluşturur. işin kendisinde işin işini yapar, böylece sizin uğraşmanıza gerek kalmaz.
4. Three.js ile sahneyi oluşturun
Işık, Kamera, Oluşturma!
Özel öğemiz, 3D tuğla modellerimizi oluşturmak içinthree.js kullanır. Bir <brick-viewer>
öğesinin her örneği için yalnızca bir kez yapmak istediğimiz şeyler var. Örneğin,Three.js sahnesini, kamerayı ve ışıklandırmayı ayarlayabiliriz. Bunları, BrickViewer sınıfına oluşturucuya ekleyeceğiz. Daha sonra kullanabilmeniz için bazı nesneleri sınıf özellikleri olarak tutacağız: kamera, sahne, denetimler ve oluşturucu.
Üç.js sahne kurulumunu ekleyin:
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
nesnesi, oluşturulanThree.js sahnesini gösteren bir DOM öğesi sağlar. Bu mülke domElement
mülkü üzerinden erişilir. Bu değeri, ${value}
söz dizimini kullanarak oluşturma şablonu sabit değerine ayarlayabiliriz.
Şablonda bulunan src
mesajını kaldırın ve oluşturucunun DOM öğesini ekleyin:
export class BrickViewer extends LitElement {
render() {
return html`
${this._renderer.domElement}
`;
}
}
Oluşturucunun dom öğesinin tamamının gösterilmesini sağlamak için <brick-viewer>
öğesinin kendisini de display: block
olarak ayarlamamız gerekir. Stilleri, css
şablon sabit değerine ayarlanmış styles
adlı bir statik mülkte sağlayabiliriz.
Sınıfa şu stili ekleyin:
export class BrickViewer extends LitElement {
static styles = css`
/* The :host selector styles the brick-viewer itself! */
:host {
display: block;
}
`;
}
Şu anda <brick-viewer>
, oluşturulmuş birThree.js sahnesi gösteriyor:
Ama... boş. Şimdi bir model ekleyelim.
Tuğla yükleyici
Daha önce tanımladığımız src
özelliğini, three.js ile birlikte gönderilen LDrawLoader'a ileteceğiz.
LDraw dosyaları, Tuğla modelini ayrı bina adımlarına ayırabilir. Toplam adım sayısına ve tuğla görünürlüğüne LDrawLoader API'si üzerinden erişilebilir.
Bu özellikleri, yeni _loadModel
yöntemini ve oluşturucudaki yeni satırı kopyalayın:
@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
ne zaman aransın? src özelliği her değiştiğinde çağrılması gerekir. src
mülkünü @property
ile dekore ederek mülkü ışık öğe güncelleme yaşam döngüsüne dahil ettik. Bu dekore edilmiş mülklerden biri Değer değişikliklerinde, özelliklerin yeni ve eski değerlerine erişebilen bir dizi yöntem çağrılır. İlgilendiğimiz yaşam döngüsü yönteminin adı update
. update
yöntemi, az önce değişen özellikler hakkında bilgi içeren bir PropertyValues
bağımsız değişkeni alır. Burası _loadModel
işletmesini aramak için mükemmel bir yer.
update
yöntemini ekleyin:
export class BrickViewer extends LitElement {
update(changedProperties: PropertyValues) {
if (changedProperties.has('src')) {
this._loadModel();
}
super.update(changedProperties);
}
}
<brick-viewer>
öğemiz, artık src
özelliğiyle belirtilen bir tuğla dosyasını görüntüleyebilir.
5. Kısmi Modelleri Görüntüleme
Şimdi mevcut inşaat adımını yapılandırılabilir hale getirelim. <brick-viewer step="5"></brick-viewer>
değerini belirtebilmek ve 5. inşaat adımında tuğla modelinin neye benzediğini görmek istiyoruz. Bunun için step
özelliğini @property
ile süsleyerek gözlemlenen bir mülk yapalım.
step
mülkünü dekore edin:
export class BrickViewer extends LitElement {
@property({type: Number})
step?: number;
}
Şimdi, yalnızca geçerli yapı adımına kadar olan tuğlaları görünür hale getiren yardımcı bir yöntem ekleyeceğiz. Güncelleme yönteminde yardımcıyı, step
özelliği her değiştirildiğinde çalışması için çağırırız.
update
yöntemini güncelleyin ve yeni _updateBricksVisibility
yöntemini ekleyin:
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);
}
}
Tamam, şimdi tarayıcınızın geliştirici araçlarını açın ve <brick-viewer>
öğesini inceleyin. Bu özelliğe step
özelliği ekleyin. Örneğin:
Oluşturulan modele ne olduğunu izleyin. Modelin ne kadarının gösterileceğini kontrol etmek için step
özelliğini kullanabiliriz. step
özelliği "10"
olarak ayarlandığında kod aşağıdaki gibi görünür:
6. Tuğla Sette Gezinme
mwc-simgesi-düğmesi
<brick-viewer>
uygulamamızın son kullanıcısı da kullanıcı arayüzü üzerinden derleme adımlarında gezinebilir. Sonraki adım, önceki adım ve ilk adıma gitmek için düğmeler ekleyelim. Bunu kolaylaştırmak için Materyal Tasarım'ın düğme web bileşenini kullanacağız. @material/mwc-icon-button
zaten içe aktarıldığından <mwc-icon-button></mwc-icon-button>
içinde aktarmaya hazırız. Kullanmak istediğimiz simgeyi şu şekilde simge özelliğiyle belirtebiliriz: <mwc-icon-button icon="thumb_up"></mwc-icon-button>
. Olası tüm simgeleri burada bulabilirsiniz: material.io/resources/icons.
Oluşturma yöntemine birkaç simge düğmesi ekleyelim:
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>
`;
}
}
Sayfamızda Materyal Tasarım'ı kullanmak, web bileşenleri sayesinde bu kadar kolay!
Etkinlik bağlantıları
Bu düğmelerin bir işlevi vardır. "Yanıt" düğmesi yapım adımını 1 olarak sıfırlayacaktır. "Gezinme_before" düğmesi, oluşturma adımını ve "navigation_next" öğesini azaltacaktır. değeri artırmalıdır. lit-element etkinlik bağlamalarıyla bu işlevi eklemeyi kolaylaştırır. Değişmez HTML şablonunuzda öğe özelliği olarak @eventname=${eventHandler}
söz dizimini kullanın. eventHandler
artık bu öğede bir eventname
etkinliği algılandığında çalışacak. Örnek olarak, üç simge düğmemize tıklama etkinliği işleyicileri ekleyelim:
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>
`;
}
}
Düğmeleri şimdi tıklamayı deneyin. İyi iş çıkardınız!
Stiller
Düğmeler çalışıyor ancak iyi görünmüyorlar. Hepsi en altta bir arada toplanır. Sahnede üst üste bindirecek şekilde stilize edelim.
Bu düğmelere stil uygulamak için static styles
özelliğine geri döneriz. Bu stiller, yalnızca bu web bileşenindeki öğelere uygulanacağı anlamına gelir. Bu, web bileşenlerini yazmanın zevklerinden biridir: Seçiciler daha basit olabilir, CSS'nin okunması ve yazılması daha kolay olur. Hoşça kalın BEM!
Stilleri şu şekilde görünecek şekilde güncelleyin:
export class BrickViewer extends LitElement {
static styles = css`
:host {
display: block;
position: relative;
}
#controls {
position: absolute;
bottom: 0;
width: 100%;
display: flex;
}
`;
}
Kamerayı sıfırla düğmesi
<brick-viewer>
uygulamamızın son kullanıcıları, fare kontrollerini kullanarak sahneyi döndürebilir. Düğmeler eklerken kamerayı varsayılan konumuna sıfırlamak için bir düğme ekleyelim. Tıklama etkinliği bağlaması olan başka bir <mwc-icon-button>
işi halleder.
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>
`;
}
}
Daha hızlı gezinme
Bazı tuğla setlerinde çok sayıda basamak vardır. Kullanıcı belirli bir adıma atlamak isteyebilir. Adım numaraları içeren bir kaydırma çubuğu eklemek hızlı gezinmeye yardımcı olabilir. Bunun için <mwc-slider>
öğesini kullanacağız.
mwc-kaydırma çubuğu
Kaydırma çubuğu öğesinde, minimum ve maksimum kaydırma çubuğu değeri gibi birkaç önemli veri parçası gerekir. Minimum kaydırma çubuğu değeri her zaman "1" olabilir. Model yüklenmişse maksimum kaydırma çubuğu değeri this._numConstructionSteps
olmalıdır. <mwc-slider>
adlı çocuğa, özellikleri aracılığıyla bu değerleri söyleyebiliriz. _numConstructionSteps
özelliği tanımlanmamışsa max
özelliğini ayarlamaktan kaçınmak için ifDefined
lit-html yönergesini de kullanabiliriz.
"Geri" ile "geri" arasında bir <mwc-slider>
ekleyin ve "forward" düğmeler:
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>
`;
}
}
Veri "yukarıda"
Kullanıcı kaydırma çubuğunu hareket ettirdiğinde mevcut yapım adımı değişmeli ve modelin görünürlüğü buna uygun şekilde güncellenmelidir. Kaydırma çubuğu sürüklendiğinde, kaydırma çubuğu öğesi bir giriş etkinliği yayınlar. Bu etkinliği yakalamak ve oluşturma adımını değiştirmek için kaydırma çubuğuna bir etkinlik bağlama ekleyin.
Etkinlik bağlamayı ekleyin:
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>
`;
}
}
Harika! Hangi adımın görüntüleneceğini değiştirmek için kaydırma çubuğunu kullanabiliriz.
Veri "aşağı"
Bir şey daha var. "Geri" ve "next" adımları değiştirmek için kaydırma çubuğu tutma yerinin güncellenmesi gerekir. <mwc-slider>
öğesinin değer özelliğini this.step
öğesine bağlayın.
value
bağlamasını ekleyin:
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>
`;
}
}
Kaydırma çubuğu neredeyse bitmek üzere. Diğer kontrollerle güzel görünmesi için esnek bir stil ekleyin:
export class BrickViewer extends LitElement {
static styles = css`
/* ... */
mwc-slider {
flex-grow: 1;
}
`;
}
Ayrıca, kaydırma çubuğu öğesinin kendisinde layout
değerini çağırmamız gerekir. Bu işlemi, DOM ilk kez oluşturulduktan sonra çağrılan firstUpdated
yaşam döngüsü yönteminde gerçekleştireceğiz. query
tasarımcısı, şablondaki kaydırma çubuğu öğesi için referans almamıza yardımcı olabilir.
export class BrickViewer extends LitElement {
@query('mwc-slider')
slider!: Slider|null;
async firstUpdated() {
if (this.slider) {
await this.slider.updateComplete
this.slider.layout();
}
}
}
Bir araya getirilen tüm kaydırma çubuğu eklemeleri (daha iyi görünmesini sağlamak için kaydırma çubuğunda ekstra pin
ve markers
özellikleriyle birlikte) aşağıda verilmiştir:
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>
`;
}
}
İşte son ürün!
7. Sonuç
Kendi HTML öğemizi oluşturmak için lit-Element'in nasıl kullanılacağı konusunda birçok şey öğrendik. Şu konularda bilgi edindik:
- Özel öğe tanımlayın
- Özellik API'si bildirme
- Özel bir öğe için görünüm oluşturma
- Stilleri kapsülle
- Veri aktarmak için etkinlikleri ve özellikleri kullanma
Lit-Element hakkında daha fazla bilgiyi resmi sitesinde bulabilirsiniz.
Tamamlanmış bir tuğla görüntüleyici öğesini stackblitz.com/edit/brick-viewer-complete adresinde görüntüleyebilirsiniz.
tuğla görüntüleyici NPM'de de gönderilir. Kaynağı burada görüntüleyebilirsiniz: GitHub deposu.