项目中使用水波纹进度条,可以让项目的预加载更加生动,用户等待阶段相对也没那么无聊~~
本项目中使用Canvas模拟水波纹滚动效果来实现,主要原理是数学的正弦曲线。
原理
水波纹外形上酷似正弦曲线,于是通过正弦曲线去分析水波纹的实现原理。
正弦曲线公式为y=Asin(ωx+φ)+k
A为振幅,改变该值,可改变水波纹波浪的深度,A越大,水波纹越陡,A越小,水波纹越平。
φ为出相,改变该值,可改变水波纹水平方向的位置,φ为正数,水波纹向右移动,φ为负数,水波纹向左移动。
k为偏距,改变改值,可改变水波纹垂直方向的位置。
ω为角速度,改变改值,可改变水波纹波浪的宽度,ω越大,水波纹越窄,ω越小,水波纹越宽。
通过正弦曲线的原理,水波纹进度条由下往上移动,可以通过增大k向上移动实现,水波纹的滚动可以通过φ从右至左移动,水波纹的形状由正弦曲线形成。
实现
1.画出波浪线
var wave = { init : function(){ var canvas = document.getElementById('canvas'), winW = document.body.clientWidth; this.ctx = canvas.getContext('2d'); canvas.width = winW * 0.6; canvas.height = winW * 0.6;
this.canvasW = canvas.width;
this.draw() },
draw : function(){
this.drawWave(this.ctx);
},
drawWave : function(ctx){ var canvasW = this.canvasW, startX = 0, waveH = 6, waveW = 0.04, offsetY = canvasW*0.5;
ctx.beginPath(); for(var x = startX ; x < canvasW ; x += 20 / canvasW){ var y = waveH * Math.sin((startX + x) * waveW) + offsetY; points.push([x ,y]); ctx.lineTo(x , y); } ctx.stroke(); } } wave.init()
|
根据想要的效果设定波浪深度waveH
、波浪宽度waveW
。
2.添加波浪流动效果
var wave = { speed : 50 , offsetX : 0 , init : function(){ ... },
draw : function(){
this.ctx.clearRect(0,0,this.canvasW,this.canvasW); this.offsetX += this.speed;
this.drawWave(this.ctx);
requestAnimationFrame(this.draw.bind(this)); },
drawWave : function(ctx){ ... for(var x = startX ; x < canvasW ; x += 20 / canvasW){ var y = waveH * Math.sin((startX + x) * waveW + this.offsetX) + offsetY; ctx.lineTo(x , y); } ctx.stroke(); } } wave.init()
|
通过波浪快速水平滑动,做出波浪流动效果。通过循环修改φ
即offsetX
,移动波浪线。每次循环绘制波浪线前需要清除画板this.ctx.clearRect(0,0,this.canvasW,this.canvasW);
,重新绘制波浪线。
3.雏形
画出水波纹进度条整体雏形,添加色彩~
var wave = { ... isDrawContainer : false, init : function(){ ... },
draw : function(){ ...
if(!this.isDrawContainer){ this.drawContainer(ctx); }
... },
drawContainer : function(ctx){ var pointR = this.canvasW / 2, lineWidth = 10 , circleR = pointR - (lineWidth);
ctx.lineWidth = lineWidth; ctx.beginPath(); ctx.arc(pointR,pointR,circleR,0,2 * Math.PI); ctx.strokeStyle = 'rgba(192,225,242,0.3)'; ctx.stroke(); ctx.clip(); this.isDrawContainer = true; },
drawWave : function(ctx){ ... ctx.lineTo(canvasW , canvasW); ctx.lineTo(startX , canvasW); ctx.lineTo(startPos[0] , startPos[1]); ctx.fillStyle = '#a4def6'; ctx.fill(); } } wave.init()
|
通过`drawContainer`函数画出滚动条容器,并剪切出需要的区域,通过isDrawContainer
判断,优化性能。
4.添加波浪溢满效果
可以通过偏距k
即offsetY
来移动波浪的垂直方向的高度,但由于canvas的y轴方向与正弦曲线的y轴相反,填充的方向相同,故在本例子中,通过修改y轴初始值来实现该效果。
var wave = { offsetYRange : 1.1 , offsetY : 0, ...
draw : function(){ ...
if(this.offsetY < this.offsetYRange){ this.offsetY += 0.003; } ... }, ...
drawWave : function(ctx){ ... for(var x = startX ; x < canvasW ; x += 20 / canvasW){ var y = (1-this.offsetY) * canvasW + waveH * Math.sin((startX + x) * waveW + this.offsetX); ... } ... } } wave.init()
|
5.添加海浪真实感
通过添加多一条颜色深的波浪来实现海浪真实感。
var wave = { ...
draw : function(){ ...
this.drawWave(ctx , this.offsetX , this.offsetY , 0.04 , 6 , '#a4def6'); this.drawWave(ctx , this.offsetX + 2 , this.offsetY - 0.02, 0.04 , 8, '#79d4f9');
... },
...
drawWave : function(ctx , offsetX , offsetY , waveW , waveH , color){ var canvasW = this.canvasW, startX = 0;
ctx.beginPath();
var startPos = [startX]; for(var x = startX ; x < canvasW ; x += 20 / canvasW){ var y = (1 - offsetY) * canvasW + waveH * Math.sin((startX + x) * waveW + offsetX);
... } ... } } wave.init()
|
波浪数据通过多次测试出最佳效果而得出。
6.添加进度数据
var wave = { ... offsetYSpeed : 0.003, progressNum : 0, ...
draw : function(){ ...
if(this.offsetY < this.offsetYRange){ this.offsetY += this.offsetYSpeed;
this.progressNum += 100/(this.offsetYRange/this.offsetYSpeed);
document.querySelector('.proNum').innerHTML = parseInt(this.progressNum) + '%'; }
... },
... } wave.init()
|
最终效果如下:
源码:https://github.com/ZENGzoe/wave-progress