一、随便说几句####
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的使用会越来越重要。
网友评论