HTML5 Canvas前端开发那些事程序员

用canvas绘制一场蓝色大雨

2016-08-12  本文已影响475人  忽如寄

一、随便说几句####

HTML5中的canvas无疑是一颗明星,利用canvas可以绘制各种各样的图形、动画,因而其在前端开发和游戏开发中有着重要地位,在网页中合理使用canvas也会使整个网页的效果格外引人注目,这次我们利用canvas实现一场蓝色大雨效果,体验一把canvas的精彩。

二、布局和画布设置####

布局没什么好说的,只是注意一点设置html和body的height为100%后,会出现滚动条,这时我们只需要将画布的定位改为固定定位fixed即可。

<canvas id="canvas"></canvas>
*{
      margin:0;
      padding:0;
}
html,body{
      height:100%;
      background:rgb(0,5,5);
}
#canvas{
      position:fixed;
      z-index:1;
}

画布设置当然是用canvas了,显然我们要使canvas占满整个浏览器窗口,关于这个可以参考下图



所以使用body.clientHeight和body.clientWidth

var Height=document.body.clientHeight,
      Width=document.body.clientWidth,
      canvas=document.getElementById("canvas"),
      context=canvas.getContext("2d");
canvas.height=Height;
canvas.width=Width;

三、绘制雨滴####

首先雨点可以看成一个对象,有开始降落的位置,降落的速度等,我们一次绘制50个雨滴,将其放在一个数组中

var rainArr=[];
function addRain(){
            for(var i=0;i<50;i++){
                var rain={
                    x:Math.ceil(Math.random()*Width),
                    y:-Math.ceil(Math.random()*1000),
                    r:Math.random()*2+2,
                    v:Math.random()*2+5,
                }
                rainArr.push(rain);
            }
        }
addRain();

绘制雨滴肯定使用绘制圆的方法,为了实现掉落的效果,我们需要不断减少r值和移动绘制的y值,因此绘制50个雨滴中会用到两次for循环

function render(){
            for(var i = 0;i<rainArr.length;i++){
                var _y = rainArr[i].y;
                var r = 2;
                var arpha = 1
                for(var j = 0;j<50;j++){
                    context.beginPath();
                    context.arc(rainArr[i].x , _y , r , 0 , Math.PI*2 , true);
                    context.fillStyle = 'rgba(0,223,223,'+arpha+')';
                    context.fill();
                    context.closePath();
                    _y -= r*1.5;
                    r -= 0.04;
                    arpha -= 0.02;
                }
                rainArr[i].y+=rainArr[i].v;             
        }
render();

这时候你可以打开你的浏览器来查看一下效果,诶,貌似黑乎乎一片,并没有效果,┑( ̄Д  ̄)┍,等等,我们的设置y为负值,此时在浏览器上方,而且也还没有添加动画,我们这时候那就添加动画,让雨滴落下来吧,每次重绘的时候我们采用clearRect()方法重置画布

function render(){
            context.clearRect(0,0,Width,Height);
                var _y = rainArr[i].y;
                var r = 2;
                var arpha = 1
                for(var j = 0;j<50;j++){
                    context.beginPath();
                    context.arc(rainArr[i].x , _y , r , 0 , Math.PI*2 , true);
                    context.fillStyle = 'rgba(0,223,223,'+arpha+')';
                    context.fill();
                    context.closePath();
                    _y -= r*1.5;
                    r -= 0.04;
                    arpha -= 0.02;
                }
                rainArr[i].y+=rainArr[i].v;
        }

此时完整代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<style>
    *{
        margin:0;
        padding:0;
    }
    html,body{
        height:100%;
        background:rgb(0,5,5);
    }
    #canvas{
        position:fixed;
        z-index:1;
    }
