3,389
个编辑
MCBBS Wiki欢迎您共同参与编辑!在参与编辑之前请先阅读Wiki方针。
如果在编辑的过程中遇到了什么问题,可以去讨论板提问。
为了您能够无阻碍地参与编辑 未验证/绑定过邮箱的用户,请尽快绑定/验证。
MCBBS Wiki GitHub群组已上线!
您可以在回声洞中发表吐槽!
服务器状态监控。点击进入
本站由MCBBS用户自行搭建,与MCBBS及东银河系漫游指南(北京)科技有限公司没有从属关系。点此了解 MCBBS Wiki 不是什么>>
Salt lovely(留言 | 贡献) 小 (添加了自定义烟花颜色色相,颜色波动范围,粒子数量三种自定义参数) |
Salt lovely(留言 | 贡献) 小 (使用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"; | |||
(() => { | |||
// src/utils/utils.ts | |||
function docReady(fn) { | |||
if (document.readyState === "loading") { | |||
window.addEventListener("DOMContentLoaded", fn); | |||
} else { | |||
fn(); | |||
} | } | ||
} | |||
function randomChoice(arr) { | |||
if (arr.length < 1) { | |||
return null; | |||
} | } | ||
/ | return arr[Math.floor(Math.random() * arr.length)]; | ||
function resizeCanvas() { | } | ||
// widget/SaltFirework/widget.ts | |||
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)); | |||
if (h > 360) | |||
h -= 360; | |||
else if (h < 0) | |||
h += 360; | |||
return h; | |||
} | } | ||
} | |||
function drawParticles() { | |||
if (!particles.length) | |||
return; | |||
context.globalCompositeOperation = "lighter"; | |||
for (let i = 0; i < particles.length; i++) { | |||
let p = particles[i]; | |||
if (!p) | |||
continue; | |||
p.x += p.spdX * p.spdFall; | |||
p.y += p.spdY * p.spdFall; | |||
p.spdY += fallSpeed; | |||
p.spdFall *= 0.978; | |||
p.alpha -= fadeSpeed; | |||
context.beginPath(); | |||
context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false); | |||
context.closePath(); | |||
context.fillStyle = `hsla(${p.hue},100%,${p.bright}%,${p.alpha / 100})`; | |||
context.fill(); | |||
//! 标记已经透明到看不见的粒子 | |||
if (p.alpha < fadeSpeed) | |||
particles[i] = null; | |||
} | } | ||
if (lastLength === 0 && particles.length === 0) { | |||
zeroFrame += 1; | |||
if (zeroFrame === 30) | |||
canvas.height = window.innerHeight; | |||
} else { | |||
zeroFrame = 0; | |||
} | } | ||
/ | lastLength = particles.length; | ||
function clearParticles() { | } | ||
function drawTail() { | |||
if (zeroFrame >= 30) | |||
return; | |||
//! 保留前一刻的图案作为尾迹 | |||
particles = | context.globalCompositeOperation = "destination-out"; | ||
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); | |||
} | |||
})(); | |||
})() | |||
</script></includeonly> | </script></includeonly> | ||