最近几年,虚拟现实VR的概念火了一把,各种VR概念的游戏、设备、视频受到人们的广泛关注。笔者在逛商场的时候也经常会看到有VR设备体验的地方让游人体验一把,各种酷炫的头盔和酷炫的设备着实抓人眼球。但是作为一个前端工作者,我们肯定也希望在我们的网页里也能看到这么酷炫的效果,不经意间在网上发现了一个网页虚拟现实框架A-Frame,分享一下笔者的使用心得。

介绍

  A-Frame是Mozilla发布的一个全新的开源框架,旨在帮助开发者开发在浏览器中运行的高性能响应式的VR体验。只需要在页面中引入aFrame.min.js就能够集成支持VR页面所需要的组件了。

A-Frame

优点

基于DOM

  我们可以使用传统的JavaScript DOM API来操纵A-Frame场景来添加逻辑,行为和功能。同时,A-Frame是基于DOM的,现在一些流行的框架能够基于A-Frame工作,比如React、Vue、jQuery和Angular。

实体组件系统

  A-Frame是一个基于three.js的实体组件系统。在A-Frame里一切都是实体,我们插入组件,可以随意撰写外观,行为和功能集成。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

丰富的生态系统

  A-Frame配备了多个组件,但由于A-Frame在其核心部分是完全可扩展的,社区已经为生态系统填充了许多组件,如物理,粒子系统,音频可视化和Leap Motion控件。这个生态系统是A-Frame的命脉。开发人员可以构建一个组件并发布它,然后其他人可以使用该组件并直接从HTML使用,甚至不必知道任何JavaScript。

e-system

强大的可视化检查器

  可视化编辑器用于检查和编辑A框架场景的可视化工具。与浏览器的DOM检查器类似,您可以进入任何A-Frame场景,本地或Web上,然后点击ctrl+alt+i键盘。
  这将打开视觉检查器,我们可以在其中进行更改。可以在视觉上移动和放置物体,用组件的属性随意的挪动物体,或者围绕相机平移以查看场景的不同视图。

组件

  介绍了这么多,让我们来看一下A-Frame是如何来构造组件的。

a-scene

  一个场景是由a-scene创建的,是全景渲染的根对象,所有的元素都需要放在a-scene这个组件里。它会处理3D所需的所有设置:设置WebGL、画布、相机、灯光、渲染器、渲染循环以及开启及时的WebVR支持。

a-sky

  每一个场景都需要一个背景,a-sky标签用来设置场景的背景,可以直接放置src为全景图片,或者直接渲染color值。

1
2
3
<a-scene>
<a-sky color="#ccc" src="images/panorama.jpg"></a-sky>
</a-scene>

  如果直接渲染了color值,那么整个背景就会变成该颜色;如果设置全景图片,可以左右移动来查看。效果链接戳这里

effect

a-box

  我们通过a-box标签来生成一个长方体,有一下几个重要的属性:

  • width:宽度
  • height:高度
  • depth:深度
  • color:颜色
  • position:位置
  • rotation:旋转
  • scale:缩放
1
2
3
4
5
6
7
8
9
10
11
12
<a-scene>
<a-sky color="#f0f0f0"></a-sky>
<a-box
color="red"
depth="1"
height="1"
width="1"
position="0 2 -5"
rotation="0 45 45"
scale="1 1 1">
</a-box>
</a-scene>

  最后生成一个长1高1深1颜色为红色的长方体:

cub

a-assets

  但是如果仅仅是红色的外观那么就太单调了。A-Frame允许我们给组件设置纹理图片,虽然可以直接给组件设置src属性,不过不推荐这种做法,推荐通过资源管理系统a-assets
  一般在游戏等视觉体验丰富的场景中,由于有着大量的图片、模型、声音等资源,都会对这些资源进行一个预加载处理,确保在渲染的时候不会出现缺失的现象。
  我们把这些资源放到a-assets也是为了进行预加载。我们可以存放以下资源:

  • <a-asset-item>:其他资产,如3D模型和材料
  • <audio>:声音文件
  • <img>:图像纹理
  • <video>:视频纹理

  我们通过给资源标志一个唯一的id,然后在组件的src中引用这个id来进行调用。

1
2
3
4
5
6
7
8
9
<a-box
src="#boxTexture"
depth="1"
height="1"
width="1"
position="0 2 -5"
rotation="0 45 45"
scale="1 1 1">
</a-box>

  这样我们的长方体就变成了一个带有图案纹理的长方体。

cub1

a-light

  我们可以通过使用a-light来改变场景的亮度。默认情况下,如果我们没有指定任何指示灯,A-Frame将添加环境光和定向光。如果A-Frame没有为我们添加灯,场景将是黑色的。一旦我们添加了我们自己的灯,默认的照明设置将被删除,并替换为我们的设置。
  我们还会添加一个点光源,点光源就像灯泡; 我们可以将它们放在场景周围,点光源对实体的影响取决于它与实体的距离。

1
2
3
4
5
6
7
8
< - 红色定向灯从左上方闪烁。 - >
<a-light color="red" position="-1 1 0"></a-light>
< - 蓝点光,5米空中。 - >
<a-light tpye="point" color="blue" position="0 5 0"></a-light>
< - 昏暗环境照明。 - >
<a-light type="ambient" color="yellow"></a-light>

  我们给环境一个黄色照明的光源,最后的效果是这样的。

cub2

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

a-animation

  我们可以使用A-Frame的内置动画系统<a-animation>向盒子添加动画。我们可以将<a-animation>元素作为实体的子代。让我们把盒子上下摆动来给场景添加一些动作。

