9

开始

从这里开始准备攻略webgl(准备挖新坑),Flutter框架当然也会继续补充,但是今天学习的不是webgl,而是css3d-engine这个库,因为之前搞活动看到了一个全景旋转活动就是使用这个库完成,颇为惊艳(一开始以为是webgl实现的,但是看了代码才知道用CSS3就可以完成,虽然觉得还是应该用webgl做比较合适),抱着好奇心于是学习一下,嗯,这个库设计相当精简,整个库的代码才800多行,所以代码看下来没啥压力,今天顺着一个例子来分析一下。

全景旋转

首先学习一下基础坐标系:
图片描述
这个只要记住一下x,y,z轴各自方向就可以,下面分析会用到。

接下来就是今天分析的例子,也是来自css3d-engine的例子:
图片描述

一个不停旋转的全景图,当然我们把镜头拉开一点,发现其实它是一个圆柱体不停在旋转:
图片描述

只是我们的镜头刚好在圆柱体的里面,所以就看到全景图不停在旋转了。

再接着分析构建整个场景的代码:

 var s = new C3D.Stage();
 s.size(window.innerWidth, window.innerHeight).material({
    color: "#cccccc"
 }).update();

这里会初始化整个舞台,也会创建默认的摄像机:

initialize: function (params) {
    ...
    this.el.style[prefix + 'Perspective'] = '800px';
    this.el.style[prefix + 'TransformStyle'] = 'flat';
    this.el.style[prefix + 'Transform'] = '';
    this.el.style.overflow = 'hidden';
    
    this.__rfix = new C3D.Sprite();
    this.el.appendChild(this.__rfix.el);
    
    this.__pfix = new C3D.Sprite();
    this.__rfix.el.appendChild(this.__pfix.el);
    
    this.setCamera(new C3D.Camera());
}

Stage初始化的时候,设置默认的perspective是设为800px,而且会创建两个Sprite辅助构建场景(这两个Sprite作用相当的大,场景旋转,拉进拉远都是靠这两个Sprite),最后设置摄像机;当调起update方法然后会顺着调起Stage的updateT方法:

updateT: function () {
            this.fov = fixed0(0.5 / Math.tan((this.camera.fov * 0.5) / 180 * Math.PI) * this.height);
            this.el.style[prefix + 'Perspective'] = this.fov + 'px';
            this.__rfix.position(fixed0(this.width / 2), fixed0(this.height / 2), this.fov).rotation(-this.camera.rotationX, -this.camera.rotationY, -this.camera.rotationZ).updateT();
            this.__pfix.position(-this.camera.x, -this.camera.y, -this.camera.z).updateT();
            return this;
        },

这里可以算是整个Stage计算的核心了,首先是Stage的fov计算,它依赖了Camera的fov,而Camera的fov默认就是75(因为人的有效视角就是75度),接着整个计算其实就是一个已知角度和对边求邻边的公式:
图片描述

这里计算方式其实出自Three.js,github上的讨论
回到Stage刚才初始化的时候,一开始一口气创建三个嵌套的div:

<!--示例,方便分析-->
<div id="stage">
    <div id="__rfix">
        <div id="__pfix"></div>
    </div>
</div>

我们在stage设置好perspective属性,在我的电脑(全屏)上计算出来的是619px,根据刚才的公式,是跟大家的浏览器高度有关,然后设置__rfix元素位置:屏幕居中,重点是Z轴位置的设置,可以看到设置的刚计算出来perspective等于translateZ(619px),所以现在的位置(记住一开始的坐标系,往屏幕外的为正,也就靠近视点):
图片描述

然后设置__pfix的位置,Z轴方向上,取了摄像机相反的方向,因为我们一般理解摄像机拉远拉近都是摄像机在移动,但是整个场景往相反方向移动其实也可以达到相同效果,所以这里就是整个场景移动来到做到的:
图片描述

现在再看,在刚才代码可以看到当camera的x,y,z更新的时候,其实通过位移__pfix来做到的;而camera的rotateX,rotageY,rotateZ更新的时候,则是通过旋转__rfix来做到的。为什么这样的设置,我们刚才看到__rfix把tranlateZ设置到视点上,其实目的是为了让后面的元素可以以视点为原点进行布局,这样我们布局时可以通过控制跟视点的距离进而控制用户视野;而旋转的时候也可以以视点为原点进行旋转,x,y,z移动也是以视点为原点进行,可以想象当镜头拉远200px,再沿x轴旋转45度的场景。

基本舞台的构建已经明白了,继续全景旋转是怎样做出来的:
首先整个场景是由20张129*1170的图片组成一个圆柱体,那么这个圆柱体的半径是多少尼?通过以下计算:

0.5* 129 / Math.tan(360 / 20 / 2 / 180 * Math.PI)

得出407px,所以代码上把整个场景放到-400px也是应该根据这个半径得出来的:

  var pano = this.createPano(bgData, panoRect);
  pano.position(0, 0, -400).updateT();

所以现在整个场景是这样的(可能椭圆更合适一点):
图片描述

总结

这个库还是很不错的库,也学习到一些3D相关的知识,可以考虑怎样融入日常的活动或者页面里面,增加吸引力。


tain335
576 声望196 粉丝

Keep it simple.