在UP2017预热站中,页面使用了粒子效果,现就前端方面的实现做一个小结。
线上地址:
在UP 2017预热站中,页面使用了粒子效果,点击 UP2017预热站 先看一下效果
30秒快问快答
Q:这个H5用到了什么技术?背景是视频吗?
A:首先,这个其实是个PC站点,最初也没有计划在手机上浏览。背景不是视频,是WebGL,实现上用的是包装好的three.js库
Q:模型是怎么做的?
A:和普通的3D建模一样,常规的3D建模软件即可;然后使用插件导出为three.js可方便读取的JSON格式
Q:花了多久时间?
A:这个项目设计和前端是从2月底开始的,然后4月初页面基本完成。期间包括了前期的技术预研、可行性评估等;和我平时遇到的项目不同的是,这次的设计和前端两者是同步进行的。
流程总览
今年UP2017主题为 奇点·共生,如果使用粒子效果来营造效果,会非常契合主题!于是就有了下文。
我花了10天左右的时间,弄出来一个初始的Demo,实现了粒子的展现、变换。和大家讨论了下,觉得效果不错,于是便继续沿着这个方向下去。
设计和前端同步进行:3D建模然后转换格式,再导入页面,并使用JS控制动画、交互。
因为涉及的点会比较多,所以我决定从各个小的板块来分开说明,可能会比较散。对于有兴趣继续了解的前端开发er,建议先跑一遍three.js的quick start,网上搜一下应该会比较多的。
模型
使用常规的3D建模软件即可。下面的左图是线框视图,右图是页面效果。
模型的要求,具体到这次的项目来说,需要顶点均匀分布;如果需要一些图案,是通过点的密集程度来体现的。
然后给到我的是3D模型,给到我再处理。我这次使用的是Blender,尝试过几次,兼容性比较好的是fbx格式,然后通过给Blender安装插件之后,将模型转换为便于three.js读取的JSON格式。
导入过程参考文章:Importing a Modeled Mesh From Blender to Three.js
由于这次只需要顶点信息,参考下图的参数设置导出即可。
然后得到模型的JSON格式的数据,在页面中加载并读取它,这里使用的加载器是THREE.JSONLoader
如果只希望显示顶点,比如这次的项目,用THREE.js建立3D物体的时候它有专门的粒子系统:Points,以及与之对应的材质PointsMaterial
动画
页面动画主要有切换动画、KV的动画以及二维码的动画,下面会分别说明。THREE.js负责将图形显示出来,粒子间过渡时候的缓动动画使用的是tween.js来控制。
切换动画
只是把模型显示出来还是相对简单的,使用THREE.Points
载入顶点几何体和PointsMaterial
材质即可,主要难点是切换的动画。因为各个模型的顶点数是不同的,如果要切换,不能一一映射。
每天想着这个问题,某天突然就灵光一现了,我可以先读取所有的模型的顶点信息,然后得到所有的模型中顶点数最多的一个的顶点数量,新建一个拥有这么多个顶点的Points作为全场的动画承载体,其余的模型只是用来记录顶点位置,不直接参与表演。
这样一来,模型的切换动画就简单多了。你可能要问,多余的点怎么办,飞到屏幕外吗?其实不用,多余的点从头开始就好了。
比如说承载体有10000个粒子,对于一个有9000个顶点的模型,多余的1000个顶点继续从头开始。9001到10000间的点和1到9000间的点重叠在一起即可。
最初的时候发现动画有些生硬,但是一直想不通为什么,后来突然明白了,因为所有粒子“齐步走”,于是改进了下,利用了tween.js的delay方法:
tween.delay(animationDuration*Math.random());
齐步走的粒子们 vs 随机延迟起步的粒子们:
加上了随机的延迟之后的效果,自然多了。
最后一页的KV动画
KV的动画是裙边的效果,具体的表现就是,它有一个中心,离这个中心越远的地方,振幅越大。然后,又是周期平滑运动,正弦函数就是这种情况使用的啦,组装了一下,如下图的公式。
实际使用的时候,可能要加一些系数微调一下,比如用sine里面的部分控制周期、外面乘以的系数可以控制振幅。
二维码动画
另外页面有一个不太起眼的小角落,右下角藏着一个二维码哟。
这个二维码其实也是一个三维的模型,切换方面的原理和之前的几何体一样,唯一的差别是材质不同。几何体是圆形的点,二维码的材质是方块形,材质的map
设置为null
即可是方形啦(没有纹理默认方形)
new THREE.PointsMaterial({ ... map: null, //texture ... });
氛围
氛围是什么?其实就是后期处理(postprcess),一图胜千言:
左图是处理前的,相对比较生硬。右图主要使用了fog和composer (三方插件)
fog好理解,和现实中的雾/霾类似,设定一个距离,在这个范围内可见,超过之后就慢慢消失在“浓雾”里了
scene.fog = new THREE.FogExp2(0x05050c, 0.0005); renderer.setClearColor(scene.fog.color);
三方的composer很多,这里用的是alteredq的一系列EffectComposer,包括过亮效果、暗角、电视效果等。
还有就是页面上一些悬浮的小颗粒,这个是用了3个粒子系统,然后错落着运动实现。
优化
切换的时候有点卡,后来发现是初始化tween实例造成的(怎么发现的?因为我发现不初始化tween就不卡了),所以这里就把tween示例放到了每一个顶点对象上,有则复用无则初始。切换过程流畅度大大提升了。
if(!vtx.tweenvtx){ vtx.tweenvtx=new TWEEN.Tween(ele) ... };
剩下的优化点主要就是移动端了。
移动端的体验不是很好的,不过尽量优化下。首先想到的是粒子数减少一些,使用了SimplifyModifier这个修改器(Modifier),画面是有部分损失的,但是流畅度提升了不少,但是在安卓上有个问题,它的计算过程特别慢,具体算法不知,可能是不支持部分特性导致的吧。然后就没有采用这个方法了。
既然不能减,那至少不增加。在最后一个KV上,其实是使用了一个TessellateModifier的,Tessellation是细分曲面,常玩单机游戏的同学可能在画质设定里面看到过这个单词。也就是在顶点与顶点之间自动嵌入新的顶点。画面对比,右侧是动态增加顶点的效果:
最后,分辨率,移动端本来按照习惯是要根据屏幕的devicePixelRatio来设定分辨率的
renderer.setPixelRatio(window.devicePixelRatio);
但是为了流畅度,这里舍弃了
上线
跨域的问题
原本计划周一上线,但是周五快下班的时候,遇到这个问题,很焦急。搜了一圈,在TextureLoader里面找到个选项,不为null即可,是的,即使是空字符串也OK
var loader = new THREE.TextureLoader(); loader.crossOrigin='';
流畅度方面,如万技师大神所言,使用vertex shader性能可能更好,感觉好高深,有时间可以继续研究下。