css动画烟花秀__烟花效果

news2024/10/24 7:22:00

先看效果

CSS烟花秀

这是一个在HTML5页面,实现烟花效果的例子

以下为实现代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>烟花</title>
</head>
<style>
  body {
    margin: 0;
    padding: 0;
    overflow: hidden;
  }

  .canvasBox {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  canvas {
    border: 1px solid;
    background-color: #ccc;
  }
</style>

<body>
<div class="canvasBox">
  <canvas id="canvas"></canvas>
</div>
<audio id="background-music" src="yanhua.mp3"  preload="auto" autoplay loop></audio>
</body>

</html>
<script src="test.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const canvasWidth = document.documentElement.clientWidth || document.body.clientWidth
  const canvasHeight = document.documentElement.clientHeight || document.body.clientHeight
  const ratio = Math.max(window.devicePixelRatio, 2)
  canvas.width = canvasWidth * ratio
  canvas.height = canvasHeight * ratio
  canvas.style.width = canvasWidth + 'px'
  canvas.style.height = canvasHeight + 'px'
  const ctx = canvas.getContext('2d')
  ctx.scale(ratio, ratio)

  const getRandom = (min, max) => {
    return Math.random() * (max - min) + min
  }

  const drawCircle = ({ opacity = 1, x, y, radius, color }) => {
    ctx.save()
    ctx.globalAlpha = opacity
    ctx.beginPath()
    ctx.arc(x, y, radius, 0, Math.PI * 2)
    ctx.fillStyle = color
    ctx.fill()
    ctx.restore()
  }
  const deleteFromList = (list, target) => {
    const index = list.findIndex(item => {
      return item === target
    })
    list.splice(index, 1)
  }
  // 动画循环
  // 烟花列表
  const fireworkList = []
  const draw = () => {
    // 使用半透明清空画布,形成拖尾效果
    ctx.fillStyle = 'rgba(0,0,0,0.1)'
    ctx.fillRect(0, 0, canvasWidth, canvasHeight)
    ctx.save()

    // 修改坐标系
    ctx.translate(0, canvasHeight)
    ctx.scale(1, -1)

    const list = [...fireworkList]
    list.forEach(firework => {
      firework.update()
      if (firework.isEnd()) {
        deleteFromList(fireworkList, firework)
      }
    })

    ctx.restore()

    requestAnimationFrame(draw)
  }
  draw()

  // 烟花颜色列表
  const createFireworkColor = () => {
    const colorList = [
      '#ff0043',
      '#14fc56',
      '#1e7fff',
      '#e60aff',
      '#ffbf36',
      '#ffffff'
    ]
    return colorList[Math.floor(Math.random() * colorList.length)]
  }

  // 发射烟花
  //canvas.addEventListener('click', () => {
      setInterval(function () {
        const firework = new Firework({
          color: createFireworkColor()
        })
        fireworkList.push(firework)
        firework.launch()
      }, 1000);
  //})

</script>

 其中引用的一个test.js文件 如下:

// 爆炸碎片类
class ExplosiveDebris {
  constructor(opt) {
    this.firework = opt.firework
    this.x = opt.x
    this.y = opt.y
    this.color = Math.random() > 0.2 ? opt.color : '#fff'
    this.radius = opt.radius || 2
    this.angle = getRandom(0, 2 * Math.PI)
    this.speed = opt.speed || getRandom(0.1, 4)
    this.vx = Math.cos(this.angle) * this.speed
    this.vy = Math.sin(this.angle) * this.speed
    this.g = opt.g || 0.98
    this.time = getRandom(0.5, 1)
    this.startTime = 0
    // 痕迹碎片数量
    this.debrisCount = opt.debrisCount || 3
    // 是否要进行二次爆炸
    this.secondBurst = opt.secondBurst || false
  }

  start() {
    this.startTime = Date.now()
  }

  update() {
    const duration = (Date.now() - this.startTime) / 1000
    const vy = this.vy - this.g * duration
    this.x += this.vx
    this.y += vy
    const progress = duration / this.time
    let opacity = progress > 0.7 ? 1 - 1 * progress : 1
    if (opacity < 0) opacity = 0
    drawCircle({
      x: this.x,
      y: this.y,
      color: this.color,
      radius: this.radius,
      opacity: opacity
    })
    // 添加痕迹碎片
    if (this.debrisCount > 0 && Math.random() > 0.8) {
      this.debrisCount--
      this.firework.addDebris({
        x: this.x + getRandom(-2, 2),
        y: this.y + getRandom(-2, 2),
        color: this.color,
        radius: 0.5,
        g: 0.1
      })
    }
    return {
      x: this.x,
      y: this.y,
      isEnd: progress >= 1
    }
  }
}


// 爆炸器类
class Explosive {
  constructor(opt) {
    this.firework = opt.firework
    this.x = opt.x
    this.y = opt.y
    this.color = opt.color
    // 爆炸碎片列表
    this.debrisList = []
    // 爆炸碎片数量
    this.debrisNum = opt.debrisNum || getRandom(10, 2000)
    // 是否要二次爆炸
    this.secondBurst = opt.secondBurst || this.debrisNum <= 100
    //是否是第一次爆炸
    this.isFirstBurst = true
  }

  start(debrisNum, opt = {}) {
    const num = debrisNum || this.debrisNum
    opt.x = opt.x || this.x
    opt.y = opt.y || this.y
    opt.secondBurst = this.secondBurst && this.isFirstBurst
    for (let i = 0; i < num; i++) {
      const explosiveDebris = new ExplosiveDebris({
        firework: this.firework,
        color: this.color,
        ...opt
      })
      explosiveDebris.start()
      this.debrisList.push(explosiveDebris)
    }
    this.isFirstBurst = false
  }

  update() {
    const list = [...this.debrisList]
    list.forEach(debris => {
      const res = debris.update()
      if (res.isEnd) {
        deleteFromList(this.debrisList, debris)
        // 二次爆炸
        if (debris.secondBurst) {
          this.start(5, {
            x: res.x,
            y: res.y,
            speed: 1
          })
        }
      }
    })
    return {
      isEnd: list.length <= 0
    }
  }
}

// 痕迹碎片类
class Debris {
  constructor(opt = {}) {
    // 颜色
    this.color = opt.color || '#fff'
    // 透明度
    this.opacity = getRandom(0.1, 0.5)
    // 半径
    this.radius = opt.radius || 1
    // 存在时间
    this.time = getRandom(0.5, 1)
    // 重力,px/s2
    this.g = opt.g || 0.98
    // 位置
    this.x = opt.x
    this.y = opt.y
    // 创建的时间
    this.startTime = 0
  }

  start() {
    this.startTime = Date.now()
  }

  update() {
    const duration = (Date.now() - this.startTime) / 1000
    this.y -= this.g * duration
    drawCircle({
      opacity: this.opacity,
      x: this.x,
      y: this.y,
      radius: this.radius,
      color: this.color
    })
    return {
      x: this.x,
      y: this.y,
      isEnd: duration > this.time
    }
  }
}


// 发射器类
class Launcher {
  constructor(opt = {}) {
    // 烟花实例
    this.firework = opt.firework
    // 颜色
    this.color = opt.color
    // 初始位置
    this.x = opt.x || canvasWidth * getRandom(0.2, 0.8)
    this.y = opt.y || 0
    // 目标位置
    this.ty = canvasHeight * getRandom(0.6, 0.8)
    // 半径
    this.radius = opt.radius || getRandom(2, 5)
    // 发射的持续时间
    this.duration = opt.duration || getRandom(2000, 3500)
    // 发射时的时间
    this.startTime = 0
  }

  start() {
    this.startTime = Date.now()
  }

  easeOutCubic(t, b, c, d) {
    return c * ((t = t / d - 1) * t * t + 1) + b
  }

  update() {
    const x = this.x
    let y = this.easeOutCubic(
      Date.now() - this.startTime,
      this.y,
      this.ty - this.y,
      this.duration
    )
    y = Math.min(y, this.ty)
    // 透明度变小
    let opacity = 1 - 1 * (y / this.ty)
    if (opacity < 0) opacity = 0
    this.draw(x, y, opacity)
    // 添加痕迹碎片
    if (Math.random() > 0.7 && opacity >= 0.1) {
      this.firework.addDebris({
        x: x + getRandom(-2, 2), // x坐标添加一段随机量
        y
      })
    }
    return {
      x,
      y,
      isEnd: y >= this.ty //返回true代表发射结束
    }
  }
  draw(x, y, opacity) {
    // 外圆,烟花的颜色
    drawCircle({
      opacity: opacity,
      x: x,
      y: y,
      radius: this.radius,
      color: this.color
    })
    // 内圆,白色
    drawCircle({
      opacity: opacity,
      x: x,
      y: y,
      radius: this.radius / 2,
      color: '#fff'
    })
  }
}

// 烟花类
class Firework {
  constructor(opt = {}) {
    // 颜色
    this.color = opt.color || tinycolor.random().toHexString()
    // 发射器
    this.launcher = null
    // 爆炸器
    this.explosive = null
    // 烟花状态:waiting(等待发射)、launching(发射中)、bursting(爆炸中)、end(烟花结束)
    this.status = 'waiting'
    // 痕迹碎片列表
    this.debrisList = []
  }

  // 发射
  launch() {
    this.launcher = new Launcher({
      firework: this,
      color: this.color
    })
    this.launcher.start()
    this.status = 'launching'
  }

  // 爆炸
  burst({ x, y }) {
    this.explosive = new Explosive({
      firework: this,
      x,
      y,
      color: this.color
    })
    this.explosive.start()
  }

  // 更新
  update() {
    if (this.status === 'launching') {
      const res = this.launcher.update()
      if (res.isEnd) {
        this.status = 'bursting'
        this.burst(res)
      }
    } else if (this.status === 'bursting') {
      const res = this.explosive.update()
      if (res.isEnd) {
        this.status = 'end'
      }
    }
    // 更新痕迹碎片
    this.updateDebris()
  }

  // 添加痕迹碎片
  addDebris(opt = {}) {
    const debris = new Debris({
      ...opt,
      color: opt.color || this.color
    })
    debris.start()
    this.debrisList.push(debris)
  }

  // 更新痕迹碎片
  updateDebris() {
    const list = [...this.debrisList]
    list.forEach(debris => {
      const res = debris.update()
      if (res.isEnd) {
        deleteFromList(this.debrisList, debris)
      }
    })
  }

  isEnd() {
    return this.status === 'end'
  }
}

一个音频文件为 yanhua.mp3

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

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

相关文章

<项目代码>YOLOv8路面垃圾识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

Codeforces Round 881 (Div. 3)(A~F1题解)

这场比赛可能是因为比较老吧&#xff0c;我感觉很轻松&#xff0c;就是第五个卡了一下&#xff0c;看错题了&#xff0c;原本应该是严格大于的&#xff0c;从而导致代码一直出现bug&#xff0c;在1小时20分钟才解决 A. Sasha and Array Coloring 题意&#xff1a;就是说给你n个…

提权 | Windows系统

文章目录 cmd提权meterpreter提权getsystemsteal_tokenmigrate 令牌窃取(MS16-075)烂土豆提权步骤烂土豆提权原理 sc命令提权抓本地密码提权其他工具pr工具 内核提权WindowsVulScan cmd提权 前言&#xff1a;我们getshell一个用windows部署的网站后&#xff0c;通过蚁剑或者其…

ESP32 S3 语音识别 语音唤醒程序流程

ESP32 S3 语音识别 语音唤醒程序流程 参考例程首先进行esp_periph_set_init 初始化之后执行setup_player&#xff0c;之后执行start_recorder&#xff0c;识别的主处理voice_read_task 参考例程 D:\Espressif\esp-adf\examples\speech_recognition\wwe\ 首先进行esp_periph_se…

零知识学习WLAN漫游二、无线漫游介绍(2)

接前一篇文章&#xff1a;零知识学习WLAN漫游一、无线漫游介绍&#xff08;1&#xff09; 本文内容参考&#xff1a; WLAN漫游简介_漫游主动性-CSDN博客 无线漫游_百度百科 无线漫游简述-CSDN博客 特此致谢&#xff01; 一、WLAN漫游简介 3. 漫游协议和快速漫游协议 802.…

算法的学习笔记—数字在排序数组中出现的次数(牛客JZ53)

&#x1f600;前言 在编程中&#xff0c;查找有序数组中特定元素的出现次数是一个常见的问题。本文将详细讲解这个问题的解决方案&#xff0c;并通过二分查找法优化效率。 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 &#x1f970;数字在排序数组中出现的次数&#x…

九、pico+Unity交互开发——触碰抓取

一、VR交互的类型 Hover&#xff08;悬停&#xff09; 定义&#xff1a;发起交互的对象停留在可交互对象的交互区域。例如&#xff0c;当手触摸到物品表面&#xff08;可交互区域&#xff09;时&#xff0c;视为触发了Hover。 Grab&#xff08;抓取&#xff09; 概念&#xff…

深入浅出:深度学习模型部署全流程详解

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; Yaoyao2024往期回顾&#xff1a; 【论文精读】PSAD&#xff1a;小样本部件分割揭示工业异常检测的合成逻辑每日一言&#x1f33c;: 生活要有所期待&#xff0c; 否则就如同罩在…

【国潮来袭】华为原生鸿蒙 HarmonyOS NEXT(5.0)正式发布:鸿蒙诞生以来最大升级,碰一碰、小艺圈选重磅上线

在昨日晚间的原生鸿蒙之夜暨华为全场景新品发布会上&#xff0c;华为原生鸿蒙 HarmonyOS NEXT&#xff08;5.0&#xff09;正式发布。 华为官方透露&#xff0c;截至目前&#xff0c;鸿蒙操作系统在中国市场份额占据 Top2 的领先地位&#xff0c;拥有超过 1.1 亿 的代码行和 6…

想让前后端交互更轻松?alovajs了解一下?

作为一个前端开发者&#xff0c;我最近发现了一个超赞的请求库 alovajs&#xff0c;它真的让我眼前一亮&#xff01;说实话&#xff0c;我感觉自己找到了前端开发的新大陆。大家知道&#xff0c;在前端开发中&#xff0c;处理 Client-Server 交互一直是个老大难的问题&#xff…

查缺补漏----用户工作区,系统缓冲区,外设工作最短时间计算

对于下面这一题&#xff0c;分析起来很简单&#xff1a; 答案&#xff1a;C 以上是单缓冲区&#xff0c;补充双缓冲区是什么情况&#xff1a; 1.假设磁盘块与缓冲区大小相同&#xff0c;每个盘块读入缓冲区的时间为15us&#xff0c;由缓冲区送至用户区的时间是5us&#xff0c…

etl-查询错误log日志和oracle删除数据表空间

查看weblogic日志的目录 建立连接ssh root192.168.30.1xx 密码hygd123 找到下面路径中的文件 cd /home/weblogic/Oracle/Middleware/user_projects/domains/base_domain/bapp-logs 查看log日志 tail -f -n 400 Adminservers.log oracle删除表空间&#xff08;切换到dba用户…

Android 13 SystemUI 隐藏下拉快捷面板部分模块(wifi,bt,nfc等)入口

frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java createTileInternal(tileSpec)方法注释想隐藏的模块即可。

Qt开发-----线程调度

目录 前言 一、Linux下查看进程的情况 二、线程的创建 三、多线程的创建和使用 前言 以下引用内容源自正点原子Qt开发指南文档。 我们写的一个应用程序&#xff0c;应用程序跑起来后一般情况下只有一个线程&#xff0c;但是可能也有特殊情况。比如我们前面章节写的例程都跑…

《YOLO目标检测》—— YOLOv1 详细介绍

文章目录 一、算法特点二、网络结构三、检测过程四、损失函数五、性能表现六、优缺点 YOLO v1&#xff08;You Only Look Once version 1&#xff09;是一种快速的目标检测算法&#xff0c;以下是对YOLO v1的详细介绍&#xff1a; 一、算法特点 端到端的网络结构&#xff1a;Y…

项目:Boost 搜索引擎

项目&#xff1a;Boost 搜索引擎 1、项目背景 公司&#xff1a;百度、360、搜狗、谷歌 …站内搜索&#xff1a;搜索的数据更垂直&#xff08;相关&#xff09;&#xff0c;数据量小 2、整体框架 3、技术栈和项目环境 技术栈&#xff1a;C/C C11&#xff0c;STL&#xff0c;jso…

【JAVA毕设】基于JAVA的仓库管理系统

一、项目介绍 本系统前端框架采用了比较流行的渐进式JavaScript框架Vue.js。使用Vue-Router实现动态路由&#xff0c;Ajax实现前后端通信&#xff0c;Element-plus组件库使页面快速成型。后端部分&#xff1a;采用SpringBoot作为开发框架&#xff0c;同时集成MyBatis、Redis、…

C#中的LINQ之美:优雅的数据查询与操作

LINQ&#xff08;Language Integrated Query&#xff0c;语言集成查询&#xff09;是C#中一个强大的工具&#xff0c;它将查询功能直接融入到语言中&#xff0c;使开发者能够以一种更直观、更接近自然语言的方式来操作数据。LINQ不仅能极大地提高开发效率&#xff0c;而且让代码…

掌握ElasticSearch(五):查询和过滤器

一、查询和过滤器的区别 在 Elasticsearch 中&#xff0c;查询&#xff08;Query&#xff09;和过滤器&#xff08;Filter&#xff09;是用于检索和筛选数据的重要组成部分。它们虽然都能用来查找文档&#xff0c;但在性能和用法上有所不同。下面详细介绍查询和过滤器的概念以…

Lucas带你手撕机器学习——K近邻

K近邻 (K-Nearest Neighbor KNN) K近邻算法&#xff08;K-Nearest Neighbors, KNN&#xff09;是一种简单直观的机器学习算法&#xff0c;适用于分类和回归问题。它的核心思想是&#xff1a;判断一个数据点的类别或预测值时&#xff0c;参考它在特征空间中最近的 KKK 个数据点…