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画布的方向,而不是单纯的矩形的旋转;也因为是改变了画布的方法,所以画布的坐标是根据旋转之后的的位置重新定位的
上面是平移或旋转之后画布的位置(以坐标轴看出)
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 中设置的大小是表示了图片本身的大小
完整代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>火把</title> 8 <style> 9 html,body{ 10 margin:0; 11 width:100%; 12 height:100%; 13 /* font-size: 0; */ 14 } 15 canvas{ 16 background: #000; 17 vertical-align: bottom; 18 19 /* width:100%; 20 height:100%; */ 21 } 22 </style> 23 </head> 24 <body> 25 <canvas id="torch" width="600px" height="600px"></canvas> 26 <script> 27 let ele=document.getElementById('torch'); 28 ele.width=document.documentElement.clientWidth; 29 ele.height=document.documentElement.clientHeight; 30 let ctx=ele.getContext('2d'); 31 32 //初始化,存放火焰的数组 33 let arrTorch=[]; 34 // 鼠标的当前位置 35 let mouse={}; 36 for(let i=0;i<8;i++){ 37 arrTorch.push(new torch()); 38 } 39 console.log(arrTorch); 40 41 function random(min,max){ 42 return min+Math.random()*(max-min); 43 } 44 45 // 鼠标事件 46 window.onmousemove= function (event){ 47 let e = event|| window.event; 48 let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; 49 let scrollY = document.documentElement.scrollTop || document.body.scrollTop; 50 mouse.x = e.pageX || e.clientX + scrollX; 51 mouse.y= e.pageY || e.clientY + scrollY; 52 } 53 window.onmouseout = function() { 54 mouse.x= null; 55 mouse.y= null; 56 } 57 // 用requestAnimationFrame代替setInterval 58 // 适配不同的浏览器缺少某个方法的一段算法 这段代码的作用就是解决一些浏览器没有requestAnimationFrame这个方法 59 //像这样的一段算法,或者说代码,是有名词来称呼它的叫做 垫片(polyfill) 60 61 window.requestAnimationFrame = window.requestAnimationFrame 62 || window.mozRequestAnimationFrame 63 || window.webkitRequestAnimationFrame 64 || window.msRequestAnimationFrame 65 || function(callback){ 66 setInterval(callback,16.7) 67 }; 68 69 // 构造火焰 70 function torch(){ 71 // 构造出来的菱形的对角线一半的长度 72 this.width=random(18,30); 73 this.maxWidth=this.width; 74 // 火焰位置 75 if(mouse.x&&mouse.y){ 76 this.location={ 77 x:mouse.x, 78 y:mouse.y 79 } 80 }else{ 81 // 表示鼠标不在当前范围中则采用固定的位置 82 this.location={ 83 x:Math.floor(ele.width/2), 84 y:Math.floor(ele.height/2) 85 } 86 87 } 88 // 中心点 89 this.rectCenterPoint = {x:this.location.x, y:this.location.y-this.width }; // 矩形中心点 90 // this.windy=random(-.1,.1); 91 // 运动速度 92 this.speed={ 93 x: random(-.2,.5), 94 y: random(1.5,2.5) 95 } 96 // 火焰存活时间 97 this.life=this.width*random(1,2); 98 this.remaining_life=this.maxWidth/this.life; 99 this.radius = Math.sqrt(Math.pow(this.width,2)+Math.pow(this.width,2)); 100 this.g=random(20,70); 101 102 // 绘制菱形 103 this.rect=function(ctx){ 104 ctx.beginPath(); 105 ctx.shadowBlur=2; 106 ctx.shadowColor="rgb(215,148,21)"; 107 108 // 画菱形 109 // ctx.translate(this.rectCenterPoint.x, this.rectCenterPoint.y); 110 // ctx.rotate(45*Math.PI/180); 111 // ctx.translate(-this.rectCenterPoint.x, -this.rectCenterPoint.y); 112 113 // // ctx.fillStyle = 'rgb(255,255,0,.5)'; 114 // ctx.fillStyle=`rgba(${random(245,255)},${this.g},37,${random(.5,1)})`; 115 // ctx.fillRect(this.location.x, this.location.y,this.width,this.width); 116 // ctx.fill(); 117 // ctx.rotate(-45*Math.PI/180); 118 // ctx.translate(-this.rectCenterPoint.x, -this.rectCenterPoint.y); 119 120 ctx.moveTo(this.location.x,this.location.y); 121 ctx.lineTo(this.location.x-this.width,this.location.y-this.width); 122 ctx.lineTo(this.location.x,this.location.y-2*this.width); 123 ctx.lineTo(this.location.x+this.width,this.location.y-this.width); 124 ctx.lineTo(this.location.x,this.location.y); 125 // 渐变颜色 126 let grd = ctx.createRadialGradient(this.rectCenterPoint.x,this.rectCenterPoint.y,random(1,8),this.rectCenterPoint.x,this.rectCenterPoint.y, this.radius); 127 grd.addColorStop(1,`rgba(245,${this.g},37,.8)`); 128 grd.addColorStop(0,`rgb(244,${random(37,71)},37)`); 129 ctx.fillStyle=grd; 130 ctx.fill(); 131 ctx.closePath() 132 } 133 134 } 135 136 137 //实例化 138 function draw(){ 139 ctx.clearRect(0,0,ele.width,ele.height); 140 // ctx.globalCompositeOperation = 'destination-over' 141 for (var i = 0; i < arrTorch.length; i++) { 142 var p = arrTorch[i]; 143 p.rect(ctx); 144 p.width-=p.remaining_life; 145 // 每次移动 有关颜色的参数的变化 146 p.g+=random(50,65); 147 // 位置变化 148 p.location.y-=p.speed.y; 149 p.location.x= p.location.x-p.speed.x; 150 // 若菱形长度为0 或者生存时间为0 151 if(p.width<0||p.remaining_life<0){ 152 // 当前的菱形重新实例化一次 153 arrTorch[i]=new torch(); 154 } 155 } 156 requestAnimationFrame(draw); 157 } 158 draw(); 159 160 </script> 161 </body> 162 </html>