A-Frame.js 學習&文件翻譯(一)實體

weixin_33763244發表於2017-04-29

A-Frame是什麼

A-Frame是Mozilla 開源 web
虛擬現實框架,他能夠非常方便的建立VR視口,載入部分格式的模型,設定照相機等,這為對計算機圖形學不是很瞭解的同學,減輕了好多負擔。我分別用了threeJS和A-Frame.js做了兩個小專案,全英文文件看的好累,就順便翻譯了部分文件,之後會分享threeJS與模型匯出與載入的一些坑。

A-Frame讓你構建場景,僅僅通過HTML ,然而對你使用JavaScript,three.js,和其他的WebAPI沒有限制,A-Frame使用一個採用的是一個實體-元件-系統的模式,使得他更加 的結構化,可延展,並且他不但是開源的,免費的,並且還是一個有著受歡迎的社群和完善工具與元件的生態系統。

Begin Example

clipboard.png

以我的webVR自主裝修館為例,如何構建出圖中模型屋的場景?真的很簡單

<head>
    <title>模板檔案</title>
    <meta name="keywords" content="">
    <meta name="description" content="">
    <!--引入aframe.js  -->   
    <script src="static/js/bundle/aframe.js"></script>
</head>
<!--場景標籤 他代表了整個場景的開始 是全域性根物體,所有的實體(entity)都包含在場景裡--> 
<a-scene>
  <!--<a-assets>使用資源管理系統來快取資源,為了更好的效能,這個標籤內的資源將會預載入快取起來--> 
  <a-assets>
    <!--<a-asset-item>載入各種資源 比如3D模型或者材質  --> 
    <a-asset-item id="floor-obj" src="fox.obj"></a-asset-item>
    <!-- 載入圖片  -->
    <img src="static/img/f1.jpg" id="f1-texture" alt=""> 
    <img src="static/img/sky_sphere.jpg" id='sky-box' alt="">
    <!--載入視訊  --> 
    <video id="video" src="video.mp4"></video>
  </a-assets>
   <!--相機  --> 
  <a-camera fov="80"><a-cursor></a-cursor></a-camera>
  <!--materialchange__floor 元件名,material材質與屬性細節 obj-model模型obj檔案    -->
  <a-entity materialchange__floor material="src: #f1-texture;  metalness: 0.6;repeat:25;" id="floor" obj-model="obj: #floor-obj;"></a-entity>
  <a-video src="#video"></a-video>
  <!--天空盒  --> 
  <a-sky color="#EEEEFF" material="src: #sky-box"></a-sky>
</a-scene>

entity-component-system

A-Frame採用實體-元件-系統模式,使用這個框架的時候,我們把燈光,模型,材質等都當做是一個實體,就是我們呈現在視覺上的主要元素,而component,元件類似vue,封裝可重用的程式碼,為專案新增功能,從而組裝成一個系統

enrity 實體

A-Frame表現一個實體通過<a-entity>元素,作為定義在entity-component-system中的一個部分,這些實體是一個佔位符,讓我們來插入元件,並提供他的外觀,行為和功能;

在A-Frame中,實體們的本質就是附加上位置,旋轉和大小的元件

Example 例子

<a-entity geometry="primitive: box" material="color: red"
          light="type: point; intensity: 2.0" id="mario">
var el = document.querySelector('#mario');

如例所示,我們可以繫結元件給實體,讓他渲染或者做些什麼,我們可以通過幾何(geometry)元件和材質(material)元件定義他形狀與外觀,可以使用燈光元件讓他發出燈光;我們可以通過DOM API輕鬆的得到一個實體,一旦我們拿到了這個實體,我們就能去控制他一些詳細的屬性和方法。

Properties 屬性

components

<a-entity>.components是附加在實體上的一個元件物件,這給我們一個途徑去取得這個實體的元件,包括每一個元件的資料,狀態和方法;

例如,如我我們要去取得threeJS裡的照相機或者材質,我們可以這麼去做:

var camera = document.querySelector('a-entity[camera]').components.camera.camera;
var material = document.querySelector('a-entity[material]').components.material.material;

或者一個元件所暴露的API

document.querySelector('a-entity[sound]').components.sound.pause();

isPlaying

