程序员过圣诞 | 用HTML写出绽放的烟花

news2024/10/7 14:26:54

文章目录

    • 一、前言
    • 二、创意名
    • 三、效果展示
    • 四、烟花代码
    • 五、总结


一、前言

2022年圣诞节到来啦,圣诞节是基督教纪念耶稣诞生的重要节日。亦称耶稣圣诞节、主降生节,天主教亦称耶稣圣诞瞻礼。耶稣诞生的日期,《圣经》并无记载。公元336年罗马教会开始在12月25日过此节。12月25日原是罗马帝国规定的太阳神诞辰。有人认为选择这天庆祝圣诞,是因为基督教徒认为耶稣就是正义、永恒的太阳。5世纪中叶以后,圣诞节作为重要节日,成了教会的传统,并在东西派教会中逐渐传开。因所用历法不同等原因,各教派会举行庆祝的具体日期和活动形式也有差别。圣诞节习俗传播到亚洲主要是在十九世纪中叶,日本、韩国等都受到了圣诞文化的影响。现在西方在圣诞节常互赠礼物,举行欢宴,并以圣诞老人、圣诞树等增添节日气氛,已成为普遍习俗。圣诞节也成为西方世界以及其他很多地区的公共假日。

二、创意名

圣诞节就要到了,本篇我们将用html+js写一个动态的烟花代码,程序员的浪漫这不就来了吗,感兴趣的小伙伴可下载学习,安静的在家中读懂它的那一刻,在这疫情肆虐的日子里也是一件很不错的事,将有趣的东西分享给你,希望你度过一个愉快的圣诞节!

三、效果展示

基础版动画效果,有声音

在这里插入图片描述
进阶版动画效果,可点击绽放烟花

在这里插入图片描述

四、烟花代码

动画主要由焰火类,背景图和随机函数等组成。进阶版可点击屏幕直接开始绽放烟花。
以下是HTML部分,完整代码及效果点击下载👉烟花代码
进阶版👉烟花代码2

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>动态烟花代码</title>
    <style>
        html,
        * {
            margin: 0;
            padding: 0
        }

        body {
            background-image: url(bg.png);
            background-size: 100% 100%;
            background-size: 100%; 
            background-repeat:no-repeat;
        }

        .demo {
            margin: 0 auto;
            width: 100%;
            height: 100%;
        }

        h1 {
            margin: 150px auto 30px auto;
            text-align: center;
            font-family: 'Roboto';
        }
    </style>
</head>

<body>
    <div class="demo">
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
    <script src="fireworks.js"></script>
    <script>
        $('.demo').fireworks({
            sound: true,
            opacity: 0.6,
            width: '100%',
            height: '100%'
        });
    </script>

</body>

</html>

js部分

// 焰火集合类
class Fireworks {
    _timer = null
    _animater = null
    _useAnimationFrame = true
    
    ctx = null // 画布上下文,都画这上面
    offScreenCtx = null // 离屏 canvas,优化性能
    fps = 60 // 帧率控制
    fireworks = [] // 焰火数组
    fireworkCount = 8 // 焰火数量
    fireworkInterval = 400 // 焰火爆炸间隔💥
    fireworkColors = DEFAULT_COLORS // 焰火颜色随机取值数组
    particleOptions = { // 粒子配置
      size: 15, // 几块钱的烟花
      speed: 15, // 燃烧的速度
      gravity: 0.08, // 🌍 地球的引力,向下的
      power: 0.93, // 动力,值越大冲越远
      shrink: 0.97, // 燃料消耗的速度
      jitter: 1, // 摇摇晃摇
      color: 'hsla(210, 100%, 50%, 1)', // 颜色
    }
  
    constructor(dom, options = {}) {
      if (!(dom instanceof HTMLElement)) {
        options = dom || {}
      }
  
      if (!dom) {
        dom = document.body
      }
  
      this.initCanvas(dom)
  
      const { particleOptions = {}, ...others } = options
      this.particleOptions = { ...this.particleOptions, ...particleOptions }
      Object.keys(others).forEach(key => this[key] = others[key])
  
      this._useAnimationFrame = this.fps >= 60
    }
  
    // 初始化画布
    initCanvas(dom) {
      let canvas = dom
  
      const isCanvas = canvas.nodeName.toLowerCase() === 'canvas'
      if (!isCanvas) {
        canvas = document.createElement('canvas')
        dom.appendChild(canvas)
      }
  
      const { width, height } = dom.getBoundingClientRect()
      canvas.width = width
      canvas.height = height
      canvas.style.cssText = `width: ${width}px; height: ${height}px;`
  
      this.ctx = canvas.getContext('2d')
  
      const offScreenCanvas = canvas.cloneNode()
      this.offScreenCtx = offScreenCanvas.getContext('2d')
    }
  
