时间一晃就过了两个月,我现在写的文章也是10月初到10月中旬这段时期的一点经验。

A-Frame介绍

  • Virtual Reality: Drop in the library and have a WebVR scene within a few lines of markup.

    • 虚拟现实:使用这个js库可以用几行标签语言实现一个webVR情景。

  • Based on the DOM: Manipulate with JavaScript, use with your favorite libraries and frameworks.

    • 基于DOM实现:用js操作DOM实现的方式,可以搭配你需要的库和框架实现更多的功能。

  • Entity-Component-System: Based on an entity-component-system pattern for better composability and extensibility.

    • ECS:用ECS游戏设计模式来构建更强性能拓展性可玩性的WebVR应用。

VR看房的技术分析

  1. 市面上多数现有的VR看房都不是真的VR看房,只是一个带有全景的flash播放器;

  2. 真的VR应该具有沉浸的感受,应该可以搭配具有陀螺仪的设备嵌入cardboard眼镜使用;

  3. 简单的VR看房实际上就是,一个全景图,通过设置在空间中的锚点,触发切换到另一个全景图。

根据上面的分析,我们就可以知道,其实没多难,就是切换一个图片,更换锚点的位置。但是我们一般的web工具不支持全景的构建,只有three.js这款早期用标准的js实现空间的概念。但是three.js还是不足以容易地实现我们想要的东西,因为我们要的只是一个简单的场景切换,如果没有可以有一个东西只要简单配置就可以实现这样就全景感观功能就好了。因此我选择了A-Frame这个工具来实现我这个目标。

view.html

<body id="main-screen" class="">
 <a-scene>
  <a-assets>
    <img id="floor" src="pic/floor.jpg">
    <img id="arrow" src="pic/arrow.png">
    <img id="ru" src="pic/ru.jpg">
  </a-assets>
  <a-sky id="asky-back" src="#ru"></a-sky>
  <a-sky id="asky-front" src="#floor"></a-sky>
 
  <a-curvedimage id="arrow1" src="#arrow" theta-length="15" height=".36" rotation="0 180 0" scale="0.5 0.5 0.5">
        <a-animation attribute="position"  to=".2 0 0" repeat="indefinite" dur="500" direction="alternate"></a-animation>
  </a-curvedimage>
  <!-- Camera with gaze cursor -->
  <a-entity>
      <a-entity camera look-controls wasd-controls>
          <!-- Cursor should only interact with .box class entities -->
          <a-entity cursor id="cursor"
                    geometry="primitive: ring; radiusOuter: 0.015;
                              radiusInner: 0.01; segmentsTheta: 32"
                    material="color: white; shader: flat"
                    position="0 0 -0.75">
          </a-entity>
      </a-entity>
  </a-entity>
</a-scene>
</body>

这是我VR看房的用a-frame简单几个配件:
<a-scene>,<a-assets>,<a-sky>,<a-curvedimage>,<a-entity>

scene顾名思义是场景: 就像游戏一样的一段剧情画面,我们这里只需要用一个情景。
assets则是素材: 像unity3D一样的游戏引擎都有这个概念,用来引入素材,模型,材质。
sky: 这个是a-frame通过封装entity弄出来的东西,直接引用就可以变成球状背景。
curvedimage: 空间中的贴图,指定某个位置,它就会出现在那里,还可以搭配animation实现3D的一些简单动画特效,旋转,位移,翻转,还有各种线性函数动画。

entity: 这是一个抽象概念,基本上整个a-frame里的元素都是基于这个东西封装拓展出来的,我们这里实现了一个凖焦点,还有实现了一个camera(摄像头,用来观察整个情景的入口)。

下面是js代码

<script>
    var skyBack = document.querySelector('#asky-back');
    var skyFront = document.querySelector('#asky-front');
    var arrow = document.querySelector('#arrow1');
    var cur = document.querySelector('#cursor');
    arrow.addEventListener('click', function (evt) {
        var time=300;
        var i = time;
        var j = 0;
        function fadeOut(picTarget)
        {   
            skyBack.setAttribute('src',picTarget);
            skyFront.setAttribute('opacity',(1/time)*i);
            skyBack.setAttribute('opacity',(1/time)*j);
            j++;i--;
            if(i>0) setTimeout(function(){fadeOut(picTarget)},1);
            else setTimeout(skyFront.setAttribute('src',picTarget),time);
        }
        if(skyFront.getAttribute('src')=='#floor'){
            setTimeout(fadeOut('#ru'),1000);
        }else{
            setTimeout(fadeOut('#floor'),1000);
        }    
    });
