canvas下一场夏雨

举报
北极光之夜。 发表于 2021/05/21 17:26:32 2021/05/21
【摘要】 一.先看效果(源码在最后):我的B站地址~效果演示更清晰图片展示,因为图片限制5m大小,所以演示不太多: 二.实现过程(可一步一步实现):因为雨是重点,所以中间 logo 部分就不详细写了,可直接看源码~ 1.定义canvas标签与设置css基本样式: <canvas id="canvas"></canvas> *{ margin: 0; ...

一.先看效果(源码在最后):

我的B站地址~效果演示更清晰

图片展示,因为图片限制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>

四.总结:

看到这里了,不点个赞再走吗~

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200