
炎炎夏日,快用代码下场雨ヽ`☂ヽ`canvas
发布日期:2021-05-07 18:24:15
浏览次数:22
分类:原创文章
本文共 14028 字,大约阅读时间需要 46 分钟。
一.先看效果(源码在最后):
哗啦啦啦
图片展示,因为图片限制5m大小,所以演示不太多:
二.实现过程(可一步一步实现):
因为雨是重点,所以中间 logo 部分就不详细写了,可直接看源码~
1.定义canvas标签与设置css基本样式:
<canvas id="canvas"></canvas>
*{ margin: 0; padding: 0; box-sizing: border-box; } #canvas{ position: fixed; background-color: rgb(0, 0, 0); z-index: -10; }
position: fixed; 固定定位。
2. 开始js部分,获取画布与定义基本变量:
// 获取画布 var canvas = document.querySelector("#canvas"); var rain = canvas.getContext('2d'); // drop数组,存每个散开的小水滴信息 var drop = []; // water数组,存每丝雨的信息 var water = []; // 雨的数量,可自行更改 var waterNum = 100; // 小水(雨)滴的重力 var gravity = 1; // 鼠标在页面的初始位置 var mouseX=-36,mouseY=-36; // 关于雨的角度值,值为-1到1,后面讲 var direction = 0; // 这也是关于鼠标在页面位置的角度值,值为-1到1 var mouseDelay = 0; // 这是画布自适应窗口大小的函数,复制即可 window.onresize = resizeCanvas; function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas();
2.初始化雨:
// 一丝雨的初始化,封装,后面好几处要调用 function creatWater(){ water.push({ //值为0或1,判断是否要散开水滴 add:1, //随机初始水平位置 x:Math.random()*3*canvas.width-canvas.width, // 随机初始垂直位置,在上面一点,这样雨能从上面下落 y: Math.random()*500-500, // 随机雨的长度 len: Math.random()*20+50, // 随机雨的速度 speed: Math.random()*10+35, // 随机雨的随机颜色 color: `rgb(255,255,255,${ Math.random()*0.5})`, // 随机散开水滴数量 dropNum:Math.random()*6+6 }) } // 雨数组初始化,每丝雨都来 function chushi(){ for (let i = 0; i < waterNum; i++) { creatWater(); } }
3.初始化雨滴:
// 散开水滴数组初始化,x为水平位置,y为垂直位置,dropNum为数量 function creatDrop (x,y,dropNum){ //给drop数组添加元素 for (let j = 0; j < dropNum; j++) { drop.push({ // x轴位置 pagex:x, // y轴位置 pagey:y, // x轴移动距离 dx:Math.random()*12-6, // y轴移动距离 dy:Math.random()*10-20, // 半径 r:Math.random()+2, //颜色 color: `rgb(255,255,255,${ Math.random()*0.5+0.5})`, }) } }
4.绘画每一丝雨:
// 绘画,画雨 function drawWater(){ //遍历数组 for (let i = 0; i < water.length; i++){ let temp = water[i]; // 颜色 rain.strokeStyle = temp.color; // 开始路径 rain.beginPath(); // 开始点 rain.moveTo(temp.x,temp.y); // 结束点,连线,如: 当前x位置+长度*角度值 rain.lineTo(temp.x+temp.len*direction,temp.y+temp.len); // 线宽度 rain.lineWidth = 3; // 绘制 rain.stroke(); } }
5.绘画每一个雨滴:
// 绘画雨滴 function drawDrop(){ //遍历 for (let i = 0; i < drop.length; i++){ let temp = drop[i]; // 线宽度 rain.lineWidth = 2; //颜色 rain.strokeStyle = temp.color; //开始路径 rain.beginPath(); // 画一个随机的弧度 rain.arc(temp.pagex,temp.pagey,temp.r, Math.PI , Math.random() * 2 * Math.PI); // 绘制 rain.stroke(); //结束路径 rain.closePath(); } }
5. 雨信息更新,同时判断各种事件:
//雨信息的更新 function updateWater(){ for (let i = 0; i < water.length; i++){ // 判断雨的底部是否碰到鼠标,碰到就散开成水滴,x轴y轴与鼠标的位置绝对值在35之内则散开。 if(Math.abs(mouseX-water[i].x)<35&&Math.abs(mouseY-water[i].y-water[i].len)<35){ // 创建雨滴,传入x轴y轴大小与数量 creatDrop(water[i].x,water[i].y+water[i].len,water[i].dropNum); // 既然水滴散开了,就清除掉这丝雨 water.splice(i,1); // 重新来一丝随机的雨 creatWater(); } // 判断雨的底部是否超过画布底部,且add值为1 if(((water[i].y+water[i].len)>=canvas.height) && water[i].add==1){ // add值为0 water[i].add = 0; // 创建雨滴,传入x轴y轴大小与数量,y轴位置就为画布高即可 creatDrop(water[i].x,canvas.height,water[i].dropNum); } // 判断整丝雨是否超过画布 if(water[i].y>canvas.height){ // 清除它 water.splice(i,1); // 来个新的 creatWater(); } // 缓动动画原理,雨角度慢慢接近鼠标的角度 direction += (mouseDelay - direction)*0.0002; // 雨x轴位置改变 water[i].x += water[i].speed*direction; //雨y轴位置改变 water[i].y += water[i].speed; } }
6. 雨滴信息更新:
// 雨滴信息更新 function updateDrop(){ for(let i=0;i<drop.length;i++){ // y轴移动距离加上重力。因为dy一开是负数,所以雨滴先升后降 drop[i].dy += gravity; // x轴位置改变,同时它也受雨角度影响 drop[i].pagex += drop[i].dx + direction*10; // y轴位置改变 drop[i].pagey += drop[i].dy; //判断雨滴是否超过画布 if(drop[i].pagey>canvas.height){ //清除水滴 drop.splice(i,1); } } }
7.绑定鼠标事件:
// 绑定鼠标移动事件 window.addEventListener('mousemove',e=>{ // 得到x轴位置 mouseX = e.clientX; //得到y轴位置 mouseY = e.clientY; // 雨角度值,在-1到1之间 mouseDelay = (e.clientX-canvas.offsetWidth/2)/(canvas.offsetWidth/2); }) // 判断鼠标离开事件 window.addEventListener('mouseout',()=>{ // 给个值,不会产生雨滴的值就行 mouseY=canvas.height+40; })
8.设置定时器,开始下雨动画:
// 先初始化雨数组 chushi(); //设置定时器,开始动画 setInterval(function(){ // 清除画布 rain.clearRect(0,0,canvas.width,canvas.height); // 更新雨和雨滴信息 updateWater(); updateDrop(); // 绘画雨和雨滴 drawWater(); drawDrop(); },20)
三.完整代码:
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>北极光之夜。</title> <style> *{ margin: 0; padding: 0; box-sizing: border-box; } #canvas{ position: fixed; background-color: rgb(0, 0, 0); z-index: -10; } svg{ position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); } .txt{ font-family: 'fangsong'; font-weight: 900; font-size: 80px; letter-spacing: 3px; fill: transparent; stroke: rgb(30, 134, 252); stroke-width: 1.5px; stroke-dasharray: 625; stroke-dashoffset: 625; animation: draw 2s linear infinite; text-shadow: 0 0 10px rgb(30, 134, 252), 0 0 20px rgb(30, 134, 252), 0 0 40px rgb(30, 134, 252), 0 0 60px rgb(30, 134, 252); } @keyframes draw{ 0%,100%{ stroke-dasharray: 625; stroke-dashoffset: 625; } 45%,55%{ stroke-dasharray: 652; stroke-dashoffset: 0; } } </style></head><body> <svg width="500" height="200"> <text x="30" y="120" class="txt">北极光之夜。</text> </svg> <canvas id="canvas"></canvas> <script> // 获取画布 var canvas = document.querySelector("#canvas"); var rain = canvas.getContext('2d'); // drop数组,存每个散开的小水滴信息 var drop = []; // water数组,存每丝雨的信息 var water = []; // 雨的数量,可自行更改 var waterNum = 100; // 小水(雨)滴的重力 var gravity = 1; // 鼠标在页面的初始位置 var mouseX=-36,mouseY=-36; // 关于雨的角度值,值为-1到1,后面讲 var direction = 0; // 这也是关于鼠标在页面位置的角度值,值为-1到1 var mouseDelay = 0; // 这是画布自适应窗口大小的函数,复制即可 window.onresize = resizeCanvas; function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas(); // 一丝雨的初始化,封装,后面好几处要调用 function creatWater(){ water.push({ //值为0或1,判断是否要散开水滴 add:1, //随机初始水平位置 x:Math.random()*3*canvas.width-canvas.width, // 随机初始垂直位置,在上面一点,这样雨能从上面下落 y: Math.random()*500-500, // 随机雨的长度 len: Math.random()*20+50, // 随机雨的速度 speed: Math.random()*10+35, // 随机雨的随机颜色 color: `rgb(255,255,255,${ Math.random()*0.5})`, // 随机散开水滴数量 dropNum:Math.random()*6+6 }) } // 雨数组初始化,每丝雨都来 function chushi(){ for (let i = 0; i < waterNum; i++) { creatWater(); } } // 散开水滴数组初始化,x为水平位置,y为垂直位置,dropNum为数量 function creatDrop (x,y,dropNum){ //给drop数组添加元素 for (let j = 0; j < dropNum; j++) { drop.push({ // x轴位置 pagex:x, // y轴位置 pagey:y, // x轴移动距离 dx:Math.random()*12-6, // y轴移动距离 dy:Math.random()*10-20, // 半径 r:Math.random()+2, //颜色 color: `rgb(255,255,255,${ Math.random()*0.5+0.5})`, }) } } // 绘画,画雨 function drawWater(){ //遍历数组 for (let i = 0; i < water.length; i++){ let temp = water[i]; // 颜色 rain.strokeStyle = temp.color; // 开始路径 rain.beginPath(); // 开始点 rain.moveTo(temp.x,temp.y); // 结束点,连线,如: 当前x位置+长度*角度值 rain.lineTo(temp.x+temp.len*direction,temp.y+temp.len); // 线宽度 rain.lineWidth = 3; // 绘制 rain.stroke(); } } // 绘画雨滴 function drawDrop(){ //遍历 for (let i = 0; i < drop.length; i++){ let temp = drop[i]; // 线宽度 rain.lineWidth = 2; //颜色 rain.strokeStyle = temp.color; //开始路径 rain.beginPath(); // 画一个随机的弧度 rain.arc(temp.pagex,temp.pagey,temp.r, Math.PI , Math.random() * 2 * Math.PI); // 绘制 rain.stroke(); //结束路径 rain.closePath(); } } //雨信息的更新 function updateWater(){ for (let i = 0; i < water.length; i++){ // 判断雨的底部是否碰到鼠标,碰到就散开成水滴,x轴y轴与鼠标的位置绝对值在35之内则散开。 if(Math.abs(mouseX-water[i].x)<35&&Math.abs(mouseY-water[i].y-water[i].len)<35){ // 创建雨滴,传入x轴y轴大小与数量 creatDrop(water[i].x,water[i].y+water[i].len,water[i].dropNum); // 既然水滴散开了,就清除掉这丝雨 water.splice(i,1); // 重新来一丝随机的雨 creatWater(); } // 判断雨的底部是否超过画布底部,且add值为1 if(((water[i].y+water[i].len)>=canvas.height) && water[i].add==1){ // add值为0 water[i].add = 0; // 创建雨滴,传入x轴y轴大小与数量,y轴位置就为画布高即可 creatDrop(water[i].x,canvas.height,water[i].dropNum); } // 判断整丝雨是否超过画布 if(water[i].y>canvas.height){ // 清除它 water.splice(i,1); // 来个新的 creatWater(); } // 缓动动画原理,雨角度慢慢接近鼠标的角度 direction += (mouseDelay - direction)*0.0002; // 雨x轴位置改变 water[i].x += water[i].speed*direction; //雨y轴位置改变 water[i].y += water[i].speed; } } // 雨滴信息更新 function updateDrop(){ for(let i=0;i<drop.length;i++){ // y轴移动距离加上重力。因为dy一开是负数,所以雨滴先升后降 drop[i].dy += gravity; // x轴位置改变,同时它也受雨角度影响 drop[i].pagex += drop[i].dx + direction*10; // y轴位置改变 drop[i].pagey += drop[i].dy; //判断雨滴是否超过画布 if(drop[i].pagey>canvas.height){ //清除水滴 drop.splice(i,1); } } } // 绑定鼠标移动事件 window.addEventListener('mousemove',e=>{ // 得到x轴位置 mouseX = e.clientX; //得到y轴位置 mouseY = e.clientY; // 雨角度值,在-1到1之间 mouseDelay = (e.clientX-canvas.offsetWidth/2)/(canvas.offsetWidth/2); }) // 判断鼠标离开事件 window.addEventListener('mouseout',()=>{ // 给个值,不会产生雨滴的值就行 mouseY=canvas.height+40; }) // 先初始化雨数组 chushi(); //设置定时器,开始动画 setInterval(function(){ // 清除画布 rain.clearRect(0,0,canvas.width,canvas.height); // 更新雨和雨滴信息 updateWater(); updateDrop(); // 绘画雨和雨滴 drawWater(); drawDrop(); },20) </script></body></html>
四.总结:
看到这里了,不点个赞再走吗~
五.其它文章:
其它文章:
…等等
发表评论
最新留言
留言是一种美德,欢迎回访!
[***.207.175.100]2025年03月27日 08时23分26秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
王爽 《汇编语言》 读书笔记 三 寄存器(内存访问)
2021-05-07
Android基本知识
2021-05-08
命令模式【Command Pattern】
2021-05-08
OSI 7 层网络模型
2021-05-08
JDK 内置的多线程协作工具类的使用场景
2021-05-08
Java 中哪些对象可以获取类对象
2021-05-08
linux 的 cp 命令如何复制不提示覆盖
2021-05-08
linux 的 sleep 命令
2021-05-08
js 的 let var const 区别
2021-05-08
vue计算属性和监听器区别
2021-05-08
11.2.6 时间值的小数秒
2021-05-08
11.2.7 日期和时间类型之间的转换
2021-05-08
redis 内存溢出_从数据存储的角度告诉你Redis为什么这么快!
2021-05-08
实例分析Facebook激励视频广告接入
2021-05-08
实例:使用OKGO下载网络压缩包资源,然后解压缩放在本地使用
2021-05-08
解决mybatis嵌套查询使用PageHelper分页不准确
2021-05-08
Redis源码分析(七)--- zipmap压缩图
2021-05-08