</script>

skyFront和skyBack是用于切换的场景的,js里面描述的就是利用opacity透明属性,一个逐渐增大,一个逐渐减小再替换,实现一个完整的切换功能。由于a-sky标签不是继承于entity,所以animation效果无法搭配使用,因此选择采用修改基本属性的方式实现。
此外,由于切换场景,需要用到一些camera角度的切换的功能,具体可以查看issue。里面有获取角度变量修改角度变量的方法。

VR全景播放器技术分析

  1. Youtube其实是个指南,里面很多技术他们都率先自己用

  2. 网上也有一些全景播放器,但是不是基于原生web,而是基于flash

  3. 但是实际上web除了播放内容的质量需要对应GPU支持以外,都没什么了,什么陀螺仪接口啊之类的东西chrome的内核早就支持了。所以用A-Frame来做完全可以。

下面我们就一起来走一走这一套技术,主要会遇到的坑有三个,一个是手机端上无法自动播放视频,所以必须加个触发功能。另一个坑就是ios上无法正常地全屏播放。最后一个就是视频的编码问题。

video.html的header部分

<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />

用于适配ios的全屏模式,顺便让chrome和safari变色变得跟播放器颜色一样。

video.html的body部分

<button id="play-button">PLAY</button>
<button id="pause-button" style="display:none">PAUSE</button>
<div id="mask" class="mask opacity"></div>
<a-scene id="vr-scene">
    <a-camera 
        id="camera"
        position="0 1.8 4"
        visible="true"
        scale="2" 
        color="#000000" 
        offset="2" 
        maxdistance="100"
        opacity="0.7">
    </a-camera>
    <a-assets>
        <video id="vid"
        webkit-playsinline
        playsinline
        crossOrigin="anonymous"
        autoplay="false"
        loop="true"
        style="display: none">
        <source type="video/mp4" src="video/test.mp4" -webkit-playsinline=true>
        </video>
    </a-assets>
    <a-entity geometry="primitive: sphere;radius: 5000;segmentsWidth: 64;segmentsHeight: 64;" material="shader: flat; src: #vid;" scale="-1 1 1">
        </a-entity>
</a-scene>    

a-camera就是a-entity的变种,实现摄像机功能。然后这里要重点介绍video里面的属性,playsinline属性可以强行把视频限制在web页面里播放,而不是跳到ios的自带播放器里;然后crossOrigin主要是用来解决视频的url来源跟网站域名不同的问题;autoplay只要去到移动端手记上就会变成flase,据说这是手机的一个省流量人性化设计。有了他们以后就可以建立一个球状场景进行视频的播放。

下面是js部分

<script> var vid = document.getElementById('vid'); var pause = document.getElementById('pause-button'); var play = document.getElementById('play-button'); var mask = document.getElementById('mask'); var u = navigator.userAgent; var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端或者uc浏览器 var isiOS= !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端 if(isAndroid||isiOS){ play.addEventListener("click", function(e){ this.style.opacity = 0; vid.play(); mask.setAttribute('style','display:none'); pause.setAttribute('style',''); }, false); pause.addEventListener("click", function(e){ play.removeAttribute("style"); vid.pause(); mask.setAttribute('style',''); pause.setAttribute('style','display:none'); }, false); }else{ mask.setAttribute('style','display:none'); pause.setAttribute('style','display:none'); play.setAttribute('style','display:none'); } </script> 

js部分主要是做了一个播放按钮的实现,当桌面端的时候不出现,当手机端的时候再弹出来用于触发video标签的play()方法。此外这段代码在ios上也只是兼容ios10以上,ios10以前貌似还是必须自己实现一套,我觉得太麻烦了就没兼容了,目标是通俗易懂嘛。
更多水土不服,可以参考官方issue回答,有几个issue帖子有很多解决方法。issue地址

本DEMO源码下载地址