自己私下做的一个火焰效果,留下作为记录。如果觉得有用,就请给个赞吧!
相关的解释都在代码中注释了.如果不需要看解释,文末有全部代码.可以先复制下来看看效果.
首先创建canvas对象,同时创建获取随机数的函数,方便后续使用.
let canvas_el = document.getElementById("fireGraphic");
let ctx = canvas_el.getContext("2d");
canvas_el.width = canvas_el.height = 600;
function rand (min, max){
return Math.floor((Math.random() * (max - min + 1)) + min);
};
//火焰粒子对象效果
function fireBall(position) {
this.rest(position);
};
// 帧数动画对象
window.requestAnimFrame = function () {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (a) {
window.setTimeout(a, 1E3 / 60)
}
}();
创建单个火焰粒子,具有独立的属性和方法.粒子状态需要重新初始化的判断条件有多个,比如:当粒子的半径减少为负数时,或粒子超出画布时,或粒子存活时间剩余为0时,都可以重置此粒子的状态.本文是采用当粒子半径减少为负数作为重置条件.
//火焰粒子对象效果
function fireBall(position) {
this.rest(position);
};
//初始/重置粒子状态
fireBall.prototype.rest = function (position) {
this._radius = rand(5, 25);//随机产生初始粒子半径
this._position = position || {x: 300, y: 300};//初始位置
this._lineWidth = 1;//粒子边界线宽
//rgba(255,'+rand(0,165)+','+rand(0,100)+',0.3)
this._color = 'rgba(255,5,0,0.3)';//粒子颜色
}
//渲染
fireBall.prototype.render=function(){
ctx.beginPath();
ctx.arc(this._position.x, this._position.y, this._radius, 0, Math.PI * 2, false);
ctx.fillStyle = ctx.strokeStyle = this._color;
ctx.lineWidth = this._lineWidth;
ctx.fill();
ctx.stroke();
}
// 更新粒子状态
fireBall.prototype.update = function () {
if(this._radius>0&&this._position.y >this._radius){
this._position.x -= rand(-3,3);
this._position.y -= rand(3,4);
this._radius -= 0.5;
}else{
this.rest();
}
}
单个粒子的功能实现完成后,只需要使用多个粒子构成火球效果
//用多个粒子模拟火球视觉效果,粒子数量自定
let fires = [];
for(var i=0; i< 200; i++){
fires.push(new fireBall())
}
随后,按照循环调用粒子函数,渲染每一帧的图像.
let loop = function(){
//循环调用
requestAnimFrame(loop, canvas_el)
//覆盖上一帧图像
ctx.globalCompositeOperation="destination-out";
ctx.fillStyle = 'hsla(0, 0%, 0%, 0.2)';
ctx.fillRect(0,0,600,600);
ctx.globalCompositeOperation = 'lighter';
//绘制本帧的图像,更新状态为下一帧做准备
fires.forEach(fire=>{
fire.render();
fire.update();
})
}
loop();
PS:在此基础上,可以封装一个火球对象,后续使用时会更方便.本文不再做更多探讨
效果图:
最后附上全部代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>新增页面</title>
<style>
body {
background: #111;
}
canvas {
display: block;
left: 30%;
position: absolute;
top: 30%;
}
</style>
</head>
<body>
<canvas id="fireGraphic"></canvas>
<script>
let canvas_el = document.getElementById("fireGraphic");
let ctx = canvas_el.getContext("2d");
canvas_el.width = canvas_el.height = 600;
function rand (min, max){
return Math.floor((Math.random() * (max - min + 1)) + min);
};
//火焰粒子对象效果
function fireBall(position) {
this.rest(position);
};
// 帧数动画对象
window.requestAnimFrame = function () {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (a) {
window.setTimeout(a, 1E3 / 60)
}
}();
//初始/重置粒子状态
fireBall.prototype.rest = function (position) {
this._radius = rand(5, 25);//随机产生初始粒子半径
this._position = position || {x: 300, y: 300};//初始位置
this._lineWidth = 1;//粒子边界线宽
//rgba(255,'+rand(0,165)+','+rand(0,100)+',0.3)
this._color = 'rgba(255,5,0,0.3)';//粒子颜色
}
//渲染
fireBall.prototype.render=function(){
ctx.beginPath();
ctx.arc(this._position.x, this._position.y, this._radius, 0, Math.PI * 2, false);
ctx.fillStyle = ctx.strokeStyle = this._color;
ctx.lineWidth = this._lineWidth;
ctx.fill();
ctx.stroke();
}
// 更新粒子状态
fireBall.prototype.update = function () {
if(this._radius>0&&this._position.y >this._radius){
this._position.x -= rand(-3,3);
this._position.y -= rand(3,4);
this._radius -= 0.5;
}else{
this.rest();//
}
}
//用多个粒子模拟火球视觉效果,粒子数量自定
let fires = [];
for(var i=0; i< 200; i++){
fires.push(new fireBall())
}
let loop = function(){
//循环调用
requestAnimFrame(loop, canvas_el)
//覆盖上一帧图像
ctx.globalCompositeOperation="destination-out";
ctx.fillStyle = 'hsla(0, 0%, 0%, 0.2)';
ctx.fillRect(0,0,600,600);
ctx.globalCompositeOperation = 'lighter';
//绘制本帧的图像,更新状态为下一帧做准备
fires.forEach(fire=>{
fire.render();
fire.update();
})
}
loop();
</script>
</body>
</html>