    // 创建单个焰火
    createFirework(x, y, color) {
      const { ctx, particleOptions, fireworkColors } = this
      const { width, height } = ctx.canvas
      x = x ?? random(width * 0.1, width * 0.9)
      y = y ?? random(height * 0.1, height * 0.9)
      color = color ?? random(fireworkColors)
      const particleCount = random(80, 100)
  
      const firework = new Firework({ particleOptions, particleCount, x, y, color })
      this.fireworks.push(firework)
    }
  
    // 焰火燃尽,无情灭之
    checkFireworks() {
      this.fireworks = this.fireworks.filter(firework => !firework.isBurnOff())
    }
  
    // 检查是否需要创建焰火
    loop() {
      let interval = this.fireworkInterval * random(0.5, 1)
      this._timer = setTimeout(() => {
        this.checkFireworks()
  
        if (this.fireworks.length < this.fireworkCount) {
          this.createFirework()
        }
  
        this.loop()
      }, interval)
    }
  
    // 绘制焰火
    render(animationFunction, interval) {
      this._animater = animationFunction(() => {
        const { width, height } = this.ctx.canvas
  
        // 通过绘制黑色透明图层,达到尾焰的效果
        this.ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'
        this.ctx.fillRect(0, 0, width, height)
        
        this.offScreenCtx.clearRect(0, 0, width, height)
  
        this.fireworks.forEach(firework => {
          firework.render(this.offScreenCtx)
        })
  
        this.ctx.save()
        this.ctx.globalCompositeOperation = 'lighter'
        this.ctx.drawImage(this.offScreenCtx.canvas, 0, 0, width, height)
        this.ctx.restore()
  
        this.render(animationFunction, interval)
      }, interval)
    }
  
    // 前进四 !!!
    start() {
      this.loop()
      // 60 帧就用 requestAnimationFrame,否则用 setTimeout
      const animationFunction = this._useAnimationFrame ? requestAnimationFrame : setTimeout
      const interval = 16.67 * (60 / this.fps)
      this.render(animationFunction, interval)
    }
  
    // 休息一下
    pause() {
      this._timer && clearTimeout(this._timer)
      this._animater && (this._useAnimationFrame ? cancelAnimationFrame(this._animater)
        : clearTimeout(this._animater))
  
      this._timer = null
      this._animater = null
    }
  
    // 结束吧这个世界
    stop() {
      this.pause()
  
      this.fireworks.length = 0
  
      const { width, height } = this.ctx.canvas()
      this.ctx.clearRect(0, 0, width, height)
    }
  }
  
  // 焰火类
  class Firework {
    _status = STATUS.INIT
  
    x = 0
    y = 0
  
    color = 'rgba(255, 255, 255, 1)'
    particleCount = 80
    particles = []
    particleOptions = {}
  
    constructor(options = {}) {
      Object.keys(options).forEach(key => this[key] = options[key])
      this._status = STATUS.INIT
  
      this.initParticles()
    }
  
    // 初始化粒子
    initParticles() {
      const { x, y, color, particleOptions } = this
      const { size: baseSize } = particleOptions
  
      for (let index = 0; index < this.particleCount; index++) {
        const size = random(-baseSize / 2, baseSize / 2) + baseSize
        const particle = new Particle({ ...particleOptions, x, y, size, color })
        this.particles.push(particle)
      }
    }
  
    // 更新粒子
    updateParticles() {
      this.particles.forEach(particle => particle.update())
  
      this.particles = this.particles.filter(particle => !particle.isBurnOff())
  
       // 拥有的粒子都燃尽了,自己也就结束了
      if (this.particles.length === 0) {
        this._status = STATUS.COMPLETED
      }
    }
  
    // 渲染粒子
    render(ctx) {
      this.updateParticles()
      if (this.isBurnOff()) return
  
      this.particles.forEach(particle => {
        particle.render(ctx)
      })
    }
  
    isBurnOff() {
      return this._status === STATUS.COMPLETED
    }
  }
  
  // 焰火粒子类
  class Particle {
    size = 10
    speed = 15
    gravity = 0.2
    power = 0.92
    shrink = 0.93
    jitter = 0.08
    color = 'hsla(210, 100%, 50%, 1)'
    shadowColor = 'hsla(210, 100%, 50%, 0.1)'
  
    x = 0 // x 坐标位置
    y = 0 // y 坐标位置
  
    vel = { // 速度
      x: 0,
      y: 0,
    }
  
    constructor(options) {
      Object.keys(options).forEach(key => {
        this[key] = options[key]
      })
      const angle = random(0, Math.PI * 2)
      const speed = Math.cos(random(0, Math.PI / 2)) * this.speed
      this.vel = {
        x: Math.cos(angle) * speed,
        y: Math.sin(angle) * speed,
      }
      this.shadowColor = tinycolor(this.color).setAlpha(0.1)
    }
  
    // 移形换位
    update() {
      this.vel.x *= this.power
      this.vel.y *= this.power
  
      this.vel.y += this.gravity
  
      const jitter = random(-1, 1) * this.jitter
      this.x += this.vel.x + jitter
      this.y += this.vel.y + jitter
  
      this.size *= this.shrink
    }
  
    // 绘制单粒子
    render(ctx) {
      if (this.isBurnOff()) return
  
      ctx.save()
  
      const { x, y, size, color, shadowColor } = this
      // 红里透白,像极了爱情
      const gradient = ctx.createRadialGradient(x, y, 0, x, y, size / 2)
      gradient.addColorStop(0.1, 'rgba(255, 255, 255, 0.3)')
      gradient.addColorStop(0.6, color)
      gradient.addColorStop(1, shadowColor)
  
      ctx.fillStyle = gradient
  
      // ctx.beginPath()
      // ctx.arc(x, y, size, 0, Math.PI * 2, true)
      // ctx.closePath()
      // ctx.fill()
  
      // 绘制矩形性能更好
      ctx.fillRect(x, y, size, size)
  
      ctx.restore()
    }
  
    // 小到看不到
    isBurnOff() {
      return this.size < 1
    }
  }

