当前位置: 首页 > article >正文

canvas鼠标点击特效

1. 基础准备

1.准备基础的canvas画布

    let mousedown // 鼠标按下事件
    let mouseup // 鼠标抬起事件
    const balls = [] // 存放小球
    let longPressed = false
    let longPress // 长按事件计时器
    let multiplier = 0 // 倍数器
    let width, height // 画布宽高
    let origin // 原点坐标
    let normal // 每次小球坐标变化的基数
    let ctx // canvas的上下文

    const colours = ["#F73859", "#14FFEC", "#00E0FF", "#FF99FE", "#FAF15D"] // 烟花色彩

    const canvas = document.createElement("canvas") // 创建canvas
    document.body.appendChild(canvas) // 添加canvas
    // 设置基础样式
    canvas.setAttribute(
        "style",
        "width: 100%; height: 100%; top: 0; left: 0; z-index: 99999; position: fixed; pointer-events: none;"
    )

2.准备鼠标样式

const pointer = document.createElement("span") // 生成鼠标元素
pointer.classList.add("pointer") // 添加样式
document.body.appendChild(pointer) // 添加到body

// 添加鼠标移动事件
window.addEventListener(
  "mousemove",
  function (e) {
    const x = e.clientX
    const y = e.clientY
    pointer.style.top = y + "px"
    pointer.style.left = x + "px"
  },
  false
)
    body{
        cursor: none; /* 隐藏鼠标 */
    }
    .pointer {
        position: fixed; /* 固定位置 */
        top: 50%;
        left: 50%;
        width: 50px;
        height: 50px;
        border-radius: 50%;
        transform: translate(-50%, -50%);
        background-image: url(./image.jpg); /* 鼠标图片 */
        background-size: cover;
        background-repeat: no-repeat;
    }

3.监听并实时更新画布样式

 // 判断当前画笔状态和页面事件监听状态,用于增强代码的兼容性
  if (canvas.getContext && window.addEventListener) {
    ctx = canvas.getContext("2d") // 获取canvas的上下文

    updateSize() // 更新画布大小

    window.addEventListener("resize", updateSize, false) // 监听窗口大小变化,重新设置画布大小和原点坐标
    // TODO: 开启渲染
    // TODO: 设置点击事件
  } else {
    console.log("对不起,您的浏览器不支持")
  }

 //   更新画布大小
  function updateSize() {
    canvas.width = window.innerWidth * 2 // canvas 宽
    canvas.height = window.innerHeight * 2 // canvas 高
    canvas.style.width = window.innerWidth + "px"
    canvas.style.height = window.innerHeight + "px"

    ctx.scale(2, 2) // 将画布放大2倍,绘制的所有东西都放大2倍
    
    width = canvas.width = window.innerWidth
    height = canvas.height = window.innerHeight

    origin = {
      x: width / 2,
      y: height / 2
    }
    normal = {
      x: width / 2,
      y: height / 2
    }
  }

2. 生成烟花小球

1.准备小球类

 class Ball {
    x // x轴坐标
    y // y轴坐标
    angle // 弧度
    multiplier // 倍数
    vx // x轴速度
    vy // y轴速度
    r // 半径
    color // 颜色

    // 构造函数
    constructor(x = origin.x, y = origin.y) {
        // 设置位置
      this.x = x
      this.y = y

      this.angle = Math.PI * 2 * Math.random() // 设置一个随机弧度
    //   判断是否是长按状态
    // TODO: 处理长按
        this.multiplier = randBetween(6, 12) // 生成随机倍数

    //   生成速度
      this.vx = (this.multiplier + Math.random() * 0.5) * Math.cos(this.angle)
      this.vy = (this.multiplier + Math.random() * 0.5) * Math.sin(this.angle)

      this.r = randBetween(8, 12) + 3 * Math.random() // 生成随机半径

      this.color = colours[Math.floor(Math.random() * colours.length)] // 生成随机颜色

    }
    update() {

      this.x += this.vx - normal.x // 更新x轴坐标
      this.y += this.vy - normal.y // 更新y轴坐标

      normal.x = (-2 / window.innerWidth) * Math.sin(this.angle)
      normal.y = (-2 / window.innerHeight) * Math.cos(this.angle)

      this.r -= 0.3 // 缩小半径
    //   减慢速度
      this.vx *= 0.9 // 
      this.vy *= 0.9
    }
  }

2.开始绘制

if (canvas.getContext && window.addEventListener) {
  ...
 loop() // 开始绘制 此时什么都没有绘制,只是开启了一个循环,只要balls有数据就会开始绘制
 ...
}
    //   渲染函数
  function loop() {

    ctx.fillStyle = "rgba(255, 255, 255, 0)" // 设置画布背景色

    ctx.clearRect(0, 0, canvas.width, canvas.height) // 清理画布内容
    
    // 遍历所有小球
    for (let i = 0; i < balls.length; i++) {
      const b = balls[i] // 小球对象
      if (b.r < 0) continue // 小球半径小于0,则进行绘制
      ctx.fillStyle = b.color // 设置小球颜色
      ctx.beginPath() // 开始绘制
      // 在路径中添加一个圆,圆心在 (b.x, b.y),半径为 b.r,从0弧度开始,绘制一个完整的圆(Math.PI * 2 弧度),false 表示顺时针绘制
      ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2, false)
      ctx.fill() // 用当前的填充颜色填充路径,即填充球体。其实就是b.color
      b.update() // 用球体对象的 update 方法,更新球体的状态,如位置、速度等
    }
      multiplier -= 0.4 
      // TODO: 处理长按
      // TODO: 处理小球删除
    
    requestAnimationFrame(loop) // 循环调用 loop 函数,实现动画效果
  }