1
2
3
4
5
6
7
8
9
10
<a-box src="#boxTexture" depth="1" height="1" width="1" position="0 2 -5" rotation="0 45 45" scale="1 1 1">
<!-- 在box里面添加animation元素 -->
<a-animation
attribute="position"
to="0 1 -5"
direction="alternate"
dur="2000"
repeat="indefinite">
</a-animation>
</a-box>

animation

  一些属性说明:

  • attribute:需要把哪个属性作为动画
  • to:属性到某个值
  • direction:方向,alternate表示来回
  • dur:时间间隔
  • repeat:重复次数

a-text

  在A-Frame中还可以添加文本组件<a-text>

1
2
3
4
5
<a-text value="Hello, A-Frame!"
color="#0abef0"
position="-0.9 0.2 -3"
scale="1.5 1.5 1.5">
</a-text>

  最后添加文字的效果,效果链接戳这里

cub3

a-cylinder

  圆筒原型是多功能的,可用于创建不同种类的形状:

1
2
3
4
5
6
7
8
<!-- 基本圆筒。 -- >
<a-cylinder color="crimson" height="3" radius="1.5"></a-cylinder>
<!-- 六角形。 -- >
<a-cylinder color="cyan" segments-radial="8"></a-cylinder>
<!-- 吃豆人。 -- >
<a-cylinder color="yellow" theta-start="50" theta-length="280" side="double"></a-cylinder >
<!-- 绿色管道。 -->
<a-cylinder color="green" open-ended="true"></a-cylinder>

a-cone

  用于创造一个椎体。

1
<a-cone position="0 0 -20" rotation="35 45 30" height="10" radius-top="2" radius-bottom="10" color="#F3BA8D"></a-cone>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

使用JS和DOM

  在A-Frame中也有DOM元素,通过querySelector()和querySelectorAll()方法来提供元素的遍历,查询,查找和选择。这个很像jQuery中的选择器。

querySelector

  如果我们想抓住一个元素,我们使用querySelector()返回那一个元素。比如我们来抓住场景元素:

1
2
var scene = document.querySelector('a-scene');
console.log(scene);

  如果元素具有ID,则可以使用ID选择器(即,#)。我们来抓住一个有一个ID的红色框。之前我们在整个文档上做了一个查询选择器。在这里,我们将在场景范围内做一个查询选择器。使用查询选择器,我们可以将查询的范围限制在任何元素内:

1
2
var scene = document.querySelector('a-scene');
console.log(scene.querySelector('#mybox'));

querySelectorAll

  如果我们要抓取一组元素,我们使用querySelector()哪个返回一个元素数组。我们可以查询元素名称、类名、属性名:

1
2
3
4
5
6
7
8
9
10
// 查询元素名称
console.log(document.querySelectorAll('a-box'));
// 查询类名
console.log(document.querySelectorAll('.mybox'));
// [
// <a-entity light="type:ambient"></a-entity>
// <a-entity light="type:directional"></a-entity>
//]
// 查询属性名
console.log(document.querySelectorAll('[light]'));

  如果我们抓住了一组使用的实体querySelectorAll(),我们可以循环使用它们for。我们围绕场景中的每个元素循环遍历。

1
2
3
4
var els = document.querySelectorAll('a-box');
for(var i = 0; i < els.length; i++){
console.log(els[i]);
}

createElement

  要创建一个实体,我们可以使用document.createElement。这将给我们一个空白的实体:

1
var el = document .createElement('a-entity');

  但是,在将实体附加到我们的场景之前,该实体将不会被初始化或者成为场景的一部分。

appendChild

  要向DOM添加实体,我们可以使用.appendChild(element)。具体来说,我们想把它添加到我们的场景中。我们抓住现场,创建实体,并将实体附加到我们的场景。

1
2
3
var sceneEl = document .querySelector('a-scene');
var entityEl = document .createElement('a-entity');
sceneEl.appendChild(entityEl);

  请注意,这appendChild()方法是浏览器中的异步操作。在实体完成附加到DOM之前,我们不能对实体执行许多操作(如调用.getAttribute())。如果我们需要查询刚被追加的实体上的一个属性,我们可以监听loaded该实体上的事件,或者将逻辑放在A-Frame组件中,以便一旦它被准备好就执行:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

removeChild

  要从DOM中移除实体,因此从场景中删除一个实体,我们removeChild(element)从父元素调用。如果我们有一个实体,我们必须要用它的parent(parentNode)去除实体。

1
entityEl.parentNode.removeChild(entityEl);

setAttribute

  要更新组件,我们可以使用setAttribute()方法。更新组件需要几种形式。如果组件是单属性组件,则setAttribute其行为与通常情况相同:

1
entity.setAttribute('visible',false);

  但是如果是单属性,它可以处理该值的特殊解析。例如,position组件是单属性组件,但其属性类型解析器允许它占用一个对象:

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

  要设置或替换多属性组件的组件数据,我们可以传递注册组件的属性名称,并将属性对象传递为value:

1
2
3
4
5
entity.setAttribute('light', {
type: 'spot',
distance: 30,
intensity: 2.0
});

removeAttribute

  从DOM中删除属性或者分离组件,调用组件的remove生命周期方法。

1
2
entity.removeAttribute('goemetry'); //分离几何组件。
entity.removeAttribute('sound'); //分离声音组件。

本文地址: http://xieyufei.com/2017/04/09/FrontEnd-Panorama.html