五、总结

圣诞节,玲儿响,温情祝福传递忙;喜盛宴,披红装,圣诞金娃送吉祥;夜月美,心儿醉,吉祥安康永相随;平安夜,送平安,幸福快乐人变美。最后,祝所有和我一样即将考研的人旗开得胜,坚持就是胜利,世上无难事,只要肯攀登。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【博客567】http/2 goaway frame 与 grpc graceful restart

http2 goway frame 与 grpc graceful restart 1、http/2 HTTP/2新增特性 二进制分帧(HTTP Frames)多路复用头部压缩服务端推送(server push)等等新特性 二进制分帧(HTTP Frames)是实现一流多用的基础 HTTP/2最革命性的原因就在于这个二进制分帧了&#xff0c;要了解二进制…

Win10 21H2 19044+vs2019 WDK驱动开发,错误 MSB8040缓解Spectre 漏洞的库以及输出SXS.DLL的垃圾信息

错误 MSB8040缓解Spectre 漏洞的库以及输出SXS.DLL的垃圾信息&#xff0c;win7关闭驱动签名、进入驱动测试模式缓解Spectre 漏洞错误的解决windbg狂刷输出SXS.DLL的垃圾信息的解决缓解Spectre 漏洞错误的解决 在工程配置属性&#xff0c;常规&#xff0c;输出目录&#xff0c;…

SSD_学习笔记记录

one-steage VGG16模型的修改 VGG16的模型输出&#xff0c;其中224.。。为特征图的大小。 SSD模型中对VGG网络的修改&#xff1a; SSD模型是对VGG中的第四个卷积块中的最后一个Conv2d进行第一个输出&#xff0c;在这里简称为Conv4-3。以及第五个卷积块中的最后一个Conv2d进行…

TCP 的可靠传输(计算机网络-运输层)

TCP的可靠传输 因特网的网络层服务是不可靠的 TCP在IP的不可靠的&#xff1a;尽最大努力服务的基础上实现了一种可靠的数据传输服务 TCP采用的可靠传输机制&#xff1a;差错检测、序号、确认、超时重传、滑动窗口等 互联网环境中端到端的时延往往是比较大的&#xff1a;采用…

(机器学习深度学习常用库、框架|Pytorch篇)第二节:Pytorch中数据加载方法(DataLoader、DataSet和Sampler)

文章目录一&#xff1a;DataLoader,、DataSet、Sampler三者的关系二&#xff1a;DataLoader,、DataSet、Sampler详解&#xff08;1&#xff09;DatasetA&#xff1a;基本介绍C&#xff1a;Pytroch内置数据集&#xff08;2&#xff09;SamplerA&#xff1a;SequentialSampler&am…

【算法与数据结构】排序详解(C语言)

目录 前言 插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序 hoare版本 ​编辑 挖坑法 前后指针版本 优化 非递归实现 归并排序 非递归实现 前言 &#x1f384;在生活中我们必不可少的就是对一组数据进行排序&#xff0c;所谓排序&#xff0c;就是使一串…

ByteBuffer常用方法与分析

目录 目标 常用API 工具方法 演示案例 allocate(int capacity)和allocateDirect(int capacity) put()和get() flip()和hasRemaining() clear() compact() wrap() 总结 目标 掌握ByteBuffer常用方法&#xff0c;分析ByteBuffer对象在切换读写模式的情况下基本属性的变…

【REDIS】安装配置 可视化工具