實體是否處於active或者playing狀態,如果我們pause這個實體,那麼isPlaying將處於false狀態

object3D

<a-entity>.object3D is a reference to the entity’s three.js Object3D representation. More specifically, object3D will be a THREE.Group object that may contain different types of THREE.Object3Ds such as cameras, meshes, lights, or sounds:

// Gaining access to the internal three.js scene graph.
var groupObject3D = document.querySelector('a-entity').object3D;
console.log(groupObject3D.parent);
console.log(groupObject3D.children);

我們可以獲得不同型別的Object3Ds通過object3DMap,

object3DMap

一個實體的object3DMap 是一個物件,這個物件給了我們一個途徑去獲取不同型別的THREE.object3Ds(例如,相機,材質,燈光,聲音)
例如一個繫結了幾何和燈光元件的實體,他的object3DMap 應該是下面這個樣子


{
  light: <THREE.Light Object>,
  mesh: <THREE.Mesh Object>
}

我們可以管理實體的THREE.Object3Ds通過getOrCreateObject3D, setObject3D, and removeObject3D.這些方法

sceneEl

一個實體有對其所屬場景元素的引用

var sceneEl = document.querySelector('a-scene');
var entity = sceneEl.querySelector('a-entity');
console.log(entity.sceneEl === sceneEl);  // >> true.

Methods 方法

addState (stateName) 新增狀態

addState將會給這個實體新增一個狀態,這個方法可以觸發stateadded 事件,並且我們可以檢查到是否我們處於這個狀態內

entity.addEventListener('stateadded', function (evt) {
  if (evt.detail.state === 'selected') {
    console.log('Entity now selected!');
  }
});
entity.addState('selected');
entity.is('selected');  // >> true

emit (name, detail, bubbles) 觸發事件

emit觸發一個在實體上定製的DOM事件,例如,我們能夠觸發一個事件去觸發一個動畫。

// <a-entity>
//   <a-animation attribute="rotation" begin="rotate" to="0 360 0"></a-animation>
// </a-entity>
entity.emit('rotate');

我們同樣能夠傳遞事件的細節或者資料通過第二個引數

entity.emit('collide', { target: collidingEntity });

這個事件是預設冒泡的,我們可以讓他不要冒泡通過設定第三個引數為false

entity.emit('sink', null, false);

flushToDOM (recursive) 重新整理DOM

flushToDOM 將會手動序列化一個實體元件的資料並且更新DOM,更多詳細內容見 component-to-DOM serialization.

getAttribute (componentName) 獲取屬性

getAttribute 檢索解析元件的屬性,包含混入(mixin)的和預設的

// <a-entity geometry="primitive: box; width: 3">
entity.getAttribute('geometry');
// >> {primitive: "box", depth: 2, height: 2, translate: "0 0 0", width: 3, ...}
entity.getAttribute('geometry').primitive;
// >> "box"
entity.getAttribute('geometry').height;
// >> 2
entity.getAttribute('position');
// >> {x: 0, y: 0, z: 0}

如果元件名是一個沒有註冊的元件,getAttribute將會正常執行

// <a-entity data-position="0 1 1">
entity.getAttribute('data-position');
// >> "0 1 1"

getDOMAttribute (componentName)

getDOMAttribute只會檢索解析顯式定義在DOM的屬性或者通過setAttribute設定的屬性,如果componentName是已經註冊的元件,getDOMAttribute將只會返回定義在HTML的元件資料,以一個陣列物件的形式,他是不包括混合值和預設值的

與getAttribute的輸出做一個比較:

// <a-entity geometry="primitive: box; width: 3">
entity.getDOMAttribute('geometry');
// >> { primitive: "box", width: 3 }
entity.getDOMAttribute('geometry').primitive;
// >> "box"
entity.getDOMAttribute('geometry').height;
// >> undefined
entity.getDOMAttribute('position');
// >> undefined

getObject3D (type)

getObject3D looks up a child THREE.Object3D referenced by type on object3DMap.

AFRAME.registerComponent('example-mesh', {
  init: function () {
    var el = this.el;
    el.getOrCreateObject3D('mesh', THREE.Mesh);
    el.getObject3D('mesh');  // Returns THREE.Mesh that was just created.
  }
});