</style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script type="text/javascript">
        var Height=document.body.clientHeight,
            Width=document.body.clientWidth,
            canvas=document.getElementById("canvas"),
            context=canvas.getContext("2d"),
            rainArr=[];

        canvas.height=Height;
        canvas.width=Width;


        function addRain(){
            for(var i=0;i<50;i++){
                var rain={
                    x:Math.ceil(Math.random()*Width),
                    y:-Math.ceil(Math.random()*1000),
                    r:Math.random()*2+2,
                    v:Math.random()*2+5,
                    ground:Height-(Math.random()*100+50),
                    groundR:Math.random()+1,
                    groundMaxR:Math.random()*20+30
                }
                rainArr.push(rain);
            }
        }
        addRain();
        render();

        function render(){
            context.clearRect(0,0,Width,Height);
            for(var i = 0;i<rainArr.length;i++){
                var _y = rainArr[i].y;
                var r = 2;
                var arpha = 1
                for(var j = 0;j<50;j++){
                    context.beginPath();
                    context.arc(rainArr[i].x , _y , r , 0 , Math.PI*2 , true);
                    context.fillStyle = 'rgba(0,223,223,'+arpha+')';
                    context.fill();
                    context.closePath();
                    _y -= r*1.5;
                    r -= 0.04;
                    arpha -= 0.02;
                }
                rainArr[i].y+=rainArr[i].v;
            }
            setTimeout(render, 3);
        }
    </script>
</body>
</html>

效果如下:

四、绘制雨滴落下后的效果####

效果下雨的效果实现了,可是为啥看着像是一场流星雨,恩,对的,我们应该来写一下雨滴落到地上的效果,这样就不会让人误会是流星雨了,雨滴落到地面后会慢慢散开,我们也需要把雨滴添加一个地面的位置,也必然是用圆形来写,所以再加一个半径属性,这个时候雨滴对象就有了以下属性了

var rain={
x:Math.ceil(Math.random()*Width),
y:-Math.ceil(Math.random()*1000),
r:Math.random()*2+2,
v:Math.random()*2+5,
ground:Height-(Math.random()*100+50),
groundR:Math.random()+1
}

我们添加一个渲染落下后的函数

function ground(num){
            context.beginPath();
            context.fillStyle="rgb(0,223,223)";
context.arc(rainArr[num].x,rainArr[num].ground,rainArr[num].groundR,0,Math.PI*2,true);
            context.fill();
            context.closePath();
        }

显然我们这时需要添加一个判断了,判断雨滴是否落到了地上,这个利用比较就可以了,很好解决,我们这时贴上完整代码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<style>
    *{
        margin:0;
        padding:0;
    }
    html,body{
        height:100%;
        background:rgb(0,5,5);
    }
    #canvas{
        position:fixed;
        z-index:1;
    }
</style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script type="text/javascript">
        var Height=document.body.clientHeight,
            Width=document.body.clientWidth,
            canvas=document.getElementById("canvas"),
            context=canvas.getContext("2d"),
            rainArr=[];

        canvas.height=Height;
        canvas.width=Width;


        function addRain(){
            for(var i=0;i<50;i++){
                var rain={
                    x:Math.ceil(Math.random()*Width),
                    y:-Math.ceil(Math.random()*1000),
                    r:Math.random()*2+2,
                    v:Math.random()*2+5,
                    ground:Height-(Math.random()*100+50),
                    groundR:Math.random()+1
                }
                rainArr.push(rain);
            }
        }
        addRain();
        render();

        function render(){
            context.clearRect(0,0,Width,Height);
            for(var i = 0;i<rainArr.length;i++){
                var _y = rainArr[i].y;
                var r = 2;
                var arpha = 1
                for(var j = 0;j<50;j++){
                    if(_y<rainArr[i].ground){
                    context.beginPath();
                    context.arc(rainArr[i].x , _y , r , 0 , Math.PI*2 , true);
                    context.fillStyle = 'rgba(0,223,223,'+arpha+')';
                    context.fill();
                    context.closePath();
                    _y -= r*1.5;
                    r -= 0.04;
                    arpha -= 0.02;
                }
                }
                rainArr[i].y+=rainArr[i].v;
                console.log(rainArr[i].y>rainArr[i].ground);
                if(rainArr[i].y>rainArr[i].ground){
                    ground(i);
                    rainArr[i].groundR+=2;
                }
            }
            setTimeout(render, 3);
        }
        function ground(num){
            context.beginPath();
            context.fillStyle="rgb(0,223,223)";
            context.arc(rainArr[num].x,rainArr[num].ground,rainArr[num].groundR,0,Math.PI*2,true);
            context.fill();
            context.closePath();
        }
    </script>
