写点什么

快过年了,用五种不同的 JS 特效带你看烟花

  • 2022 年 1 月 14 日
  • 本文字数:9584 字

    阅读完需:约 31 分钟

快过年了,用五种不同的JS特效带你看烟花

今天给大家带来几个好看的基于 HTML+CSS+JS 的烟花特效


雪花樱花浪漫贺卡 以及 圣诞树 我前段时间刚做过,感兴趣的也可以看看我前几期的博客:


雪花:跟小海一起看下雪——用 HTML、CSS 和 JS 实现简单的下雪特效

樱花:HTML 制作 3D 樱花漫天飞舞及浪漫信封

圣诞树和贺卡:https://xie.infoq.cn/article/04369b6c40349fc1d025f9370

🎇 五种不同的 JS 烟花特效

🍕 一、简单大气的烟花

演示地址:http://haiyong.site/fireworks1



HTML 代码:


这里的 HTML 代码很简短


<div>  <canvas id="canvas"></canvas></div>
复制代码


CSS 代码


css 也只有这两段内容


body{  background:black;  overflow:hidden;  margin:0;}canvas{  background:#000;}
复制代码


JS 代码


所有的源码都在这里了,复制粘贴即可


window.addEventListener("resize", resizeCanvas, false);window.addEventListener("DOMContentLoaded", onLoad, false);window.requestAnimationFrame = window.requestAnimationFrame       || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame    || window.oRequestAnimationFrame      || window.msRequestAnimationFrame     || function (callback) {    window.setTimeout(callback, 1000/60);}; var canvas, ctx, w, h, particles = [], probability = 0.04,     xPoint, yPoint; function onLoad() {     canvas = document.getElementById("canvas");     ctx = canvas.getContext("2d");     resizeCanvas();          window.requestAnimationFrame(updateWorld); }   function resizeCanvas() {     if (!!canvas) {         w = canvas.width = window.innerWidth;         h = canvas.height = window.innerHeight;     } }   function updateWorld() {     update();     paint();     window.requestAnimationFrame(updateWorld); }   function update() {     if (particles.length < 500 && Math.random() < probability) {         createFirework();     }     var alive = [];     for (var i=0; i<particles.length; i++) {         if (particles[i].move()) {             alive.push(particles[i]);         }     }     particles = alive; }   function paint() {     ctx.globalCompositeOperation = 'source-over';     ctx.fillStyle = "rgba(0,0,0,0.2)";     ctx.fillRect(0, 0, w, h);     ctx.globalCompositeOperation = 'lighter';     for (var i=0; i<particles.length; i++) {         particles[i].draw(ctx);     } }   function createFirework() {     xPoint = Math.random()*(w-200)+100;     yPoint = Math.random()*(h-200)+100;     var nFire = Math.random()*50+100;     var c = "rgb("+(~~(Math.random()*200+55))+","          +(~~(Math.random()*200+55))+","+(~~(Math.random()*200+55))+")";     for (var i=0; i<nFire; i++) {         var particle = new Particle();         particle.color = c;         var vy = Math.sqrt(25-particle.vx*particle.vx);         if (Math.abs(particle.vy) > vy) {             particle.vy = particle.vy>0 ? vy: -vy;         }         particles.push(particle);     } }   function Particle() {     this.w = this.h = Math.random()*4+1;       this.x = xPoint-this.w/2;     this.y = yPoint-this.h/2;          this.vx = (Math.random()-0.5)*10;     this.vy = (Math.random()-0.5)*10;         this.alpha = Math.random()*.5+.5;          this.color; }   Particle.prototype = {     gravity: 0.05,     move: function () {         this.x += this.vx;         this.vy += this.gravity;         this.y += this.vy;         this.alpha -= 0.01;         if (this.x <= -this.w || this.x >= screen.width ||             this.y >= screen.height ||             this.alpha <= 0) {                 return false;         }         return true;     },     draw: function (c) {         c.save();         c.beginPath();                  c.translate(this.x+this.w/2, this.y+this.h/2);         c.arc(0, 0, this.w, 0, Math.PI*2);         c.fillStyle = this.color;         c.globalAlpha = this.alpha;                  c.closePath();         c.fill();         c.restore();     } } 
复制代码

🍜 二、在农村看到的烟花

演示地址:http://haiyong.site/fireworks2(需要使用电脑打开,没做响应式手机端打开一片黑,或者可以看看后面的烟花)



HTML 代码:


这里的 HTML 代码还是一样的简短


<div id="jsi-fireworks-container" class="container"></div>
复制代码


CSS 代码


css 也只有这三段内容


html, body{    width: 100%;    height: 100%;    margin: 0;    padding: 0;    overflow: hidden;    background-color: #101010;}.container{    position: absolute;    width: 500px;    height: 500px;    top: 50%;    left: 50%;    margin-top: -250px;    margin-left: -250px;}canvas{    position: absolute;    top: 0;    left: 0;}
复制代码


JS 代码


JS 代码比较长,我这里放了一部分,需要完整源码的可以关注公众号【海拥】回复【烟花】


var RENDERER = {  LEAF_INTERVAL_RANGE : {min : 100, max : 200},  FIREWORK_INTERVAL_RANGE : {min : 20, max : 200},  SKY_COLOR : 'hsla(210, 60%, %luminance%, 0.2)',  STAR_COUNT : 100,    init : function(){    this.setParameters();    this.reconstructMethod();    this.createTwigs();    this.createStars();    this.render();  },  setParameters : function(){    this.$container = $('#jsi-fireworks-container');    this.width = this.$container.width();    this.height = this.$container.height();    this.distance = Math.sqrt(Math.pow(this.width / 2, 2) + Math.pow(this.height / 2, 2));    this.contextFireworks = $('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0).getContext('2d');    this.contextTwigs = $('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0).getContext('2d');        this.twigs = [];    this.leaves = [new LEAF(this.width, this.height, this)];    this.stars = [];    this.fireworks = [new FIREWORK(this.width, this.height, this)];        this.leafInterval = this.getRandomValue(this.LEAF_INTERVAL_RANGE) | 0;    this.maxFireworkInterval = this.getRandomValue(this.FIREWORK_INTERVAL_RANGE) | 0;    this.fireworkInterval = this.maxFireworkInterval;  },  reconstructMethod : function(){    this.render = this.render.bind(this);  },  getRandomValue : function(range){    return range.min + (range.max - range.min) * Math.random();  },  createTwigs : function(){    this.twigs.push(new TWIG(this.width, this.height, 0, 0, Math.PI * 3 / 4, 0));    this.twigs.push(new TWIG(this.width, this.height, this.width, 0, -Math.PI * 3 / 4, Math.PI));    this.twigs.push(new TWIG(this.width, this.height, 0, this.height, Math.PI / 4, Math.PI));    this.twigs.push(new TWIG(this.width, this.height, this.width, this.height, -Math.PI / 4, 0));  },  createStars : function(){    for(var i = 0, length = this.STAR_COUNT; i < length; i++){      this.stars.push(new STAR(this.width, this.height, this.contextTwigs, this));    }  },  render : function(){    requestAnimationFrame(this.render);        var maxOpacity = 0,      contextTwigs = this.contextTwigs,      contextFireworks = this.contextFireworks;        for(var i = this.fireworks.length - 1; i >= 0; i--){      maxOpacity = Math.max(maxOpacity, this.fireworks[i].getOpacity());    }    contextTwigs.clearRect(0, 0, this.width, this.height);    contextFireworks.fillStyle = this.SKY_COLOR.replace('%luminance', 5 + maxOpacity * 15);    contextFireworks.fillRect(0, 0, this.width, this.height);        for(var i = this.fireworks.length - 1; i >= 0; i--){      if(!this.fireworks[i].render(contextFireworks)){        this.fireworks.splice(i, 1);      }    }    for(var i = this.stars.length - 1; i >= 0; i--){      this.stars[i].render(contextTwigs);    }    for(var i = this.twigs.length - 1; i >= 0; i--){      this.twigs[i].render(contextTwigs);    }    for(var i = this.leaves.length - 1; i >= 0; i--){      if(!this.leaves[i].render(contextTwigs)){        this.leaves.splice(i, 1);      }    }    if(--this.leafInterval == 0){      this.leaves.push(new LEAF(this.width, this.height, this));      this.leafInterval = this.getRandomValue(this.LEAF_INTERVAL_RANGE) | 0;    }    if(--this.fireworkInterval == 0){      this.fireworks.push(new FIREWORK(this.width, this.height, this));      this.maxFireworkInterval = this.getRandomValue(this.FIREWORK_INTERVAL_RANGE) | 0;      this.fireworkInterval = this.maxFireworkInterval;    }  }};
复制代码

🧀 三、可点击的烟花

演示地址:http://haiyong.site/fireworks3



HTML 代码:


<canvas id="canvas"></canvas>
复制代码


CSS 代码


body{  background-color: #000;}
canvas{ display: block; margin: auto; -webkit-tap-highlight-color:rgba(0,0,0,0); -webkit-user-select:none; }
复制代码


完整 JS 代码


(function() {  var Fireworks, GRAVITY, K, SPEED, ToRadian, canvas, context, ctx, fireBoss, repeat, stage;        canvas = document.getElementById("canvas");        context = canvas.getContext("2d");        canvas.width = window.innerWidth;        canvas.height = window.innerHeight;        stage = new createjs.Stage(canvas);        stage.autoClear = false;        ctx = canvas.getContext("2d");        ctx.fillStyle = "rgba(0, 0, 0, 0)";        ctx.fillRect(0, 0, canvas.width, canvas.height);        createjs.Ticker.setFPS(50);        createjs.Touch.enable(stage);        stage.update();
// 重力 GRAVITY = 1;
// 抵抗 K = 0.9;
// 速度 SPEED = 12;
// 从度数转换为弧度 ToRadian = function(degree) { return degree * Math.PI / 180.0; };
// 制作烟花的class Fireworks = class Fireworks { constructor(sx = 100, sy = 100, particles = 70) { var circle, i, j, rad, ref, speed; this.sx = sx; this.sy = sy; this.particles = particles; this.sky = new createjs.Container(); this.r = 0; this.h = Math.random() * 360 | 0; this.s = 100; this.l = 50; this.size = 3; for (i = j = 0, ref = this.particles; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) { speed = Math.random() * 12 + 2; circle = new createjs.Shape(); circle.graphics.f(`hsla(${this.h}, ${this.s}%, ${this.l}%, 1)`).dc(0, 0, this.size); circle.snapToPixel = true; circle.compositeOperation = "lighter"; rad = ToRadian(Math.random() * 360 | 0); circle.set({ x: this.sx, y: this.sy, vx: Math.cos(rad) * speed, vy: Math.sin(rad) * speed, rad: rad }); this.sky.addChild(circle); } stage.addChild(this.sky); }
explode() { var circle, j, p, ref; if (this.sky) { ++this.h; for (p = j = 0, ref = this.sky.getNumChildren(); (0 <= ref ? j < ref : j > ref); p = 0 <= ref ? ++j : --j) { circle = this.sky.getChildAt(p); // 加速度 circle.vx = circle.vx * K; circle.vy = circle.vy * K; // 位置计算 circle.x += circle.vx; circle.y += circle.vy + GRAVITY;
this.l = Math.random() * 100; // 粒度 this.size = this.size - 0.001; if (this.size > 0) { circle.graphics.c().f(`hsla(${this.h}, 100%, ${this.l}%, 1)`).dc(0, 0, this.size); } } if (this.sky.alpha > 0.1) { this.sky.alpha -= K / 50; } else { stage.removeChild(this.sky); this.sky = null; } } else { } } };
fireBoss = [];
setInterval(function() { ctx.fillStyle = "rgba(0, 0, 0, 0.1)"; ctx.fillRect(0, 0, canvas.width, canvas.height); }, 40);
setInterval(function() { var x, y; x = Math.random() * canvas.width | 0; y = Math.random() * canvas.height | 0; fireBoss.push(new Fireworks(x, y)); return fireBoss.push(new Fireworks(x, y)); }, 1300);
repeat = function() { var fireworks, j, ref; for (fireworks = j = 0, ref = fireBoss.length; (0 <= ref ? j < ref : j > ref); fireworks = 0 <= ref ? ++j : --j) { if (fireBoss[fireworks].sky) { fireBoss[fireworks].explode(); } } stage.update(); };
createjs.Ticker.on("tick", repeat);
stage.addEventListener("stagemousedown", function() { fireBoss.push(new Fireworks(stage.mouseX, stage.mouseY)); return fireBoss.push(new Fireworks(stage.mouseX, stage.mouseY)); }); }).call(this);
复制代码

🍖 四、3D 旋转烟花

演示地址:http://haiyong.site/fireworks4



HTML 代码:


<canvas id="canvas"></canvas>
复制代码


CSS 代码


html,body{  margin:0px;  width:100%;  height:100%;  overflow:hidden;  background:#000;}
#canvas{ width:100%; height:100%;}
复制代码


部分 JS 代码


JS 代码比较长我就不全列出来了,需要完整源码可以翻到文章末尾下载本篇文章所有源码


 function initVars(){
pi=Math.PI; ctx=canvas.getContext("2d"); canvas.width=canvas.clientWidth; canvas.height=canvas.clientHeight; cx=canvas.width/2; cy=canvas.height/2; playerZ=-25; playerX=playerY=playerVX=playerVY=playerVZ=pitch=yaw=pitchV=yawV=0; scale=600; seedTimer=0;seedInterval=5,seedLife=100;gravity=.02; seeds=new Array(); sparkPics=new Array(); s="https://cantelope.org/NYE/"; for(i=1;i<=10;++i){ sparkPic=new Image(); sparkPic.src=s+"spark"+i+".png"; sparkPics.push(sparkPic); } sparks=new Array(); pow1=new Audio(s+"pow1.ogg"); pow2=new Audio(s+"pow2.ogg"); pow3=new Audio(s+"pow3.ogg"); pow4=new Audio(s+"pow4.ogg"); frames = 0;}
function rasterizePoint(x,y,z){
var p,d; x-=playerX; y-=playerY; z-=playerZ; p=Math.atan2(x,z); d=Math.sqrt(x*x+z*z); x=Math.sin(p-yaw)*d; z=Math.cos(p-yaw)*d; p=Math.atan2(y,z); d=Math.sqrt(y*y+z*z); y=Math.sin(p-pitch)*d; z=Math.cos(p-pitch)*d; var rx1=-1000,ry1=1,rx2=1000,ry2=1,rx3=0,ry3=0,rx4=x,ry4=z,uc=(ry4-ry3)*(rx2-rx1)-(rx4-rx3)*(ry2-ry1); if(!uc) return {x:0,y:0,d:-1}; var ua=((rx4-rx3)*(ry1-ry3)-(ry4-ry3)*(rx1-rx3))/uc; var ub=((rx2-rx1)*(ry1-ry3)-(ry2-ry1)*(rx1-rx3))/uc; if(!z)z=.000000001; if(ua>0&&ua<1&&ub>0&&ub<1){ return { x:cx+(rx1+ua*(rx2-rx1))*scale, y:cy+y/z*scale, d:Math.sqrt(x*x+y*y+z*z) }; }else{ return { x:cx+(rx1+ua*(rx2-rx1))*scale, y:cy+y/z*scale, d:-1 }; }}
function spawnSeed(){ seed=new Object(); seed.x=-50+Math.random()*100; seed.y=25; seed.z=-50+Math.random()*100; seed.vx=.1-Math.random()*.2; seed.vy=-1.5;//*(1+Math.random()/2); seed.vz=.1-Math.random()*.2; seed.born=frames; seeds.push(seed);}
复制代码

🍰 五、可拖动视角的自定义烟花

演示地址:http://haiyong.site/fireworks5



HTML 代码:


 <div id="WebGL-output"></div>
复制代码


CSS 代码


body {     margin: 0;     overflow: hidden;     background: -webkit-linear-gradient(0deg, rgb(0, 12, 91), rgb(0, 0, 0));     background: linear-gradient(0deg, rgb(0, 12, 91), rgb(0, 0, 0)); }
复制代码


部分 JS 代码


JS 代码比较长我就不全列出来了,需要完整源码可以参见文章末尾两种获取源码方式


let scene,camera,renderer,orbitControls,planeMesh,canvasTexture,isAutoLaunch = true;
const gravity = new THREE.Vector3(0, -0.005, 0);const friction = 0.998;const noise = new SimplexNoise();const textureSize = 128.0;const fireworksInstances = [];
let outputDom;
const getOffsetXYZ = i => { const offset = 3; const index = i * offset; const x = index; const y = index + 1; const z = index + 2; return { x, y, z };};
const getOffsetRGBA = i => { const offset = 4; const index = i * offset; const r = index; const g = index + 1; const b = index + 2; const a = index + 3; return { r, g, b, a };};
const gui = new dat.GUI();const guiControls = new function () { this.ParticleSize = 300; this.AutoLaunch = true;}();gui.add(guiControls, 'ParticleSize', 100, 600);gui.add(guiControls, 'AutoLaunch').onChange(e => { isAutoLaunch = e; outputDom.style.cursor = isAutoLaunch ? 'auto' : 'pointer';});
const getRandomNum = (max = 0, min = 0) => Math.floor(Math.random() * (max + 1 - min)) + min;
const launchFireWorks = () => { if (fireworksInstances.length > 5) return; const fw = Math.random() > 8 ? new BasicFIreWorks() : new RichFIreWorks(); fireworksInstances.push(fw); scene.add(fw.meshGroup);};
const autoLaunch = () => { if (!isAutoLaunch) return; if (Math.random() > 0.7) launchFireWorks();};
const drawRadialGradation = (ctx, canvasRadius, canvasW, canvasH) => { ctx.save(); const gradient = ctx.createRadialGradient(canvasRadius, canvasRadius, 0, canvasRadius, canvasRadius, canvasRadius); gradient.addColorStop(0.0, 'rgba(255,255,255,1.0)'); gradient.addColorStop(0.5, 'rgba(255,255,255,0.5)'); gradient.addColorStop(1.0, 'rgba(255,255,255,0)'); ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvasW, canvasH); ctx.restore();};
const getTexture = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d');
const diameter = textureSize; canvas.width = diameter; canvas.height = diameter; const canvasRadius = diameter / 2;
drawRadialGradation(ctx, canvasRadius, canvas.width, canvas.height); const texture = new THREE.Texture(canvas); texture.type = THREE.FloatType; texture.needsUpdate = true; return texture;};
canvasTexture = getTexture();
const getPointMesh = (num, vels, type) => {
const bufferGeometry = new THREE.BufferGeometry(); const vertices = []; const velocities = []; const colors = []; const adjustSizes = []; const masses = []; const colorType = Math.random() > 0.3 ? 'single' : 'multiple'; const singleColor = getRandomNum(100, 20) * 0.01; const multipleColor = () => getRandomNum(100, 1) * 0.01; let rgbType; const rgbTypeDice = Math.random(); if (rgbTypeDice > 0.66) { rgbType = 'red'; } else if (rgbTypeDice > 0.33) { rgbType = 'green'; } else { rgbType = 'blue'; } for (let i = 0; i < num; i++) { const pos = new THREE.Vector3(0, 0, 0); vertices.push(pos.x, pos.y, pos.z); velocities.push(vels[i].x, vels[i].y, vels[i].z); if (type === 'seed') { let size; if (type === 'trail') { size = Math.random() * 0.1 + 0.1; } else { size = Math.pow(vels[i].y, 2) * 0.04; } if (i === 0) size *= 1.1; adjustSizes.push(size); masses.push(size * 0.017); colors.push(1.0, 1.0, 1.0, 1.0); } else { const size = getRandomNum(guiControls.ParticleSize, 10) * 0.001; adjustSizes.push(size); masses.push(size * 0.017); if (colorType === 'multiple') { colors.push(multipleColor(), multipleColor(), multipleColor(), 1.0); } else { switch (rgbType) { case 'red': colors.push(singleColor, 0.1, 0.1, 1.0); break; case 'green': colors.push(0.1, singleColor, 0.1, 1.0); break; case 'blue': colors.push(0.1, 0.1, singleColor, 1.0); break; default: colors.push(singleColor, 0.1, 0.1, 1.0);}
} } } bufferGeometry.addAttribute('position', new THREE.Float32BufferAttribute(vertices, 3).setDynamic(true)); bufferGeometry.addAttribute('velocity', new THREE.Float32BufferAttribute(velocities, 3).setDynamic(true)); bufferGeometry.addAttribute('color', new THREE.Float32BufferAttribute(colors, 4).setDynamic(true)); bufferGeometry.addAttribute('adjustSize', new THREE.Float32BufferAttribute(adjustSizes, 1).setDynamic(true)); bufferGeometry.addAttribute('mass', new THREE.Float32BufferAttribute(masses, 1).setDynamic(true));
复制代码


✨ 完整源码下载

考虑到有些小伙伴基础比较薄弱,本篇文章所有源码都已打包好,下载方式有如下三种



1.关注公众号【海拥】回复【烟花】获取

2.通过我的主页获取联系方式:https://www.infoq.cn/u/haiyong,记得备注 InfoQ

发布于: 刚刚阅读数: 3
用户头像

公众号:海拥 2021.11.29 加入

【个人网站】haiyong.site 【联系方式】微信:wh18363 【软件技能】前端,Java,Python 【个人称号】InfoQ写作平台签约作者,华为云享专家,CSDN原力作者,全栈领域优质创作者,掘金2021年度人气作者No.21

评论

发布
暂无评论
快过年了,用五种不同的JS特效带你看烟花