A-Frame使用方法 教程 API
来源:互联网 发布:通话录音软件 免费 编辑:程序博客网 时间:2024/05/29 18:35
不用学习新的语言,只有前端现有的知识就可以做AR了。而且是在我们熟悉的Web环境中,不用APP就可以做到。
1. 什么是A-Frame
这个框架的命名跟移动4G的“和”有得一拼,结果都是完美错过所有关键词。
A 会被浏览器忽略 - 是连词符,frame又有太多歧义。如果打出来 aframe 还会被浏览器“智能”拆分成 a frame 变成“一个框架”。
这就导致查资料不容易,沙里淘金的感觉,不过也可能是本身资料就少的缘故。2017年05月02日更新,孤陋寡闻了,搜索时用引号包含关键词,代表全匹配搜索,搜“A-Frame”可以得到准确的结果。
A-Frame 是一个可以在HTML中创建3d场景的框架,使用Three.js和WebGL来创建VR场景。
不需要了解低层渲染细节,但是A-Frame的说明文档也很长啊。
2. 最简单的demo
在http://codepen.io打开代码,在codepen.io预览
<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script><a-scene> <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere> <a-box position="-1 0.5 -3" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box> <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder> <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane> <a-sky color="#ECECEC"></a-sky></a-scene>
代码中的 <a-sky> 就是纯色背景,其他几个看名字就知道是哪个了。
这个demo还可以用鼠标和键盘导航,如果用手机浏览器打开,就是VR效果了。
3. 从头做一个自己的场景
3.1 模板
A-Frame 的所有元素都放在 <a-scene> 中,初始代码如下:
<!DOCTYPE html><html> <head> <meta charset="utf-8"/> <title>Our First A-Frame Experience</title> <script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script> </head> <body> <a-scene> </a-scene> </body></html>
3.2 天空
天空使用的元素是 <a-sky>,代码如下:
<a-sky color="#C500FF"></a-sky>
此时会产生一个紫红色的天空。天空也可以是一个全景图.
flickr 有很多全景图,我们选一个作为背景,比如这一张:
现在把天空换成这张全景图。
<!DOCTYPE html><html> <head> <meta charset="utf-8"/> <title>Our First A-Frame Experience</title> <script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script> </head> <body> <a-scene> <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky> </a-scene> </body></html>
现在代码(在http://codepen.io打开代码,在codepen.io预览)
<img src="https://pic2.zhimg.com/v2-675986b53abe736a1480887cded7a12d_b.png" data-rawwidth="917" data-rawheight="861" class="origin_image zh-lightbox-thumb" width="917" data-original="https://pic2.zhimg.com/v2-675986b53abe736a1480887cded7a12d_r.png">现在用手机看效果是不是有身临其境的感觉了。
3.3 放一个球进去
<a-scene> <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky> <a-sphere position="0 1.25 -5" radius="1.25" color="#66ffcc"></a-sphere></a-scene>
现在场景中多了一个蓝色的球,直接看效果。
<img src="https://pic2.zhimg.com/v2-597c7018c4bf47e534236264c58bf15d_b.png" data-rawwidth="917" data-rawheight="861" class="origin_image zh-lightbox-thumb" width="917" data-original="https://pic2.zhimg.com/v2-597c7018c4bf47e534236264c58bf15d_r.png">球也可以不是纯色的,这就需要给球表面贴图,我们先从 subtlepatterns 选一个材质,
就这一张木纹吧:
<a-scene> <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky> <a-sphere position="0 1.25 -5" radius="1.25" src="https://www.toptal.com/designers/subtlepatterns/patterns/retina_wood.png"></a-sphere></a-scene>
效果是这样的
<img src="https://pic4.zhimg.com/v2-e0396c0cab7f497d4c1b0aa13bd5b043_b.png" data-rawwidth="917" data-rawheight="861" class="origin_image zh-lightbox-thumb" width="917" data-original="https://pic4.zhimg.com/v2-e0396c0cab7f497d4c1b0aa13bd5b043_r.png">3.4 光标交互
VR里也有对应的交互方案,我们现在增加动画和事件库。场景里增加一个camera和放在其中的curosr。
<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script><script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script><script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script><a-scene> <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky> <a-sphere position="0 1.25 -5" radius="1.25" src="https://www.toptal.com/designers/subtlepatterns/patterns/retina_wood.png"></a-sphere> <!-- Camera + cursor. --> <a-entity camera look-controls> <a-cursor id="cursor" animation__click="property: scale; startEvents: click; from: 0.1 0.1 0.1; to: 1 1 1; dur: 150" animation__fusing="property: fusing; startEvents: fusing; from: 1 1 1; to: 0.1 0.1 0.1; dur: 1500" event-set__1="_event: mouseenter; color: #0092d8" event-set__2="_event: mouseleave; color: black"></a-cursor> </a-entity></a-scene>
现在代码(在codepen.io查看代码,在codepen.io预览)。
<img src="https://pic1.zhimg.com/v2-3ed69cdc1d21d91dfec24a5d3e35afb8_b.png" data-rawwidth="917" data-rawheight="861" class="origin_image zh-lightbox-thumb" width="917" data-original="https://pic1.zhimg.com/v2-3ed69cdc1d21d91dfec24a5d3e35afb8_r.png">现在随着视口(摄像机)移动,在屏幕中央的光标(定位相对于摄像机固定)会跟着摄像机移动。
光标与鼠标一样遇到圆球,触发 mouseenter 事件,离开圆球触发 mouseleave 事件。现在我们已经增加了这两个事件,进入的时候光标变蓝色,离开变回默认的黑色。点击时伴随有光标缩放效果。
3.5 与场景中的球互动
就像鼠标点击按钮一样,这个光标也可以触发场景中的元素事件,不过这时就没有现成的代码库了,要自己写事件绑定。
<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script><script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script><script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script><script> AFRAME.registerComponent('hide-on-click', { dependencies: ['raycaster'], schema: { target:{type: 'selector'} }, init: function () { var data = this.data; var el = this.el; el.addEventListener('click', function () { el.setAttribute('visible', false); data.target.setAttribute('visible', true); }); } });</script><a-scene> <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky> <a-sphere hide-on-click="target:#another_cube" position="0 1.25 -5" radius="1.25" src="https://www.toptal.com/designers/subtlepatterns/patterns/retina_wood.png"></a-sphere> <a-box id="another_cube" visible="false" position="-1 1.5 -4" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box> <!-- Camera + cursor. --> <a-entity camera look-controls> <a-cursor id="cursor" animation__click="property: scale; startEvents: click; from: 0.1 0.1 0.1; to: 1 1 1; dur: 150" animation__fusing="property: fusing; startEvents: fusing; from: 1 1 1; to: 0.1 0.1 0.1; dur: 1500" event-set__1="_event: mouseenter; color: #0092d8" event-set__2="_event: mouseleave; color: black"></a-cursor> </a-entity></a-scene>
现在代码(在codepen.io打开代码,在codepen.io预览)。
<img src="https://pic2.zhimg.com/v2-ae7825f448c1ea01fea7d936c93981b5_b.png" data-rawwidth="917" data-rawheight="861" class="origin_image zh-lightbox-thumb" width="917" data-original="https://pic2.zhimg.com/v2-ae7825f448c1ea01fea7d936c93981b5_r.png">现在如果用PC浏览器看效果,鼠标左右拖动能移动摄像机(同时移动光标),鼠标单击任意位置,光标触发点击事件。点击圆球以后,圆球消失,正方体的盒子出现了。
4 做一个互动游戏
我的游戏过程是,进入页面在随机位置生成一个鸡蛋,用户用光标点击鸡蛋,鸡蛋隐藏,然后播放鸡蛋破壳动画,最后触发弹窗,提示用户你获得一只鸡。
4.1 基础代码
跟上面的差不多,只是去掉了绑定事件和圆球。因为球形太圆了,不像鸡蛋。只能用一张静态的鸡蛋图片代替。为什么不用椭圆形的3d元素呢,因为除了方形、圆柱、圆形、平面以外的物体,多边形太多了,占用内存太大。
要展示平面图形就需要另外一个库 aframe-html-shader。
<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script><script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script><script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script><script src="https://npmcdn.com/aframe-layout-component@3.0.1"></script><script src="https://rawgit.com/mayognaise/aframe-html-shader/master/dist/aframe-html-shader.min.js"></script><script> AFRAME.registerComponent('hide-on-click', { dependencies: ['raycaster'], schema: { target:{type: 'selector'} }, init: function () { var data = this.data; var el = this.el; el.addEventListener('click', function () { el.setAttribute('visible', false); data.target.setAttribute('visible', true); }); } });</script><div id="textToDisplay"><img src="./frame_1.png" alt=""></div><a-scene> <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky> <a-entity hide-on-click="target:#chickenAnimate"> <a-entity id="default_egg" geometry="primitive:plane;width:2;height:2;" position="-3 2 -10" material="shader:html;target:#textToDisplay;transparent:true;fps:0;"> </a-entity> </a-entity> <a-box id="chickenAnimate" visible="false" position="-1 1.5 -4" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box> <!-- Camera + cursor. --> <a-entity camera look-controls> <a-cursor id="cursor" animation__click="property: scale; startEvents: click; from: 0.1 0.1 0.1; to: 1 1 1; dur: 150" animation__fusing="property: fusing; startEvents: fusing; from: 1 1 1; to: 0.1 0.1 0.1; dur: 1500" event-set__1="_event: mouseenter; color: #0092d8" event-set__2="_event: mouseleave; color: black"></a-cursor> </a-entity></a-scene>
上面的代码改动增加了一个div#textToDisplay 里面只有一张鸡蛋的图片,这张图会用来渲染到 a-entity#default_egg。语法是设置 matrial 的值。具体可参考
github aframe-html-shader 项目主页。
这里有个问题,aframe-html-shader 原理是用canvas渲染DOM中的元素,如果有图片会需要由canvas处理,这就涉及到跨域问题。
跨域问题可以通过html2canvas-php-proxy解决,
但是在服务器部署这个代理有安全风险,所以我们的demo放到同一个域测试。
演示地址在: demo7
<img src="https://pic4.zhimg.com/v2-62211a02d2ef21457e958ea662c3a33f_b.png" data-rawwidth="917" data-rawheight="861" class="origin_image zh-lightbox-thumb" width="917" data-original="https://pic4.zhimg.com/v2-62211a02d2ef21457e958ea662c3a33f_r.png">此时用光标点击鸡蛋,鸡蛋会消失,正方体出现。
4.2 播放动画
鸡蛋的动画也需要用canvas渲染出来,这里再引入一个库:
aframe-gif-shader github 地址
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script><script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script><script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script><script src="https://npmcdn.com/aframe-layout-component@3.0.1"></script><script src="https://rawgit.com/mayognaise/aframe-html-shader/master/dist/aframe-html-shader.min.js"></script><script src="./aframe-gif-shader.js"></script>
然后把替代的正方体盒子换成孵蛋动画:
<a-entity id="chickenAnimate" visible="false"> <a-entity id="animate_egg" geometry="primitive:plane;width:2;height:2;" position="-3 2 -10" material="shader:gif;src:url(./egg_2017_1.gif);transparent:true;" gif=""></a-entity></a-entity>
此处的gif渲染原理是用js加载并解析每一帧动画,并用canvas渲染出来。
这里有两个小坑:
aframe-gif-shader 组件中渲染每一帧之前,忘记清空画布,导致影响重叠。(难怪demo使用的都是不透明gif图片)我已经修复了这个问题,所以引用的是本地文件。
aframe-gif-shader 组件中使用了第三方的优化算法,但是这个优化算法存在bug,连续的两帧图片上部透明部分太多会被认为是多余帧优化掉。此处可以修改gif素材绕过。
脚本也增加一点内容,播放孵蛋的动画后,等3秒弹出alert,提醒用户游戏结果,然后重置游戏。
AFRAME.registerComponent('hide-on-click', { dependencies: ['raycaster'], schema: { target: {type: 'selector'} }, init: function () { var data = this.data; var el = this.el; el.addEventListener('click', function () { el.setAttribute('visible', false); data.target.setAttribute('visible', true); setTimeout(function(){ alert('恭喜您获得一只鸡!'); window.location.reload(); },5000); }); }});
完整的效果和源码请查看: demo8
<img src="https://pic1.zhimg.com/v2-f0a2f7243e75cf1e34d36bea59deaec8_b.png" data-rawwidth="917" data-rawheight="861" class="origin_image zh-lightbox-thumb" width="917" data-original="https://pic1.zhimg.com/v2-f0a2f7243e75cf1e34d36bea59deaec8_r.png">4.3 增加趣味性,随机位置
现在游戏可以玩了,但是每次鸡蛋都处在统一位置,根本不用找。为了增加趣味性,我们随机改变鸡蛋的位置。
但是根据我的测试,完全随机的游戏一点都不好玩,太远了会变成几个像素大小,根本找不到,太近了有种糊在脸上的感觉。还有上下左右位置都不能太离谱。
多次测试后决定把位置放在用户站的位置周围固定距离的圆圈上,高度比身高多一点点的时候,比较合理。
我翻出来初中数学课本,找到了下面的公式:
var R = Math.random() * 360; // 弧度var r = 8; // 半径var X = Math.sin(R) * r; // x 坐标var Z = Math.cos(R) * r; // 俯视图 z 坐标$('#default_egg,#animate_egg').attr('position',{ x: X, y: 2, z: Z });
这下位置随机了,可是鸡蛋是平面图形,朝向必须面对摄像头才行,不然会有一种挨着窗户坐在教室第一排看黑板的感觉。
这时就需要另外一个库,从官方插件库中分出来的 kframe 插件库,其中有个 look-at 插件,看名字就知道作用就是让元素面对镜头。加上后的js引用有这么多:
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script><script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script><script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script><script src="https://npmcdn.com/aframe-layout-component@3.0.1"></script><script src="https://rawgit.com/mayognaise/aframe-html-shader/master/dist/aframe-html-shader.min.js"></script><script src="./aframe-gif-shader.js"></script><script src="./aframe-look-at-component.min.js"></script>
这里使用本地版本是没找到 cdn 地址的。
元素上也加上属性绑定:
<a-entity hide-on-click="target:#chickenAnimate"> <a-entity id="default_egg" geometry="primitive:plane;width:2;height:2;" look-at="[camera]" position="-3 2 -10" material="shader:html;target:#textToDisplay;transparent:true;fps:0;"> </a-entity></a-entity><a-entity id="chickenAnimate" visible="false"> <a-entity id="animate_egg" geometry="primitive:plane;width:2;height:2;" look-at="[camera]" position="-3 2 -10" material="shader:gif;src:url(./egg_2017_1.gif);transparent:true;" gif=""></a-entity></a-entity>
此时已经可以完整的游戏了,源代码和演示都在: demo9
<img src="https://pic2.zhimg.com/v2-d4731afd8ab9c03359d6c736ee066479_b.png" data-rawwidth="917" data-rawheight="861" class="origin_image zh-lightbox-thumb" width="917" data-original="https://pic2.zhimg.com/v2-d4731afd8ab9c03359d6c736ee066479_r.png">4.4 再增加点趣味性,AR?
现在浏览器的运算能力还做不到通过摄像头识别复杂的内容,最多只能识别有限的几个二维码。但是我们也可以退而求其次,先把场景放到用户周围再说。
我们调用摄像头,作为网页的背景就能模拟显示场景了。
iOS 系统没有开放摄像头的权限,只能通过android手机测试了。
我们再引用一个 threex.webar库中的一个threex.webcamgrabbing组件。
etUserMedia() no longer works on insecure origins.
To use this feature, you should consider switching your application to a secure origin,
such as HTTPS. See https://goo.gl/rStTGz for more details.悲剧啊,过完年 MediaStreamTrack.getSources 接口被Chrome抛弃了,微信X5也停止支持了,但是新的接口navigator.mediaDevices.getUserMedia旧手机不支持。
所以我们使用 webrtc adapter 兼容新版本的获取资源方法。自己实现获取摄像头内容。
navigator.mediaDevices.getUserMedia 接口只能在 HTTPS 地址中被调用。
就是游戏的最终版本: demo10
<img src="https://pic4.zhimg.com/v2-aaeff2e1c9f05ed68b8bed0e24884e7f_b.png" data-rawwidth="2513" data-rawheight="1921" class="origin_image zh-lightbox-thumb" width="2513" data-original="https://pic4.zhimg.com/v2-aaeff2e1c9f05ed68b8bed0e24884e7f_r.png">微信里要点最下面的,访问原网页。
最终版做了判断,支持获取摄像头的设备就使用当前环境做背景,不支持的就在小树林里找蛋。
到这里这个游戏就做出来了,如果想要做成产品给用户玩,还需要做很多美化工作。至少要有加载游戏前的Loading页面,游戏界面的装饰效果,提示浮层美化,游戏后台接口记录成绩,给用户发奖品等工作。
<img src="https://pic1.zhimg.com/v2-d50e5a063dc98a98d0ee4c02199d7198_b.png" data-rawwidth="611" data-rawheight="901" class="origin_image zh-lightbox-thumb" width="611" data-original="https://pic1.zhimg.com/v2-d50e5a063dc98a98d0ee4c02199d7198_r.png"><img src="https://pic3.zhimg.com/v2-6fed3bbff3b2d53d36d4426d6c95570a_b.png" data-rawwidth="707" data-rawheight="579" class="origin_image zh-lightbox-thumb" width="707" data-original="https://pic3.zhimg.com/v2-6fed3bbff3b2d53d36d4426d6c95570a_r.png">参考资料
A-Frame 教程
A-frame 文档
webrtc demo
远程console
- A-Frame使用方法 教程 API
- tf教程(四):Adding a frame
- Frame-By-Frame Animations的使用方法
- rest frame work 教程
- Delphi中Frame的使用方法
- frameset和frame的使用方法
- frameset和frame的使用方法
- frameset和frame的使用方法
- ios bounds ,frame,center 使用方法
- Delphi中Frame的使用方法
- Adding a frame (C++)
- Animations之Frame-By-Frame Animations的使用方法
- 帧动画Frame-by-Frame Animation的使用方法
- Tkinter教程之Frame篇
- '''Tkinter教程之Frame篇'''
- Tkinter教程之Frame篇
- 截图(Grabbing a Poster Frame)
- A-frame vr的初学
- 安卓 短视频推送 与小屏播放
- 如何求一棵二叉树的深度
- nifi探索之写入数据库
- Fast Image Processing with Fully-Convolutional Networks
- 热更新 深度解析
- A-Frame使用方法 教程 API
- promise模拟获取100个月饼
- Netty之writeAndFlush()流程
- Java POI 导出EXCEL 实现 Java导出Excel
- 2017便利蜂前端一面
- 【学习摘记】马士兵JDBC技术_课时10_JDBC处理PreparedStatement
- python里使用await关键字来等待Future对象完成
- 华为上机笔试题 计算字符个数
- POJ2342 Anniversary party(树形dp)