</body>
</html>

效果如下:


额,说好的是下雨,怎么变成了水漫金山??如果你需要来个水漫金山的效果的话,这个就不错,多绘制一些雨滴相信效果会更好。我们可以在雨滴落下后给一个波纹效果,这样更加符合实际一点,我们并且应该限制雨滴波纹最后的大小,不然就会再次出现水漫金山的效果了,另外我们需要再来一个判断,判断雨滴落下后重新生成新的雨滴,重要的一点是我们为了模拟地面的效果,使用了scale()方法,使用这个方法为了不影响其他绘制,我们必须使用save()和restore()方法,另外我们这个时候就必须调整绘制波纹的y轴了,所以代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<style>
    *{
        margin:0;
        padding:0;
    }
    html,body{
        height:100%;
        background:rgb(0,5,5);
    }
    #canvas{
        position:fixed;
        z-index:1;
    }
</style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script type="text/javascript">
        var Height=document.body.clientHeight,
            Width=document.body.clientWidth,
            canvas=document.getElementById("canvas"),
            context=canvas.getContext("2d"),
            rainArr=[];

        canvas.height=Height;
        canvas.width=Width;


        function addRain(){
            for(var i=0;i<50;i++){
                var rain={
                    x:Math.ceil(Math.random()*Width),
                    y:-Math.ceil(Math.random()*1000),
                    r:Math.random()*2+2,
                    v:Math.random()*2+5,
                    ground:Height-(Math.random()*100+50),
                    groundR:Math.random()+1,
                    groundMaxR:Math.random()*20+30
                }
                rainArr.push(rain);
            }
        }
        addRain();
        render();

        function render(){
            context.clearRect(0,0,Width,Height);
            for(var i = 0;i<rainArr.length;i++){
                var _y = rainArr[i].y;
                var r = 2;
                var arpha = 1
                for(var j = 0;j<50;j++){
                    if(_y<rainArr[i].ground){
                    context.beginPath();
                    context.arc(rainArr[i].x , _y , r , 0 , Math.PI*2 , true);
                    context.fillStyle = 'rgba(0,223,223,'+arpha+')';
                    context.fill();
                    context.closePath();
                    _y -= r*1.5;
                    r -= 0.04;
                    arpha -= 0.02;
                }
                }
                rainArr[i].y+=rainArr[i].v;
                console.log(rainArr[i].y>rainArr[i].ground);
                if(rainArr[i].y>rainArr[i].ground){
                    if(rainArr[i].groundR<rainArr[i].groundMaxR){
                    ground(i);
                    rainArr[i].groundR+=2;
                }else{
                    rainArr[i]={
                        x:Math.ceil(Math.random()*Width),
                        y:-Math.ceil(Math.random()*1000),
                        r:Math.random()*2+2,
                        v:Math.random()*2+5,
                        ground:Height-(Math.random()*100+50),
                        groundR:Math.random()+1,
                        groundMaxR:Math.random()*20+30
                    }
                }
                }
            }
            setTimeout(render, 3);
        }
        function ground(num){
            context.save();
            context.scale(1,0.5);
            context.beginPath();
            var gradient=context.createRadialGradient(rainArr[num].x,rainArr[num].ground*2,0,rainArr[num].x,rainArr[num].ground*2,rainArr[num].groundR);
            gradient.addColorStop(0,"rgba(0,223,223,0)");
            gradient.addColorStop(0.5,"rgba(0,223,223,0)");
            gradient.addColorStop(1,"rgba(0,223,223,1)");
            context.fillStyle=gradient;
            context.arc(rainArr[num].x,rainArr[num].ground*2,rainArr[num].groundR,0,Math.PI*2,true);
            context.fill();
            context.closePath();
            context.restore();
        }
    </script>