getOrCreateObject3D (type, Constructor)

If the entity does not have a THREE.Object3D registered under type, getOrCreateObject3D will register an instantiated THREE.Object3D using the passed Constructor. If the entity does have an THREE.Object3D registered under type, getOrCreateObject3D will act as getObject3D:

AFRAME.registerComponent('example-geometry', {
  update: function () {
    var mesh = this.el.getOrCreateObject3D('mesh', THREE.Mesh);
    mesh.geometry = new THREE.Geometry();
  }
});

pause ()

pause()將會停止任何動畫或者元件定義的動態行為,當我們停止一個實體的時候,它將會停止他的動畫,並且調取這個實體上每個元件的Component.pause(),這個元件決定了發生什麼當停止的時候,常常我們會移去事件監聽,一個實體被pause後,他的子實體也會被pause()

// <a-entity id="spinning-jumping-ball">
entity.pause();

play ()

play()將會開始在動畫或者元件中定義的動態行為,當DOM附加上一個實體的時候,這就好自動呼叫他,當一個實體play(),這個實體的子實體被呼叫play()

entity.pause();
entity.play();

例如,當播放聲音的時候,這個聲音元件會呼叫play()

setAttribute (attr, value, componentAttrValue)

如果屬性不是已經註冊的元件或者是單屬性元件,我們將會這樣來設定屬性

entity.setAttribute('visible', false);

雖然attr是註冊的元件的名字,它可能需要對值做特殊的解析,例如,這個位置元件是一個單屬性元件,但是他的屬性解析器允許他為一個物件

entity.setAttribute('position', { x: 1, y: 2, z: 3 });

設定多屬性元件資料,我們可以將註冊元件的名稱作為attr傳遞,並將屬性物件作為value傳遞:

entity.setAttribute('light', {
  type: 'spot',
  distance: 30,
  intensity: 2.0
});

Updating Multi-Property Component Data 更新多屬性元件資料

為了更新多屬性元件的單個屬性,我們可以將註冊元件的名稱作為attr傳遞,屬性名作為第二個引數,並且將屬性值設定為第三個引數:

// All previous properties for the material component (besides the color)  will be unaffected.
entity.setAttribute('material', 'color', 'crimson');

setObject3D (type, obj)

setObject3D will register the passed obj, a THREE.Object3D, as type under the entity’s object3DMap. A-Frame adds obj as a child of the entity’s root object3D.

AFRAME.registerComponent('example-orthogonal-camera', {
update: function () {

this.el.setObject3D('camera', new THREE.OrthogonalCamera());

}
});

removeAttribute (attr, propertyName)

如果attr是註冊元件的名稱,除了要從DOM中刪除屬性,removeAttribute還將從實體中分離元件,呼叫元件的刪除生命週期方法。

entity.removeAttribute('goemetry');  // Detach the geometry component.
entity.removeAttribute('sound');  // Detach the sound component.

如果給定propertyName,removeAttribute將重置propertyName指定的屬性的屬性值為屬性的預設值:

entity.setAttribute('material', 'color', 'blue');  // The color is blue.
entity.removeAttribute('material', 'color');  // Reset the color to the default value, white.

removeObject3D (type)

removeObject3D removes the object specified by type from the entity’s THREE.Group and thus from the scene. This will update the entity’s object3DMap, setting the value of the type key to null. This is generally called from a component, often within the remove handler:

AFRAME.registerComponent('example-light', {
  update: function () {
    this.el.setObject3D('light', new THREE.Light());
    // Light is now part of the scene.
    // object3DMap.light is now a THREE.Light() object.
  },
  remove: function () {
    this.el.removeObject3D('light');
    // Light is now removed from the scene.
    // object3DMap.light is now null.
  }
});

removeState (stateName)

removeState將從實體中彈出一個狀態。這將會觸發stateremoved事件,我們可以檢查它的刪除狀態。

entity.addEventListener('stateremoved', function (evt) {
  if (evt.detail.state === 'selected') {
    console.log('Entity no longer selected.');
  }
});
entity.addState('selected');
entity.is('selected');  // >> true
entity.removeState('selected');
entity.is('selected');  // >> false

Events 事件

clipboard.png

相關文章