近些年,我们经常会在一些网站上看到些炫酷的图片特效,这些效果大部分都是用WEBGL技术实现的, 借助shader我们可以实现css无法做到的炫酷视觉效果 。随着这门技术的兼容性越来越好,普及率越来越高,掌握它必将为你工作中的加分项。还在等什么?一起来学习下吧。
这次我们用three.js来实现一个图像波动特效,剖析其实现原理,最终可以举一反三,变成自己的东西,先来看效果。
实现图片全屏显示
首先我们要将图片等比例撑满窗口,并可以适应浏览器动态缩放。要实现这个效果,我们可以通过在resize事件中动态改变透视相机的视野fov来实现。
我们将图片的高度设置为浏览器窗口的高度,根据三角函数可以很容易计算出需要的视野角度值。
const dist = this.camera.position.z this.camera.fov = 2 * (180 / Math.PI) * Math.atan(this.h / (2 * dist))
图像变形的原理
我们知道three.js中的纹理uv坐标是和模型的顶点坐标相对应的,我们只要改变模型的顶点坐标就能达到扭曲图像的目的。
为了方便观察,我先将平面的贴图去掉,变成纯色的wireframe材质。
然后,我们在shader中对顶点坐标进行下面的变换,看看会发生什么。
vec3 pos = position;pos.z = 0.5 * sin(pos.x * 10.0);
顶点沿z方向进行了正弦变换,我们得到了一个变形后的平面模型。我们去掉网格并把材质还原,这个效果的原理就一目了然了。
效果具体实现
要实现我们的效果,我们需要先将模型通过顶点变换变成漏斗状。
根据当前顶点坐标和中心点坐标的距离计算出z值,距离中心越近z值改变越小,越远则改变越大,最终得到了一个漏斗状的模型。
float dist = length(uv - vec2(0.5)) * 2.0;float maxdist = length(vec2(0.5));float normalizedDist = dist / maxdist;pos.z += normalizedDist;
为了实现动画效果,我们还需要一个变量progress,它是一个0-1之间的值,用来控制变形效果从无到有的过程。
先在shader中加入progress变量,根据progress值控制变形的进度。
float dist = length(uv - vec2(0.5)) * 2.0;float maxdist = length(vec2(0.5));float normalizedDist = dist / maxdist;pos.z += normalizedDist * progress;
鼠标点击后,我们需要将progress值从0变成1。为了看到动画效果,需要将这个值渐进增加,这里用到了greensock的动画库,将动画时间控制在0.6秒,这里的ease属性是用来控制动画的缓动函数,不同的缓动函数会有不同的运动效果,可以根据需要多多尝试。
_onTouchBegan(e) { gsap.to(this.material.uniforms.progress, { value: 1, duration: 0.6, ease: 'expo.easeOut' }) }
到此这个效果的基础部分就介绍完了,具体还有一些实现细节,大家可以参考源码来理解,举一反三,如有问题欢迎给我留言。
github源码
https://github.com/imokya/threejs-image-distort
你“在看”我嘛?