canvas 实现火焰的简单方式

jinxiutong 2020-05-03

实现效果如下

canvas 实现火焰的简单方式

实验方法:

1. 火焰的构造

// 构造火焰
        function torch(){ 
            // 构造出来的菱形的对角线一半的长度
            this.width=random(18,30); 
            this.maxWidth=this.width;
            // 火焰位置
            if(mouse.x&&mouse.y){
                this.location={
                    x:mouse.x,
                    y:mouse.y
                }
            }else{
                 // 表示鼠标不在当前范围中则采用固定的位置
                this.location={
                    x:Math.floor(ele.width/2),
                    y:Math.floor(ele.height/2)
                }
              
            }
            // 中心点
            this.rectCenterPoint = {x:this.location.x, y:this.location.y-this.width }; // 矩形中心点
            // this.windy=random(-.1,.1);
            // 运动速度
            this.speed={
                x: random(-.2,.5),
                y: random(1.5,2.5)
            }
            // 火焰存活时间
            this.life=this.width*random(1,2);
            this.remaining_life=this.maxWidth/this.life;
            this.radius = Math.sqrt(Math.pow(this.width,2)+Math.pow(this.width,2));
            this.g=random(20,70);
            
            // 绘制菱形
            this.rect=function(ctx){
                ctx.beginPath(); 
                ctx.shadowBlur=2;
                ctx.shadowColor="rgb(215,148,21)";

                // 画菱形
                // ctx.translate(this.rectCenterPoint.x, this.rectCenterPoint.y);
                // ctx.rotate(45*Math.PI/180);
                // ctx.translate(-this.rectCenterPoint.x, -this.rectCenterPoint.y);

                // // ctx.fillStyle = ‘rgb(255,255,0,.5)‘;
                // ctx.fillStyle=`rgba(${random(245,255)},${this.g},37,${random(.5,1)})`;
                // ctx.fillRect(this.location.x, this.location.y,this.width,this.width);
                // ctx.fill();
                // ctx.rotate(-45*Math.PI/180);
                // ctx.translate(-this.rectCenterPoint.x, -this.rectCenterPoint.y);

                ctx.moveTo(this.location.x,this.location.y);
                ctx.lineTo(this.location.x-this.width,this.location.y-this.width);
                ctx.lineTo(this.location.x,this.location.y-2*this.width);
                ctx.lineTo(this.location.x+this.width,this.location.y-this.width);
                ctx.lineTo(this.location.x,this.location.y); 
                // 渐变颜色                 
                let grd = ctx.createRadialGradient(this.rectCenterPoint.x,this.rectCenterPoint.y,random(1,8),this.rectCenterPoint.x,this.rectCenterPoint.y, this.radius);
                grd.addColorStop(1,`rgba(245,${this.g},37,.8)`);
                grd.addColorStop(0,`rgb(244,${random(37,71)},37)`);
                ctx.fillStyle=grd; 
                ctx.fill();
                ctx.closePath()
            }
        
        }

 function random(min,max){
          return min+Math.random()*(max-min);
        }

菱形的构造有两种方法推荐,一种是直接用moveTo+lineTo 直接连接画出(推荐这种),

  一种是通过矩形的旋转与平移,但是矩形的旋转rotate,旋转之后是改变了canvas画布的方向,而不是单纯的矩形的旋转;也因为是改变了画布的方法,所以画布的坐标是根据旋转之后的的位置重新定位的

 canvas 实现火焰的简单方式canvas 实现火焰的简单方式

上面是平移或旋转之后画布的位置(以坐标轴看出)

2.初始化数据

//初始化,存放火焰的数组
        let arrTorch=[]; 
         // 鼠标的当前位置 
        let mouse={};
        for(let i=0;i<8;i++){
            arrTorch.push(new torch());     
        }

3.鼠标位置获取

// 鼠标事件
        window.onmousemove= function (event){
            let e = event|| window.event;
            let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
            let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
            mouse.x = e.pageX || e.clientX + scrollX;
            mouse.y= e.pageY || e.clientY + scrollY;  
        }
        window.onmouseout = function() {
            mouse.x= null;
            mouse.y= null;
        }

4.绘制画布

//实例化
        function draw(){        
           ctx.clearRect(0,0,ele.width,ele.height);
            // ctx.globalCompositeOperation = ‘destination-over‘
            for (var i = 0; i < arrTorch.length; i++) {
                var p = arrTorch[i];   
                p.rect(ctx);              
                p.width-=p.remaining_life;
                // 每次移动 有关颜色的参数的变化
                p.g+=random(50,65);
                // 位置变化
                p.location.y-=p.speed.y;
                p.location.x= p.location.x-p.speed.x;
                // 若菱形长度为0 或者生存时间为0 
                if(p.width<0||p.remaining_life<0){
                    // 当前的菱形重新实例化一次
                    arrTorch[i]=new torch();
                }
            }
           requestAnimationFrame(draw);
        }
       draw();

5. 其他

css 样式

<style>
        html,body{
            margin:0;
            width:100%;
            height:100%; 
            /* font-size: 0;  */
        }
        canvas{
            background: #000;
            vertical-align: bottom;
            
            width:100%;
            height:100%;
        }
    </style>

html

<canvas id="torch" width="600px" height="600px"></canvas>

