MCBBS Wiki欢迎您共同参与编辑!在参与编辑之前请先阅读Wiki方针

如果在编辑的过程中遇到了什么问题,可以去讨论板提问。

为了您能够无阻碍地参与编辑 未验证/绑定过邮箱的用户,请尽快绑定/验证

MCBBS Wiki GitHub群组已上线!

您可以在回声洞中发表吐槽!

服务器状态监控。点击进入

本站由MCBBS用户自行搭建,与MCBBS及东银河系漫游指南(北京)科技有限公司没有从属关系。点此了解 MCBBS Wiki 不是什么>>

微件:SaltFirework:修订间差异

来自MCBBS Wiki
跳到导航 跳到搜索
(添加了自定义烟花颜色色相,颜色波动范围,粒子数量三种自定义参数)
(使用TS重写,开源在 github.com/mcbbs-wiki/mcbbs-wiki-widget-repo,换了一种烟花粒子扩散的算法,现在粒子不会聚集成一个环了)
 
第8行: 第8行:
* Inspired By: https://codepen.io/jackrugile/pen/acAgx Author Jack Rugile
* Inspired By: https://codepen.io/jackrugile/pen/acAgx Author Jack Rugile
*/
*/
"use strict";
(function () {
(() => {
// 阻止重复调用
// src/utils/utils.ts
if (document.getElementById('saltFireWorkCanvas')) return
function docReady(fn) {
// 参数
if (document.readyState === "loading") {
// 颜色范围
window.addEventListener("DOMContentLoaded", fn);
const hueRange = (function () {
} else {
let defaultValue = (function () {
var x = []
fn();
for (let i = 1; i < 361; i++)
x.push(i)
return x
})()
let e = document.getElementById('saltForeworkHueRange')
if (!e) return defaultValue
let 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
})()// [0, 360]
// 颜色变化区间
const hueDiff = (function () {
let e = document.getElementById('saltForeworkHueDiff')
if (!e) return 30
let c = parseInt(e.textContent)
if (isNaN(c) || c < 0 || c > 180) return 30
return c
})()
// 粒子效果数量
const count = (function () {
let e = document.getElementById('saltForeworkCount')
if (!e) return 110
let c = parseInt(e.textContent)
if (isNaN(c) || c < 1 || c > 500) return 110
return c
})()
const baseRange = [1, 4] // 粒子大小
const speedMutiply = 6 // 粒子速度范围
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', function (e) { createFireworks(e.clientX, e.clientY) }) // 监听事件
}
}
}
/**清理绘图区 */
function clearCanvas() {
function randomChoice(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)
let hue = randomChoice(hueRange)
h -= 360;
for (let i = 0; i < count; i++) {
else if (h < 0)
h += 360;
let spd = Math.random() * speedMutiply + baseSpeed
let p = {
return 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() {
function drawParticles() {
for (let i = 0; i < particles.length; i++) {
if (!particles.length)
return;
let p = particles[i]
context.globalCompositeOperation = "lighter";
if (!p)
continue
for (let i = 0; i < particles.length; i++) {
p.x += Math.cos(p.rad) * p.radius
let p = particles[i];
if (!p)
p.y += Math.sin(p.rad) * p.radius + fallSpeed
continue;
p.radius *= 1 - p.speed / 100
p.alpha -= 0.75
p.x += p.spdX * p.spdFall;
context.beginPath()
p.y += p.spdY * p.spdFall;
p.spdY += fallSpeed;
context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false)
context.closePath()
p.spdFall *= 0.978;
p.alpha -= fadeSpeed;
context.fillStyle = `hsla(${p.hue},100%,${p.bright}%,${p.alpha / 100})`
context.fill()
context.beginPath();
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' // 保留前一刻的图案作为尾迹
context.fillStyle = `rgba(255,255,255,${1 / tail})`
canvas.height = 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 = []
for (let p of particles)
if (zeroFrame >= 30)
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)
}
/**随机选择 */
function randomChoice(arr) {
if (arr.length < 1) {
return null;
}
return arr[Math.floor(Math.random() * arr.length)];
}
})()
</script></includeonly>
</script></includeonly>

2022年7月24日 (日) 20:15的最新版本