canvas画波浪进度球

波浪进度球是一种非常常见的进度展示方式,常用于加载页。

下面我们来学习一下如何画一个波浪进度球

  1. 首先我们分析一下进度球的组成部分有:一个圆,波纹,波纹的填充色,百分比文字
  2. 我们可以根据这几个组成部分来制作动画。

画一个圆

var drawCircle = function(){
    ctx.beginPath();
    ctx.strokeStyle = '#1080d0';
    ctx.arc(r, r, cR+1, 0, 2 * Math.PI);
    ctx.stroke();
}

image
上面代码中的:
ctx.arc() 方法就是画一个圆形路径,括号里面都是参数:坐标轴的位置,半径长度,起始位置弧度,终止位置弧度;
ctx.beginPath() 是用来开始一条路径或者重置一条路径,如果没有这个方法,画路径的时候会用新的笔触样式将之前的路径重画一遍。
而且不管你用moveTo把画笔移动到哪里,只要不beginPath,那你一直都是在画一条路径。如果你画出的图形和你想像的不一样,记得查看是否有合理的beginPath.

我们再来画波浪

var drawSin = function(xOffset, color, waveHeight){
    ctx.save(); // 存储当前画布的样式等
    var points=[];  //用于存放绘制Sin曲线的点
    ctx.beginPath();
    //在整个轴长上取点 , sx = 0; axisLength 是x轴轴长
    for(var x = sX; x < sX + axisLength; x += Math.PI/6){
        //此处坐标(x,y)的取点,依靠公式 “振幅高*sin(x*振幅宽 + 振幅偏移量)”
        var y = Math.sin((sX + x) * waveWidth + xOffset) * 0.8 + 0.1;
        var dY = mH * (1 - nowRange / 100 ); // 因为坐标系起点是左上角,所以y轴0点位置要用1-nowRange的差。
        points.push([x, dY + y * waveHeight]); // 存储点
        ctx.lineTo(x, dY + y * waveHeight);    // 描路径  
    }
    ctx.stroke()
    ctx.restore(); // 恢复上一次ctx.save() 存储的,一定要有save()才有效,无默认。
};

image

主要内容就是三角函数里面的sin,画一个sin的坐标图来实现波纹。知识关联到canvas画图,就是将sin函数坐标点的xy轴坐标记录下来,并且用lineTo() 方法描点。上图是描点之后的样子,接下来我们需要填充颜色,填充颜色相关的方法是fill()。设置fillStyle颜色值。

ctx.fillStyle = color;
ctx.fill();

image

Σ(;゚д゚)

怎么成了这个鬼样子,和想象中不一样啊。对比之前的图片好像是根据y轴0坐标的位置形成的封闭回路做的填充。

所以我们需要规划一个封闭区间,因为我们的目的是要在波浪的下方填充颜色,所以需要将波浪下方的区域都包裹进来。我们要lineTo()到画布的右下角,再左下角,再回到起点。
image

这样我们再填充上颜色就会发现波浪线以下的区域都填充了颜色,但是可以预料到的是,这个颜色同时还覆盖了我们之前画的圆,成了圆上面的一个遮挡,并不像是圆里面的波浪。

将填充的颜色区域限制,让其看起来像在园内。

canvas有个方法叫clip(),对css比较熟悉的同学应会知道这个方法,他的作用就是限制可视范围的。我们可以在画圆的时候,用这个方法,将画布的可视范围限制在圆内, 记得不要在这里使用ctx.restore() ,否则无法做区域限制。

var drawCircle = function(){
    ctx.beginPath();
    ctx.lineWidth  = 1;
    ctx.strokeStyle = '#1080d0';
    ctx.arc(r, r, cR+1, 0, 2 * Math.PI);
    ctx.stroke();
    ctx.beginPath();
    ctx.arc(r, r, cR, 0, 2 * Math.PI);
    ctx.clip();
}

//画sin 曲线函数  xOffset表示x轴方向的偏移距离
var drawSin = function(xOffset, color, waveHeight){
    ctx.save();
    var points=[];  //用于存放绘制Sin曲线的点
    ctx.beginPath();
    //在整个轴长上取点 , sx = 0; axisLength 是x轴轴长
    for(var x = sX; x < sX + axisLength; x += Math.PI/6){
        //此处坐标(x,y)的取点,依靠公式 “振幅高*sin(x*振幅宽 + 振幅偏移量)”
        var y = Math.sin((sX + x) * waveWidth + xOffset) * 0.8 + 0.1;

        var dY = mH * (1 - nowRange / 100 ); // 因为坐标系起点是左上角,所以y轴0点位置要用1-nowRange的差。

        points.push([x, dY + y * waveHeight]); // 存储点
        ctx.lineTo(x, dY + y * waveHeight);    // 描路径  
    }
    //封闭路径
    ctx.lineTo(axisLength, mH); // 右下角的点
    ctx.lineTo(sX, mH);   // 左下角的点
    ctx.lineTo(points[0][0],points[0][1]);  // 波浪起点
    // ctx.stroke()
    ctx.fillStyle = color;
    ctx.fill();

    ctx.restore();
};

image

因为已经填充颜色了,所以ctx.stroke() 方法就不需要了。

让波浪动起来

波浪动起来的原理就是我们的sin曲线动起来,怎么动呢? 比较初级的就是水平平移,所以我们需要重复调用画曲线的方法,然后每次调用都给一个平移的值。这就是上面代码中xOffset的作用。
平移有了,然后就是需要让他自动重复调用这个方法就可以。这个我们可以用setTimeout或者setInterval来实现,但是这两个方法不是动画专用的方法,所以性能方面不是很优,最好使用由浏览器专门为动画提供的API——requestAnimationFrame() 。把上面的画圆画波浪方法放进一个render() 方法封装起来,然后requestAnimationFrame(render) 调用。就可以实现动画的效果了。但是这个时候我们发现上面的曲线虽然运动了,但是看起来很违和,这是因为波浪宽度太小的原因,我们可以修改参数将波浪扩大。也就是修改waveWidth 这个变量的值,waveWidth越小,波纹越宽。我们还可以再添加一个颜色潜一些的波纹,让波浪看起来更加立体。

写文字

var drawText = function(){
    ctx.save();
    var size = 0.4*cR;
    ctx.font = size + 'px Microsoft Yahei';
    ctx.textAlign = 'center';
    ctx.fillStyle = "rgba(06, 85, 128, 0.5)";
    ctx.fillText(~~nowRange + '%', r, r + size / 2); // ~是取反,~~ 的目的是为了保证nowRange是数字
    ctx.restore();
};

image
这就是最终效果了,文中提到的是绘制的主要过程和思路,不是所有代码。————jsdoit

posted @ 2018-08-12 15:46  前端施工队  阅读(4382)  评论(0编辑  收藏  举报