注:canvas 是一个行内元素,css 设置大小是显示图片在屏幕内像素的大小     在html 中设置的大小是表示了图片本身的大小

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>火把</title>
    <style>
        html,body{
            margin:0;
            width:100%;
            height:100%; 
            /* font-size: 0;  */
        }
        canvas{
            background: #000;
            vertical-align: bottom;
            
            /* width:100%;
            height:100%; */
        }
    </style>
</head>
<body>
    <canvas id="torch" width="600px" height="600px"></canvas>
    <script>
        let ele=document.getElementById(‘torch‘);
        ele.width=document.documentElement.clientWidth;    
        ele.height=document.documentElement.clientHeight;
        let ctx=ele.getContext(‘2d‘);
        
        //初始化,存放火焰的数组
        let arrTorch=[]; 
         // 鼠标的当前位置 
        let mouse={};
        for(let i=0;i<8;i++){
            arrTorch.push(new torch());     
        }
        console.log(arrTorch);
       
        function random(min,max){
          return min+Math.random()*(max-min);
        }

        // 鼠标事件
        window.onmousemove= function (event){
            let e = event|| window.event;
            let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
            let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
            mouse.x = e.pageX || e.clientX + scrollX;
            mouse.y= e.pageY || e.clientY + scrollY;  
        }
        window.onmouseout = function() {
            mouse.x= null;
            mouse.y= null;
        }
        // 用requestAnimationFrame代替setInterval
        // 适配不同的浏览器缺少某个方法的一段算法 这段代码的作用就是解决一些浏览器没有requestAnimationFrame这个方法
        //像这样的一段算法,或者说代码,是有名词来称呼它的叫做 垫片(polyfill)

        window.requestAnimationFrame = window.requestAnimationFrame 
                                        || window.mozRequestAnimationFrame 
                                        || window.webkitRequestAnimationFrame 
                                        || window.msRequestAnimationFrame
                                        || function(callback){
                                            setInterval(callback,16.7)
                                            };

        // 构造火焰
        function torch(){ 
            // 构造出来的菱形的对角线一半的长度
            this.width=random(18,30); 
            this.maxWidth=this.width;
            // 火焰位置
            if(mouse.x&&mouse.y){
                this.location={
                    x:mouse.x,
                    y:mouse.y
                }
            }else{
                 // 表示鼠标不在当前范围中则采用固定的位置
                this.location={
                    x:Math.floor(ele.width/2),
                    y:Math.floor(ele.height/2)
                }
              
            }
            // 中心点
            this.rectCenterPoint = {x:this.location.x, y:this.location.y-this.width }; // 矩形中心点
            // this.windy=random(-.1,.1);
            // 运动速度
            this.speed={
                x: random(-.2,.5),
                y: random(1.5,2.5)
            }
            // 火焰存活时间
            this.life=this.width*random(1,2);
            this.remaining_life=this.maxWidth/this.life;
            this.radius = Math.sqrt(Math.pow(this.width,2)+Math.pow(this.width,2));
            this.g=random(20,70);
            
            // 绘制菱形
            this.rect=function(ctx){
                ctx.beginPath(); 
                ctx.shadowBlur=2;
                ctx.shadowColor="rgb(215,148,21)";

                // 画菱形
                // ctx.translate(this.rectCenterPoint.x, this.rectCenterPoint.y);
                // ctx.rotate(45*Math.PI/180);
                // ctx.translate(-this.rectCenterPoint.x, -this.rectCenterPoint.y);

                // // ctx.fillStyle = ‘rgb(255,255,0,.5)‘;
                // ctx.fillStyle=`rgba(${random(245,255)},${this.g},37,${random(.5,1)})`;
                // ctx.fillRect(this.location.x, this.location.y,this.width,this.width);
                // ctx.fill();
                // ctx.rotate(-45*Math.PI/180);
                // ctx.translate(-this.rectCenterPoint.x, -this.rectCenterPoint.y);

                ctx.moveTo(this.location.x,this.location.y);
                ctx.lineTo(this.location.x-this.width,this.location.y-this.width);
                ctx.lineTo(this.location.x,this.location.y-2*this.width);
                ctx.lineTo(this.location.x+this.width,this.location.y-this.width);
                ctx.lineTo(this.location.x,this.location.y); 
                // 渐变颜色                 
                let grd = ctx.createRadialGradient(this.rectCenterPoint.x,this.rectCenterPoint.y,random(1,8),this.rectCenterPoint.x,this.rectCenterPoint.y, this.radius);
                grd.addColorStop(1,`rgba(245,${this.g},37,.8)`);
                grd.addColorStop(0,`rgb(244,${random(37,71)},37)`);
                ctx.fillStyle=grd; 
                ctx.fill();
                ctx.closePath()
            }
        
        }
        

        //实例化
        function draw(){        
           ctx.clearRect(0,0,ele.width,ele.height);
            // ctx.globalCompositeOperation = ‘destination-over‘
            for (var i = 0; i < arrTorch.length; i++) {
                var p = arrTorch[i];   
                p.rect(ctx);              
                p.width-=p.remaining_life;
                // 每次移动 有关颜色的参数的变化
                p.g+=random(50,65);
                // 位置变化
                p.location.y-=p.speed.y;
                p.location.x= p.location.x-p.speed.x;
                // 若菱形长度为0 或者生存时间为0 
                if(p.width<0||p.remaining_life<0){
                    // 当前的菱形重新实例化一次
                    arrTorch[i]=new torch();
                }
            }
           requestAnimationFrame(draw);
        }
       draw();  
      
    </script>
</body>
</html>

相关推荐