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