</body>
</html>

效果如下:

五、梳理代码####

代码依旧是为了可维护性梳理的,因为我的JavaScript水平有限,尤其对设计模式不懂,JavaScript代码的梳理就显得有点吃力,以下是我梳理过后的代码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>用canvas绘制一场蓝色大雨</title>
<style>
    *{
        margin:0;
        padding:0;
    }
    html,body{
        height:100%;
        background:rgb(0,5,5);
    }
    #canvas{
        position:fixed;
        z-index:1;
    }
</style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script type="text/javascript">
    (function(){
        var Height=document.body.clientHeight,
            Width=document.body.clientWidth,
            canvas=document.getElementById("canvas"),
            context=canvas.getContext("2d"),
            rainArr=[];

            canvas.height=Height;
            canvas.width=Width;

        
        
        //初始化函数,程序入口
        (function init(){
            addRain();
            render();
        })()

        //添加雨滴函数
        function addRain(){
            for(var i=0;i<50;i++){
                var rain={
                    x:Math.ceil(Math.random()*Width),
                    y:-Math.ceil(Math.random()*1000),
                    r:Math.random()*2+2,
                    v:Math.random()*2+5,
                    ground:Height-(Math.random()*100+50),
                    groundR:Math.random()+1,
                    groundMaxR:Math.random()*20+30
                }
                rainArr.push(rain);
            }
        }

        //渲染页面
        function render(){
            context.clearRect(0,0,Width,Height);
            for(var i = 0;i<rainArr.length;i++){
                var _y = rainArr[i].y;
                var r = 2;
                var arpha = 1
                for(var j = 0;j<50;j++){
                    if(_y<rainArr[i].ground){
                    context.beginPath();
                    context.arc(rainArr[i].x , _y , r , 0 , Math.PI*2 , true);
                    context.fillStyle = 'rgba(0,223,223,'+arpha+')';
                    context.fill();
                    context.closePath();
                    _y -= r*1.5;
                    r -= 0.04;
                    arpha -= 0.02;
                }
                }
                rainArr[i].y+=rainArr[i].v;
                console.log(rainArr[i].y>rainArr[i].ground);
                if(rainArr[i].y>rainArr[i].ground){
                    if(rainArr[i].groundR<rainArr[i].groundMaxR){
                    ground(i);
                    rainArr[i].groundR+=2;
                }else{
                    rainArr[i]={
                        x:Math.ceil(Math.random()*Width),
                        y:-Math.ceil(Math.random()*1000),
                        r:Math.random()*2+2,
                        v:Math.random()*2+5,
                        ground:Height-(Math.random()*100+50),
                        groundR:Math.random()+1,
                        groundMaxR:Math.random()*20+30
                    }
                }
                }
            }
            setTimeout(render, 3);
        }
        
        //绘制波纹
        function ground(num){
            context.save();
            context.scale(1,0.5);
            context.beginPath();
            var gradient=context.createRadialGradient(rainArr[num].x,rainArr[num].ground*2,0,rainArr[num].x,rainArr[num].ground*2,rainArr[num].groundR);
            gradient.addColorStop(0,"rgba(0,223,223,0)");
            gradient.addColorStop(0.5,"rgba(0,223,223,0)");
            gradient.addColorStop(1,"rgba(0,223,223,1)");
            context.fillStyle=gradient;
            context.arc(rainArr[num].x,rainArr[num].ground*2,rainArr[num].groundR,0,Math.PI*2,true);
            context.fill();
            context.closePath();
            context.restore();
        }
    })(window)
    </script>
</body>
</html>

六、最后扯两句####

canvas动画个人感觉是对帧的重新绘制是关键,找到动画的入口也是整个动画实现最为关键的一步,网页中使用canvas必然会使网页出彩不少,随着旧浏览器的淘汰,相信canvas的使用会越来越重要。

上一篇下一篇

猜你喜欢

热点阅读