canvas图形绘制之星空、噪点与烟雾效果

发表于2017-07-30
评论0 1.5k浏览

本文使用canvas图形绘制星空、噪点与烟雾效果,是canvas领域基本入门的一些效果。代码已经专门重新梳理了下,必要注释也都加上去了,希望能方便大家的学习。

二、canvas图形效果之旋转星空

图是死的,效果是活的,IE9 浏览器下会看到地球上方会有很多星星在慢慢地绕着地球转啊转,星星在闪啊闪。

像这类密集型canvas效果,一般离不开下面这几个关键字:实例,随机,变化与重绘,requestAnimationFrame。

原理就是:

  1. 先画一个位置透明度随机的静态的星星实例对象;
  2. 有一个可以改变星星位置和透明度的draw方法;
  3. 定时器跑起来,画布不停地清除与绘制,动画效果完成!

原理很简单。

本例子实现的2个难点在于:

  1. 月明星稀
    星星垂直方向实际上是个伪随机,越靠近地球,星星越密集,而越往上,越稀疏。其算法如下:

    var getMinRandom = function() {
        var rand = Math.random(); // step的大小决定了星星靠近地球的聚拢程度,
        // step = Math.ceil(2 / (1 - rand))就聚拢很明显
        var step = Math.ceil(1 / (1 - rand)); var arr = [];
        for (var i=0; i
    

    很大概率会返回一个数值偏小的值,于是,就可以有“月明星稀”的分布效果了。

  2. 圆弧轨迹
    其实很简单,我们套用高中时候学的圆方程式就可以了,如下注释截图所述:

    这下题目就简单了,已知a,b, 求y相对于x的函数表达式……

三、canvas图形效果之雪花噪点效果

图是死的,效果这里也是死的,但并不妨碍我们零距离围观,由于这里是静态的,所以但从这一点来看,似乎比上面星空简单。但是,如果仅仅看绘制一帧,那这里的噪点要比上面的星空要困难些,最大的难点在于对性能的把控。

这么说吧,上面的星空,总共最多就400个点(白色的星星),但是,这里的噪点,例如,demo中画布大小(那我的机子举例)是1920*500,其中,噪点大小是1像素*1像素,总共就有960000个绘制点,显然跟400个点完全不是一个数量级的,如果我们真的一个一个绘制下来,肯定,就连Chrome这么牛步的浏览器也会感觉到明显的卡顿,如何优化如何绘制呢?

这就是本例子实现的难点:

  1. 数量与性能
    我这里是这么处理的,虽然最终的噪点大小是1920*500,但是,我们实际上是由N块300*150的小的像瓷砖一样的小方块拼起来的。话句话说,我实际只绘制了45000个点,比960000显然要小了20倍还不止。

    这样,既满足了效果,又保证了性能。

具体实现原理为:

  1. 创建一个canvas,绘制一个300*150随机噪点图形;
  2. 把这里具有噪点的canvas以画布形式在绘制到页面上的大canvas上;

说得canvas绘图,不得不提一下非常常用的一个drawImage()方法,语法如下:

context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);

各个参数示意为(网上的描述都是直译,很生涩,我这里重新陈述了下):

参数描述
img用来被绘制的图像、画布或视频。
sx可选。img被绘制区域的起始左上x坐标。
sy可选。img被绘制区域的起始左上y坐标。
swidth可选。img被绘制区域的宽度。
sheight可选。img被绘制区域的高度。
x画布上放置img的起始x坐标。
y画布上放置img的起始y坐标。
width可选。画布上放置img提供的宽度。(伸展或缩小图像)
height可选。画布上放置img提供的高度。(伸展或缩小图像)

本例的小的噪点区块就是通过drawImage()方法被平铺到大的canvas元素上的。

四、canvas图形效果之烟雾缭绕效果

图是死的,效果是活的,IE9 浏览器下本例子,效果看上去要更酷一些,实际上,从技术层面讲,跟上面的星空旋转效果几乎如出一辙,可能还要比星空更简单一些,因为其运动轨迹直来直往,不需要转圈圈。

那为何看上去更酷呢,主要在于感觉烟雾很难去模拟。

没错,烟雾确实很难用代码直接绘制出来,实际上,这里的烟雾,是一个png图片,是使用画笔在PS里绘制导出来的。

旋转星空的例子,我们是使用canvas的fillRect方法绘制了星星,而本例子,则是使用上面提到的drawImage()方法把烟雾图片绘制进来了。

其他的位移啊,透明度变化什么的,原理都是类似。

本例子的难点主要在于模拟是否足够真实:

  1. 高处不胜寒
    越往上,烟雾越淡,实际上就是越靠近上方,透明度越低;

    // 越靠近边缘,透明度越低
    // 纵向透明度变化要比横向的明显 this.alpha = (1 - Math.abs(canvasWidth*0.5 - this.x) / canvasWidth) * (0.7 - Math.abs(canvasHeight*0.5 - this.y) / canvasHeight);
  2. 缭绕
    所谓“缭绕”,就是运动看似不具有规律性。要知道,凡事有轨迹有套路的运动都是有规律性地,你说这烟雾上上下下,左左右右运动太过于规律,效果就会打折扣,但是,真的没有规律又不好通过代码控制运动轨迹。因此,为了搞到一个接近缭绕效果的运动函数,还真是烧了不少脑细胞。

五、canvas动效与结语

本文三个例子都是canvas 2D效果,是入门学习非常好的例子。

canvas非常适合实现密集型图形和动画,可以把性能优势给发挥出来,因为就是一块画布渲染;另外一点就是省流量,比方说第2个例子的噪点效果,如果是同样效果1920*500的png图片,科科,我特意保存了下,286K,1K的代码PK 286K的图片,显然是完爆啊!

canvas还支持3D效果,也就是webGL, 亦称3D Canvas graphics, IE11 支持,目前Android 4.*任意版本都还不支持,业内著名的相关库就是threejs了。

不过,我没研究过,也没兴趣,不是我的方向。

好了,就这些,感谢阅读。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

标签: