微件:SaltFirework:修订间差异

(快过年了)
 
(使用TS重写,开源在 github.com/mcbbs-wiki/mcbbs-wiki-widget-repo,换了一种烟花粒子扩散的算法,现在粒子不会聚集成一个环了)
 
(未显示同一用户的1个中间版本)
第8行: 第8行:
* Inspired By: https://codepen.io/jackrugile/pen/acAgx Author Jack Rugile
* Inspired By: https://codepen.io/jackrugile/pen/acAgx Author Jack Rugile
*/
*/
(function () {
"use strict";
    // 参数
(() => {
    const hueRange = [0, 360] // 颜色范围
  // src/utils/utils.ts
    const hueDiff = 30 // 颜色变化区间
  function docReady(fn) {
    const count = 110 // 粒子效果数量
     if (document.readyState === "loading") {
    const baseRange = [1, 4] // 粒子大小
      window.addEventListener("DOMContentLoaded", fn);
    const speedMutiply = 6 // 粒子速度范围
    } else {
    const radius = 1.7 // 粒子扩散半径比粒子速度
      fn();
    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 clearCanvas() {
  function randomChoice(arr) {
        context.fillStyle = 'rgba(255,255,255,0)'
    if (arr.length < 1) {
        context.fillRect(0, 0, canvas.width, canvas.height)
      return null;
     }
     }
     /**重设绘图区大小*/
     return arr[Math.floor(Math.random() * arr.length)];
     function resizeCanvas() {
  }
        canvas.width = window.innerWidth
 
        canvas.height = window.innerHeight
  // 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() {
     function createFireworks(x, y) {
      let h = Math.floor(rightRandom(hue, hueDiff));
        /**这个烟花的颜色 */
      if (h > 360)
        let hue = Math.floor(Math.random() * (hueRange[1] - hueRange[0])) + hueRange[0]
        h -= 360;
        for (let i = 0; i < count; i++) {
      else if (h < 0)
            let spd = Math.random() * speedMutiply + baseSpeed
        h += 360;
            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)
            let p = particles[i]
      return;
            if (!p)
    context.globalCompositeOperation = "lighter";
                continue
    for (let i = 0; i < particles.length; i++) {
            p.x += Math.cos(p.rad) * p.radius
      let p = particles[i];
            p.y += Math.sin(p.rad) * p.radius + fallSpeed
      if (!p)
            p.radius *= 1 - p.speed / 100
        continue;
            p.alpha -= 0.75
      p.x += p.spdX * p.spdFall;
            context.beginPath()
      p.y += p.spdY * p.spdFall;
            context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false)
      p.spdY += fallSpeed;
            context.closePath()
      p.spdFall *= 0.978;
            context.fillStyle = `hsla(${p.hue},100%,${p.bright}%,${p.alpha / 100})`
      p.alpha -= fadeSpeed;
            context.fill()
      context.beginPath();
            if (p.alpha < 0.75) // 标记已经透明到看不见的粒子
      context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
                particles[i] = null
      context.closePath();
        }
      context.fillStyle = `hsla(${p.hue},100%,${p.bright}%,${p.alpha / 100})`;
        if (lastLength == 0 && particles.length == 0) {
      context.fill();
            zeroFrame += 1
      //! 标记已经透明到看不见的粒子
            if (zeroFrame == 30) // 连续30帧没有粒子
      if (p.alpha < fadeSpeed)
                canvas.height = window.innerHeight // 利用画布重设大小清除内容的特性来清理里面的东西
        particles[i] = null;
        }
        else { zeroFrame = 0 }
        lastLength = particles.length
     }
     }
     /**画出尾迹 */
     if (lastLength === 0 && particles.length === 0) {
    function drawTail() {
      zeroFrame += 1;
        context.globalCompositeOperation = 'destination-out' // 保留前一刻的图案作为尾迹
      if (zeroFrame === 30)
        context.fillStyle = `rgba(255,255,255,${1 / tail})`
         canvas.height = window.innerHeight;
         context.fillRect(0, 0, canvas.width, canvas.height)
    } else {
        context.globalCompositeOperation = 'lighter'
      zeroFrame = 0;
     }
     }
     /**清理已经消失的粒子*/
    lastLength = particles.length;
     function clearParticles() {
  }
        let cp = []
  function drawTail() {
        for (let p of particles)
    if (zeroFrame >= 30)
            if (p)
      return;
                cp.push(p)
     //! 保留前一刻的图案作为尾迹
         particles = cp
    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);
    function tick() {
  }
        // 画尾迹 -> 画这一帧的粒子 -> 删除运算完毕的粒子
})();
        drawTail()
        drawParticles()
        clearParticles()
        requestAnimationFrame(tick)
    }
})()
</script></includeonly>
</script></includeonly>

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