Redis作为一个高性能&#xff0c;内存可存储化的no SQL数据库&#xff0c;近两年来发展迅猛&#xff0c;然而并没有比较成熟的管理工具来使用&#xff0c;或者是我不知道 下载redis 并安装&#xff1a; 双击安装&#xff0c;可以安装到d: 盘 配置文件是 .conf Redis作为一个…

排序——快排(递归/非递归)

目录 定义 递归 三种方法 1.hoare法 2.挖坑法 3.双指针法 整体 优化1 优化2 非递归 定义 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法&#xff0c;其基本思想为&#xff1a;任取待排序元素序列中 的某元素作为基准值&#xff0c;按照该排序码将待排序集…

快速掌握e语言,以js语言对比,快速了解掌握。

易语言&#xff0c;怎么调试输出&#xff0c;查看内容 在js语言里&#xff0c;弹窗是 alert()在易语言里&#xff0c;弹窗是 信息框 (“弹出显示内容”, 0, “标题”, ); 在js语言里&#xff0c;调试输出是 console.log()在易语言里&#xff0c;调试输出是 调试输出 (“输出内…

开发过程中使用,可以早点下班的coding小技巧

前言 在实际开发过程中,通过时间的沉淀,一些老人常常能写出一些让小白大吃一惊“骚操作”,那些“骚操作”通常简单的离谱,却能做很多事,属实是让很多新人摸不着头脑。 做一件事时间长了,技巧也就有了。 下面来个情景小剧场: 初入职场小鱼仔:这傻逼是不是写错了,~~ s…

基于凸几何和K均值的高光谱端元提取算法(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

Sentinel统一异常处理

五.统一异常处理—BlockException 在上述规则测试中&#xff0c;当违反规则时&#xff0c;出来的异常信息页面不够友好和统一&#xff0c;我们可以通过设置统一的异常处理类&#xff0c;针对不同规则显示不同异常信息。 创建一个配置类&#xff0c;实现BlockExceptionHandler…

numpy数组,numpy索引,numpy中nan和常用方法

一&#xff1a;【numpy数组】 1.1为什么要学习numpy 1.快速 2.方便 3.科学计算的基础库 1.2什么是numpy 一个python中做科学计算的基础库&#xff0c;重在数值计算&#xff0c;也是大部分python科学计算库的基础库&#xff0c;多用于在大型&#xff0c;多维数组上执行数组运…

常用的键盘事件

1、键盘事件 键盘事件触发条件onkeyup某个键盘按键被松开时触发onkeydown某个键盘按键被按下时触发onkeypress某个键盘按键被按下时触发&#xff08;但它不识别功能键&#xff0c;比如ctrl、shift等&#xff09; 注意&#xff1a; 如果使用addEventListener不需要加ononkeypr…

Go 堆数据结构使用

说到 container/heap 下的堆数据结构&#xff0c;让我们不需要从零开始实现这个数据结构。如果只是日常工作&#xff0c;其实还挺难用到堆的&#xff0c;更多的还是在写算法题的时候会用到。 基本概念 堆分为大顶堆和小顶堆&#xff0c;区分这两种类型方便我们处理问题。大顶…

Docker安装Zookeeper教程(超详细)

生命无罪&#xff0c;健康万岁&#xff0c;我是laity。 我曾七次鄙视自己的灵魂&#xff1a; 第一次&#xff0c;当它本可进取时&#xff0c;却故作谦卑&#xff1b; 第二次&#xff0c;当它在空虚时&#xff0c;用爱欲来填充&#xff1b; 第三次&#xff0c;在困难和容易之…

第六章:关系数据理论

一、问题的提出、范式 1、【多选题】下列说法中正确的是&#xff1a; 正确答案&#xff1a; ABCD 2、【多选题】关系模式R&#xff08;项目序号&#xff0c;项目代码&#xff0c;项目名称&#xff09;&#xff0c;项目序号是码。一个项目代码只有一个项目名称。下列说法不正确…

文献检索报告

文献检索第一篇检索作业总结第一章检索任务1.1检索课题1.2确定选题所属学科1.3中英文检索词第二章检索策略与结果2.1检索中文期刊文献2.1.1 CNKI中国期刊全文数据库2.1.2 维普期刊全文数据库2.1.3 万方期刊数据库2.1.4 超星期刊全文2.2检索中文学位论文2.2.1 CNKI博硕学位论文数…

virtio-net发包流程分析

virtio-net发包流程分析 virtio-net发包流程前端驱动部分 总流程 start_xmit|---->free_old_xmit_skbs /* 释放backend处理过的desc */|---->xmit_skb /* 调用xmit_skb函数将网络包写入virtqueue */| |---->sg_set_buf /* 数据包头部填入scatterlist */| | |---->…