🧐别人的博客中有这样的效果,于是自己就尝试实现了一下。
效果如图
源码如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./assets/global.css">
<style>
.blastParticle {
position: absolute;
transform: translate(-50%, -50%);
pointer-events: none;
}
body {
overflow: hidden;
}
.hint-text {
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
user-select: none;
}
</style>
</head>
<body>
<div class="hint-text">
点击鼠标左键,查看粒子效果
</div>
<script type="module">
import { Randoms } from "https://gcore.jsdelivr.net/npm/@3r/tool/lib/randoms.js";
import { Animation } from "https://gcore.jsdelivr.net/npm/@3r/tool/lib/animation.js";
import { Maths } from "https://gcore.jsdelivr.net/npm/@3r/tool/lib/maths.js";
/**
* 爆炸粒子效果
*/
function blastParticle(params = {}) {
let { left = 0, top = 0, width = 200, height = 200, count = 20, rr = [10, 20], duration = 1 } = params
// 通过recovery拾取回收canvas 减少dom产生量
let canvas = document.querySelector('canvas.blastParticle.recovery') || document.createElement("canvas")
canvas.classList.remove('recovery')
let ctx = canvas.getContext('2d')
canvas.width = width;
canvas.height = height;
canvas.classList.add('blastParticle')
canvas.style.left = left + 'px'
canvas.style.top = top + 'px'
let cx = width / 2
let cy = height / 2
class Particle {
constructor({ x, y, r, tx, ty, ts, color, duration }) {
this.x = x;
this.y = y;
this.r = r;
this.tx = tx;
this.ty = ty;
this.ts = ts;
this.color = color
this.timestemp = +new Date()
this.update = () => {
if ((+new Date() - this.timestemp) > 1000 * duration) return;
let timeProportion = (+new Date() - this.timestemp) / (1000 * duration)
let proportion = Animation.easeOut(timeProportion)
this.x = x + tx * proportion
this.y = y + ty * proportion
this.r = r * (1 - timeProportion)
ctx.fillStyle = color
ctx.beginPath()
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2)
ctx.closePath();
ctx.fill()
}
}
}
// 记录开始时间 用于停止渲染标记
let startTimestemp = +new Date()
function renderer(particles) {
ctx.clearRect(0, 0, width, height)
for (const p of particles) {
p.update()
}
if (+new Date() - startTimestemp < duration * 1000) {
console.log("渲染中");
requestAnimationFrame(() => { renderer(particles) })
}
else {
console.log('渲染结束');
canvas.classList.add('recovery');
}
}
// 粒子列表
let particleList = []
for (let i = 0; i < count; i++) {
// 随机方向
let tx = 2 * Math.random() - 1
let ty = 2 * Math.random() - 1
// 乘以半径
tx *= cx;
ty *= cy;
let particle = new Particle({ x: cx, y: cy, r: Randoms.getRandomInt(...rr), tx, ty, ts: 0, duration, color: Randoms.getRandomColor() })
particleList.push(particle)
}
// 执行渲染
renderer(particleList)
document.body.appendChild(canvas)
}
document.addEventListener("click", (ev) => {
blastParticle({ left: ev.x, top: ev.y, duration: .5 })
})
</script>
</body>
</html>