A-Frame是Mozilla 開源 web
虛擬現實框架,他可以很是方便的建立VR視口,載入部分格式的模型,設置照相機等,這爲對計算機圖形學不是很瞭解的同窗,減輕了好多負擔。我分別用了threeJS和A-Frame.js作了兩個小項目,全英文文檔看的好累,就順便翻譯了部分文檔,以後會分享threeJS與模型導出與加載的一些坑。html
A-Frame讓你構建場景,僅僅經過HTML ,然而對你使用JavaScript,three.js,和其餘的WebAPI沒有限制,A-Frame使用一個採用的是一個實體-組件-系統的模式,使得他更加 的結構化,可延展,而且他不可是開源的,免費的,而且仍是一個有着受歡迎的社區和完善工具與組件的生態系統。vue
以個人webVR自主裝修館爲例,如何構建出圖中模型屋的場景?真的很簡單web
<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>
A-Frame採用實體-組件-系統模式,使用這個框架的時候,咱們把燈光,模型,材質等都當作是一個實體,就是咱們呈如今視覺上的主要元素,而component,組件相似vue,封裝可重用的代碼,爲項目添加功能,從而組裝成一個系統json
A-Frame表現一個實體經過<a-entity>元素,做爲定義在entity-component-system中的一個部分,這些實體是一個佔位符,讓咱們來插入組件,並提供他的外觀,行爲和功能;數組
在A-Frame中,實體們的本質就是附加上位置,旋轉和大小的組件緩存
<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輕鬆的獲得一個實體,一旦咱們拿到了這個實體,咱們就能去控制他一些詳細的屬性和方法。框架
<a-entity>.components是附加在實體上的一個組件對象,這給咱們一個途徑去取得這個實體的組件,包括每個組件的數據,狀態和方法;dom
例如,如我咱們要去取得threeJS裏的照相機或者材質,咱們能夠這麼去作:ide
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();
實體是否處於active或者playing狀態,若是咱們pause這個實體,那麼isPlaying
將處於false
狀態
<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 是一個對象,這個對象給了咱們一個途徑去獲取不一樣類型的THREE.object3Ds(例如,相機,材質,燈光,聲音)
例如一個綁定了幾何和燈光組件的實體,他的object3DMap 應該是下面這個樣子
{ light: <THREE.Light Object>, mesh: <THREE.Mesh Object> }
咱們能夠管理實體的THREE.Object3Ds經過getOrCreateObject3D, setObject3D, and removeObject3D.這些方法
一個實體有對其所屬場景元素的引用
var sceneEl = document.querySelector('a-scene'); var entity = sceneEl.querySelector('a-entity'); console.log(entity.sceneEl === sceneEl); // >> true.
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觸發一個在實體上定製的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 將會手動序列化一個實體組件的數據而且更新DOM,更多詳細內容見 component-to-DOM serialization.
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只會檢索解析顯式定義在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 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. } });
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()將會中止任何動畫或者組件定義的動態行爲,當咱們中止一個實體的時候,它將會中止他的動畫,而且調取這個實體上每一個組件的Component.pause(),這個組件決定了發生什麼當中止的時候,經常咱們會移去事件監聽,一個實體被pause後,他的子實體也會被pause()
// <a-entity id="spinning-jumping-ball"> entity.pause();
play()將會開始在動畫或者組件中定義的動態行爲,當DOM附加上一個實體的時候,這就好自動調用他,當一個實體play(),這個實體的子實體被調用play()
entity.pause(); entity.play();
若是屬性不是已經註冊的組件或者是單屬性組件,咱們將會這樣來設置屬性
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 });
爲了更新多屬性組件的單個屬性,咱們能夠將註冊組件的名稱做爲attr傳遞,屬性名做爲第二個參數,而且將屬性值設置爲第三個參數:
// All previous properties for the material component (besides the color) will be unaffected. entity.setAttribute('material', 'color', 'crimson');
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());
}
});
若是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 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將從實體中彈出一個狀態。這將會觸發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