3.设置鼠标点击生成特效

    // 鼠标按下事件
  mousedown = function (e) {
    pushBalls(randBetween(10, 20), e.clientX, e.clientY) // 添加小球,同时随机生成一个数量,同时设置鼠标点击位置为原点
    // TODO: 处理长按
  }

window.addEventListener("mousedown", mousedown, false)

function pushBalls(count = 1, x = origin.x, y = origin.y) {
  for (let i = 0; i < count; i++) {
    balls.push(new Ball(x, y)) // 添加小球
  }
}

//   生成随机数
function randBetween(min, max) {
  return Math.floor(Math.random() * max) + min
}

3.鼠标长按特效

1.设置小球长按增长倍数

class Ball {
  ...
  constructor(...) {

    //   判断是否是长按状态
      if (longPressed == true) {
        this.multiplier = randBetween(14 + multiplier, 15 + multiplier) // 生成随机倍数
      } else {
        this.multiplier = randBetween(6, 12) // 生成随机倍数
      }
    ...
  }
}

2.渲染时判断长按,生成更多的小球

    function loop() {
      ...
      // 判断是否是长按状态
      if (longPressed == true) {
        this.multiplier = randBetween(14 + multiplier, 15 + multiplier) // 生成随机倍数
      } else {
        this.multiplier = randBetween(6, 12) // 生成随机倍数
      }
      ...
    }

3.鼠标按下时循环生成更多小球

    mouseup = () => {
      ...
      if (longPressed == true) {
        document.body.classList.remove("is-longpress") // 移除长按样式

        pushBalls(randBetween(50 + Math.ceil(multiplier), 100 + Math.ceil(multiplier)), e.clientX, e.clientY) // 添加小球,同时随机生成一个数量,同时设置鼠标点击位置为原点
        longPressed = false
      }
      ...
    }

4.鼠标弹起改变按压状态,开启特效

   mouseup = function (e) {

      clearInterval(longPress) // 清除长按事件计时器

      if (longPressed == true) {
        document.body.classList.remove("is-longpress") // 移除长按样式

        pushBalls(randBetween(50 + Math.ceil(multiplier), 100 + Math.ceil(multiplier)), e.clientX, e.clientY) // 添加小球,同时随机生成一个数量,同时设置鼠标点击位置为原点
        longPressed = false
      }

      document.body.classList.remove("is-pressed") // 移除鼠标按下样式

    }

    window.addEventListener("mouseup", mouseup, false)

4.清除小球**

function loop() {
  ...
  removeBall() // 检察小球是否超出画布边界,如果超出则删除小球
  ...
}

function removeBall() {
    for (let i = 0; i < balls.length; i++) {
      const b = balls[i]
      // 检察小球是否超出画布边界,如果超出则删除小球
      if (b.x + b.r < 0 || b.x - b.r > width || b.y + b.r < 0 || b.y - b.r > height || b.r < 0) {
        balls.splice(i, 1)
      }
    }
  }

复制链接到抖音查看
8.92 Mjc:/ 01/09 t@r.EH 鼠标点击烟花特效 # 程序员 # 代码 # 前端 # canvas # 艺术在抖音 https://v.douyin.com/iSRSQQJ6/ 复制此链接,打开Dou音搜索,直接观看视频!


http://www.kler.cn/a/354812.html

相关文章:

  • Java - 日志体系_Simple Logging Facade for Java (SLF4J)日志门面_SLF4J集成logback 及 原理分析
  • 阿里云redis内存优化——PCP数据清理
  • odoo中@api.model, @api.depends和@api.onchange 装饰器的区别
  • Qt仿音乐播放器:QFileDialog添加本地文件
  • 人工智能与区块链的碰撞:双剑合璧的创新前景
  • 短视频矩阵账号管理技术源码搭建详解,支持OEM
  • 【godot游戏引擎学习笔记】初识界面
  • 【Linux】<互斥量>解决<抢票问题>——【多线程竞争问题】
  • 小程序底部导航按钮实现
  • 执行vue create XXX报错The operation was rejected by your operating system
  • 计算机网络day2
  • matlab的resample函数
  • spring 注解
  • IRP读写函数
  • 八股面试3(自用)
  • 机器学习与神经网络:物理学的新边疆
  • docker 复制文件,清除不再使用数据导出以及导出文件系统
  • 搜维尔科技:力反馈遥操作解决方案,五指灵巧手遥操作解决方案
  • Java初学者的学习顺序
  • 网络基础知识:六大交换机关键知识解析
  • 无人机之遥感影像处理篇
  • 国产 HDMI 发送芯片,兼容 HDMI1.4b 及 HDMI 1.4b 下的视频 3D 传输格式。
  • JavaScript 第9章:面向对象编程
  • 虎牙Android面试题及参考答案
  • C++ 方法积累
  • 【优选算法】(第三十六篇)