<template>
<div class="fire-view">
<canvas id="fireworks"></canvas>
</div>
</template>
<script>
export default {
mounted() {
this.init()
// this.drawLine()
},
methods: {
// 模拟向上发射的过程(这个是测试看效果用)
drawLine() {
// 基础信息
var W = window.innerWidth,
H = window.innerHeight,
canvas = document.getElementById("fireworks"),
context = canvas.getContext("2d");
canvas.width = W;
canvas.height = H;
// 保证canvas宽高和屏幕一致
if (W != window.innerWidth) {
canvas.width = W = window.innerWidth;
}
if (H != window.innerHeight) {
canvas.height = H = window.innerHeight;
}
var rb = new Rocket(200, H);
// 1秒刷新60次 动画帧率(屏幕刷新率)保持在60次/秒保障动画效果的流畅性
setInterval(loop, 1000 / 60);
function loop(){
context.fillStyle = "rgba(0, 0, 0, 0.05)";
context.fillRect(0, 0, W, H);
if(!rb.end){
rb.update();
}else{
// x: [0, W]随机数 y: 高度
rb = new Rocket(Math.random() * W, H);
}
}
function Rocket(x, y){
// x: 燃放为止, y: 烟花高度
this.pos = {
x: x,
y: y
};
this.size = 8; // 半径(单个的,不是指整个烟花半径)
this.step = 1.9; // 速度
this.end = false; // 停止向上
this.update = function(){
if(this.end)return;
this.pos.x += this.step * 0.0; // 改变x的位置,模拟烟花倾斜度
this.pos.y -= this.step; // TODO
if(this.pos.y < 0 || this.pos.x > W)this.end = true;
this.render();
};
this.render = function(){
if(this.end)return;
var c = context;
c.save(); // 保存canvas的状态
// 绘图方式globalCompositeOperation
// Canvas的 globalCompositeOperation 属性决定下一次绘图时如何将一个源(新的)图像绘制到目标(已有)的图像上。此处设置为 \'lighter\' 使同时显示源图像和目标图像,否则会影响到循环中的画布重绘。
c.globalCompositeOperation = 'lighter';
var x = this.pos.x, y = this.pos.y, r = this.size;
// 颜色径向渐变: createRadialGradient(渐变的开始圆的x坐标, 渐变的开始圆的y坐标, 开始圆的半径, 渐变的结束圆的x坐标, 渐变的结束圆的y坐标, 结束圆的半径)
// 其中 createRadialGradient() 函数的第1、2个参数与第4、5个参数分别表示渐变开始和渐变结束的圆心坐标。在这里设置为相同的值,使颜色过渡效果更自然真实。
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
// addColorStop(介于0.0与1.0之间的值表示渐变中开始与结束之间的位置, color在结束位置显示的颜色值)
gradient.addColorStop(0.1, "rgba(255, 255, 255 ," + 1 + ")");
gradient.addColorStop(1, "rgba(0, 0, 0, " + 1 + ")");
c.fillStyle = gradient;
//beginPath 开始一段新的路径
c.beginPath();
c.arc(x, y, r, 0, Math.PI * 2, true);
// closePath()方法与之前有没有beginPath()无关,如果之前画出了一些路径,closePath()将最近画出的一条路径闭合
c.closePath();
c.fill();
c.restore(); // 恢复画布
};
}
},
// 参考 https://codesandbox.io/embed/canvas-fireworks-65923?fontsize=14&hidenavigation=1&theme=dark
init() {
// 获取屏幕宽高
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight,
mousePos = { // 烟花初始为止
x: 400,
y: 300
},
// 初始化canvas
canvas = document.getElementById("fireworks"),
context = canvas.getContext("2d"),
particles = [],
rockets = [],
MAX_PARTICLES = 400,
colorCode = 0;
// init
window.onload = function () {
canvas.width = SCREEN_WIDTH;
canvas.height = SCREEN_HEIGHT;
setInterval(launch, 800);
// 1秒刷新60次 动画帧率(屏幕刷新率)保持在60次/秒保障动画效果的流畅性
setInterval(loop, 1000 / 60);
};
// 监听鼠标移动
window.document.onmousemove = function (e) {
e.preventDefault();
mousePos = {
x: e.clientX,
y: e.clientY
};
};
// 鼠标点击处发射烟花
window.document.onmousedown = function (e) {
for (var i = 0; i < 5; i++) {
launchFrom((Math.random() * SCREEN_WIDTH * 2) / 3 + SCREEN_WIDTH / 6);
}
};
// 循环创建
function launch() {
launchFrom(mousePos.x);
}
// 创建多个烟花
function launchFrom(x) {
if (rockets.length < 10) {
var rocket = new Rocket(x);
rocket.explosionColor = Math.floor((Math.random() * 360) / 10) * 10;
rocket.vel.y = Math.random() * -3 - 4;
rocket.vel.x = Math.random() * 6 - 3;
rocket.size = 8;
rocket.shrink = 0.999;
rocket.gravity = 0.01;
rockets.push(rocket);
}
}
function loop() {
// 保证canvas宽高和屏幕一致 更新屏幕大小
if (SCREEN_WIDTH !== window.innerWidth) {
canvas.width = SCREEN_WIDTH = window.innerWidth;
}
if (SCREEN_HEIGHT !== window.innerHeight) {
canvas.height = SCREEN_HEIGHT = window.innerHeight;
}
// 清除canvas
context.fillStyle = "rgba(0, 0, 0, 0.05)";
context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
var existingRockets = [];
for (var i = 0; i < rockets.length; i++) {
// update and render
rockets[i].update();
rockets[i].render(context);
// calculate distance with Pythagoras
var distance = Math.sqrt(
Math.pow(mousePos.x - rockets[i].pos.x, 2) +
Math.pow(mousePos.y - rockets[i].pos.y, 2)
);
// random chance of 1% if rockets is above the middle
var randomChance =
rockets[i].pos.y < (SCREEN_HEIGHT * 2) / 3
? Math.random() * 100 <= 1
: false;
// 爆炸规则
// -屏幕的80%
// -正在下降
// -靠近鼠标
// -随机爆炸的概率为1%
if (
rockets[i].pos.y < SCREEN_HEIGHT / 5 ||
rockets[i].vel.y >= 0 ||
distance < 50 ||
randomChance
) {
rockets[i].explode();
} else {
existingRockets.push(rockets[i]);
}
}
rockets = existingRockets;
var existingParticles = [];
for (var i = 0; i < particles.length; i++) {
particles[i].update();
// 渲染和保存可以渲染的粒子
if (particles[i].exists()) {
particles[i].render(context);
existingParticles.push(particles[i]);
}
}
// 使用现有粒子更新阵列-旧粒子应被垃圾收集
particles = existingParticles;
while (particles.length > MAX_PARTICLES) {
particles.shift(); // 删掉第一个
}
}
// 爆炸的粒子效果
function Particle(pos) {
this.pos = {
x: pos ? pos.x : 0,
y: pos ? pos.y : 0
};
this.vel = {
x: 0,
y: 0
};
this.shrink = 0.97;
this.size = 2;
this.resistance = 1;
this.gravity = 0;
this.flick = false;
this.alpha = 1; // 透明度
this.fade = 0; // 透明度褪到几
this.color = 0;
}
Particle.prototype.update = function () {
// 施加阻力
this.vel.x *= this.resistance;
this.vel.y *= this.resistance;
// 重力下降
this.vel.y += this.gravity;
// 基于速度更新位置
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
// 收缩
this.size *= this.shrink;
// 淡出
this.alpha -= this.fade;
};
Particle.prototype.render = function (c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = "lighter";
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
// 颜色径向渐变: createRadialGradient(渐变的开始圆的x坐标, 渐变的开始圆的y坐标, 开始圆的半径, 渐变的结束圆的x坐标, 渐变的结束圆的y坐标, 结束圆的半径)
// 其中 createRadialGradient() 函数的第1、2个参数与第4、5个参数分别表示渐变开始和渐变结束的圆心坐标。在这里设置为相同的值,使颜色过渡效果更自然真实。
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
// 烟花颜色渐变
// addColorStop(介于0.0与1.0之间的值表示渐变中开始与结束之间的位置, color在结束位置显示的颜色值)
gradient.addColorStop(0.1, "rgba(255,255,255," + this.alpha + ")");
gradient.addColorStop(
0.8,
"hsla(" + this.color + ", 100%, 50%, " + this.alpha + ")"
);
gradient.addColorStop(1, "hsla(" + this.color + ", 100%, 50%, 0.1)");
c.fillStyle = gradient;
//beginPath 开始一段新的路径
c.beginPath();
c.arc(
this.pos.x,
this.pos.y,
this.flick ? Math.random() * this.size : this.size,
0,
Math.PI * 2,
true
);
c.closePath();
c.fill();
c.restore(); // 恢复画布
};
// 判断是否存在
Particle.prototype.exists = function () {
return this.alpha >= 0.1 && this.size >= 1;
};
// 向上发射的效果
function Rocket(x) {
Particle.apply(this, [ { x: x, y: SCREEN_HEIGHT } ]);
this.explosionColor = 0;
}
Rocket.prototype = new Particle();
Rocket.prototype.constructor = Rocket;
// 爆炸
Rocket.prototype.explode = function () {
var count = Math.random() * 10 + 80;
for (var i = 0; i < count; i++) {
var particle = new Particle(this.pos);
var angle = Math.random() * Math.PI * 2;
// emulate 3D effect by using cosine and put more particles in the middle
var speed = Math.cos((Math.random() * Math.PI) / 2) * 15;
particle.vel.x = Math.cos(angle) * speed;
particle.vel.y = Math.sin(angle) * speed;
particle.size = 10;
particle.gravity = 0.2;
particle.resistance = 0.92;
particle.shrink = Math.random() * 0.05 + 0.93;
particle.flick = true;
particle.color = this.explosionColor;
particles.push(particle);
}
};
// 渲染
Rocket.prototype.render = function (c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = "lighter";
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255, 255, 255 ," + this.alpha + ")");
gradient.addColorStop(1, "rgba(0, 0, 0, " + this.alpha + ")");
c.fillStyle = gradient;
c.beginPath();
c.arc(
this.pos.x,
this.pos.y,
this.flick ? (Math.random() * this.size) / 2 + this.size / 2 : this.size,
0,
Math.PI * 2,
true
);
c.closePath();
c.fill();
c.restore();
};
}
}
}
</script>
<style lang="less" scoped>
.fire-view {
background: #000;
width: 100vw;
height: 100vh;
overflow: hidden;
canvas {
position: absolute;
z-index: 0;
}
}
</style>
canvas烟花效果-学习自用
最新推荐文章于 2024-07-25 22:24:14 发布