微件:SaltFirework:修订间差异

添加628字节 、​ 2022年7月24日 (星期日)
使用TS重写,开源在 github.com/mcbbs-wiki/mcbbs-wiki-widget-repo,换了一种烟花粒子扩散的算法,现在粒子不会聚集成一个环了
(快过年了)
 
(使用TS重写,开源在 github.com/mcbbs-wiki/mcbbs-wiki-widget-repo,换了一种烟花粒子扩散的算法,现在粒子不会聚集成一个环了)
 
(未显示同一用户的1个中间版本)
* Inspired By: https://codepen.io/jackrugile/pen/acAgx Author Jack Rugile
*/
"use strict";
(function () {
(() => {
// 参数
// src/utils/utils.ts
const hueRange = [0, 360] // 颜色范围
function docReady(fn) {
const hueDiff = 30 // 颜色变化区间
if (document.readyState === "loading") {
const count = 110 // 粒子效果数量
window.addEventListener("DOMContentLoaded", fn);
const baseRange = [1, 4] // 粒子大小
} else {
const speedMutiply = 6 // 粒子速度范围
fn();
const radius = 1.7 // 粒子扩散半径比粒子速度
const baseSpeed = 0.2 // 粒子最低速度
const fallSpeed = 1.1 // 粒子下坠速度
const tail = 15 // 尾迹
/**创建canvas */
let canvas = document.createElement('canvas')
/**获取canvas绘图区域 */
let context = canvas.getContext('2d')
/**烟花粒子数组 */
let particles = []
/**记录剩余粒子数量,在合适的时机清理画布 */
let lastLength = 0, zeroFrame = 0
init() // 初始化
function init() {
canvas.id = 'saltFireWorkCanvas'
canvas.style.left = '0'
canvas.style.top = '0'
canvas.style.position = 'fixed'
canvas.style.pointerEvents = 'none' // 不干扰鼠标
canvas.style.zIndex = '99999' // 置于顶层
document.body.appendChild(canvas)
resizeCanvas()
window.addEventListener('resize', resizeCanvas, false)
clearCanvas()
tick() // 开始tick
document.addEventListener('mousedown', (e) => { createFireworks(e.clientX, e.clientY) }) // 监听事件
}
}
/**清理绘图区 */
function clearCanvasrandomChoice(arr) {
if (arr.length < 1) {
context.fillStyle = 'rgba(255,255,255,0)'
return null;
context.fillRect(0, 0, canvas.width, canvas.height)
}
return arr[Math.floor(Math.random() * arr.length)];
/**重设绘图区大小*/
}
function resizeCanvas() {
 
canvas.width = window.innerWidth
// widget/SaltFirework/widget.ts
canvas.height = window.innerHeight
if (document.getElementById("saltFireWorkCanvas"))
throw new Error("同一页面中只能有一个烟花");
//! 颜色范围
var hueRange;
//! 颜色变化区间
var hueDiff;
//! 粒子效果数量
var count;
var baseRange = [1, 4];
var baseSpeed = [0.3, 2, 3];
var fallSpeed = 1.1 / 60;
var fadeSpeed = 0.65;
var tail = 15;
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var particles = [];
var lastLength = 0;
var zeroFrame = 0;
docReady(init);
function init() {
const getValue = (id, defaultValue, min, max) => {
const e = document.getElementById(id);
if (!e)
return defaultValue;
const c = parseInt(e.textContent || "");
if (isNaN(c) || c < min || c > max)
return defaultValue;
return c;
};
hueRange = (() => {
const defaultValue = (() => {
var x = [];
for (let i = 1; i < 361; i++)
x.push(i);
return x;
})();
const e = document.getElementById("saltFireworkHueRange");
if (!e)
return defaultValue;
const c = (e.textContent || "").replace(/[\s\n_]+/g, "").replace(/[\;\/\|\/\\,;、\-]+/g, ",").split(",").map((v) => parseInt(v)).filter(Boolean).filter((v) => v > 0 && v < 361);
if (!c || c.length < 1 || c.length > 360)
return defaultValue;
return c;
})();
hueDiff = getValue("saltFireworkHueDiff", 30, 0, 180);
count = getValue("saltFireworkCount", 110, 1, 500);
canvas.id = "saltFireWorkCanvas";
canvas.style.left = "0";
canvas.style.top = "0";
canvas.style.position = "fixed";
canvas.style.pointerEvents = "none";
canvas.style.zIndex = "99999";
document.body.appendChild(canvas);
resizeCanvas();
window.addEventListener("resize", resizeCanvas, false);
tick();
document.addEventListener("mousedown", function(e) {
createFireworks(e.clientX, e.clientY);
});
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
function rightRandom(base, size) {
return base + (Math.random() * size - Math.random() * size) / 2;
}
function createFireworks(x, y) {
let hue = randomChoice(hueRange);
for (let i = 0; i < count; i++) {
const spd = rightRandom((baseSpeed[1] + baseSpeed[0]) / 2, baseSpeed[1] - baseSpeed[0]);
const rad = Math.random() * 2 * Math.PI;
particles.push({
x,
y,
spdX: Math.cos(rad) * spd,
spdY: Math.sin(rad) * spd,
spdFall: baseSpeed[2],
size: rightRandom((baseRange[1] + baseRange[0]) / 2, baseRange[1] - baseRange[0]),
hue: hueRandom(),
bright: rightRandom(72, 16),
alpha: rightRandom(75, 30)
});
}
function hueRandom() {
/**创建烟花*/
let h = Math.floor(rightRandom(hue, hueDiff));
function createFireworks(x, y) {
if (h /**这个烟花的颜色> */360)
h -= 360;
let hue = Math.floor(Math.random() * (hueRange[1] - hueRange[0])) + hueRange[0]
else forif (leth i =< 0; i < count; i++) {
h += 360;
let spd = Math.random() * speedMutiply + baseSpeed
return let p = {h;
rad: Math.random() * 2 * Math.PI, // 弧度
x: x,
y: y,
speed: spd,
radius: spd * radius,
size: Math.floor(Math.random() * (baseRange[1] - baseRange[0])) + baseRange[0],
hue: hueRandom(),
bright: Math.floor(Math.random() * 16) + 65,
alpha: Math.floor(Math.random() * 51) + 50,
}
particles.push(p)
}
function hueRandom() {
let h = Math.floor(Math.random() * hueDiff + hue - hueDiff)
if (h > 360)
h -= 360
else if (h < 0)
h += 360
return h
}
}
}
/**主过程*/
function drawParticles() {
forif (let i = 0; i < !particles.length; i++) {
return;
let p = particles[i]
context.globalCompositeOperation = "lighter";
if (!p)
for (let i = 0; i < particles.length; i++) continue{
let p.x += Math.cos(p.rad) * p.radiusparticles[i];
if (!p)
p.y += Math.sin(p.rad) * p.radius + fallSpeed
continue;
p.radius *= 1 - p.speed / 100
p.x += p.alphaspdX -=* 0p.75spdFall;
p.y += p.spdY * contextp.beginPath()spdFall;
p.spdY += fallSpeed;
context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false)
p.spdFall *= context0.closePath()978;
p.alpha -= fadeSpeed;
context.fillStyle = `hsla(${p.hue},100%,${p.bright}%,${p.alpha / 100})`
context.fillbeginPath();
context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
if (p.alpha < 0.75) // 标记已经透明到看不见的粒子
context.closePath();
particles[i] = null
context.fillStyle = `hsla(${p.hue},100%,${p.bright}%,${p.alpha / 100})`;
}
context.fill();
if (lastLength == 0 && particles.length == 0) {
//! 标记已经透明到看不见的粒子
zeroFrame += 1
if (p.alpha < fadeSpeed)
if (zeroFrame == 30) // 连续30帧没有粒子
particles[i] = null;
canvas.height = window.innerHeight // 利用画布重设大小清除内容的特性来清理里面的东西
}
else { zeroFrame = 0 }
lastLength = particles.length
}
if (lastLength === 0 && particles.length === 0) {
/**画出尾迹 */
zeroFrame += 1;
function drawTail() {
if (zeroFrame === 30)
context.globalCompositeOperation = 'destination-out' // 保留前一刻的图案作为尾迹
contextcanvas.fillStyleheight = `rgba(255,255,255,${1 / tail})`window.innerHeight;
} else {
context.fillRect(0, 0, canvas.width, canvas.height)
zeroFrame = 0;
context.globalCompositeOperation = 'lighter'
}
lastLength = particles.length;
/**清理已经消失的粒子*/
}
function clearParticles() {
function drawTail() {
let cp = []
forif (let pzeroFrame of>= particles30)
if (p)return;
//! 保留前一刻的图案作为尾迹
cp.push(p)
context.globalCompositeOperation = "destination-out";
particles = cp
context.fillStyle = `rgba(0,0,0,${1 / tail})`;
context.fillRect(0, 0, canvas.width, canvas.height);
}
function clearParticles() {
if (!particles.length)
return;
let cp = [];
for (let p of particles)
if (p)
cp.push(p);
if (cp.length !== particles.length)
particles = cp;
}
function tick() {
//! 画尾迹 -> 画这一帧的粒子 -> 删除运算完毕的粒子
drawTail();
drawParticles();
clearParticles();
if (false) {
const el = document.getElementById("saltFWInfo");
if (el) {
el.innerHTML = particles.map((p) => JSON.stringify(p)).join("<br/>");
}
}
requestAnimationFrame(tick);
/**高频调用*/
}
function tick() {
})();
// 画尾迹 -> 画这一帧的粒子 -> 删除运算完毕的粒子
drawTail()
drawParticles()
clearParticles()
requestAnimationFrame(tick)
}
})()
</script></includeonly>