Canvas - 实现动画、放大模糊,处理图片的原理

Canvas - 实现动画、放大模糊,处理图片的原理

  1. requestAnimationFrame实现动画

    • 它是在canvas中实现动画的关键,不同于setTimeoutsetInterval,它不需要设置时间间隔,显示器为60hz,则每秒重绘60次。因此最平滑的动画最佳间隔就是16.6msrequestAnimationFrame采用系统时间间隔。
    • requestAnimationFrame会把每帧中所有dom操作集中起来在一次重绘或回流中完成,更加高效,是浏览器专门为动画提供的api
    • 实现动画的原理:
      • 在第一次绘画完成后清空画布,在requestAnimationFrame的回调中第二次绘画
      • 每次绘画都记录本次绘图的系统时间,并与上一次求差,得出两次绘图的间隔(持续时间):duration
      • 定义速度变量,通过速度*时间=距离的公式得出第二次绘画相比于第一次所需的移动距离
      • 处理好边界情况完成绘图
    • 示例
    const cvs: any = document.querySelector('canvas')
    cvs.width = 300
    cvs.height = 300
    const ctx: any = cvs?.getContext('2d')
    
    class OutPoint {
      x: number
      y: number
      r: number
      t: number
      outR: number
      lastTime: any
      constructor() {
        this.x = cvs.width / 2
        this.y = cvs.height / 2
        this.r = 10
        this.outR = 20
        this.t = 0
        this.lastTime = null
      }
    
      draw() {
        if (this.lastTime) {
          const speed = 10
          const duration = (performance.now() - this.lastTime) / 1000 // 间隔`duration`秒
          const distance = duration * speed
          
          this.t += distance / 10 // 随时间变化 外圈透明度降低
          this.r += distance
          this.outR += distance * 3 // 外圈移动距离为内圈3倍
    
          // 处理边界情况 当外圈透明度降为0 初始化
          if (1 - this.t <= 0) {
            this.t = 0
            this.r = 10
            this.outR = 20
          }
        }
    
        // out
        ctx.beginPath()
        ctx.arc(cvs.width / 2, cvs.height / 2, this.outR, 0, 2 * Math.PI)
        ctx.fillStyle = `rgba(255, 200, 200, ${1 - this.t})`
        ctx.fill()
    
        // white
        ctx.beginPath()
        ctx.arc(cvs.width / 2, cvs.height / 2, 20, 0, 2 * Math.PI)
        ctx.fillStyle = 'white'
        ctx.fill()
    
        // inner
        ctx.beginPath()
        ctx.arc(cvs.width / 2, cvs.height / 2, this.r, 0, 2 * Math.PI)
        ctx.fillStyle = 'red'
        ctx.fill()
        this.lastTime = performance.now()
      }
    }
      
    class Graph {
      outPoint: any
      constructor() {
        this.outPoint = new OutPoint()
      }
      draw() {
        // requestAnimationFrame 
        requestAnimationFrame(() => {
          this.draw()
        })
        ctx.clearRect(0, 0, cvs.width, cvs.height) // 清空画布
        this.outPoint.draw()
      }
    }
      
    // 调用
    const g = new Graph()
    g.draw()
    

    在这里插入图片描述 在这里插入图片描述

  2. canvas放大不清晰的原理及解决办法

    • canvas本质上可以理解为图片,而图片属于位图,只要保持原始尺寸=样式尺寸*缩放倍率等式成立,图片就能保持清晰
    // 例如 定义canvas的宽高,即为canvas的原始尺寸
    // 给canvas设置样式style的宽高即为样式尺寸
    // img 标签同理
    const cvs: any = document.querySelector('canvas')
    cvs.width = 300 // 原始尺寸
    cvs.height = 300 // 原始尺寸
    cvs.style.width = '300px' // 样式尺寸
    cvs.style.height = '300px' // 样式尺寸
    const devicePixelRatio = window.devicePixelRatio // 缩放倍率
    
    • js中获取 window.devicePixelRatio 即可得到当前浏览器的缩放倍率,接下来只需要保持等式成立即可
    const cvs: any = document.querySelector('canvas')
    cvs.width = 300 * devicePixelRatio // 原始尺寸 * 缩放倍率
    cvs.height = 300 * devicePixelRatio
    cvs.style.width = '300px' // 样式尺寸不变
    cvs.style.height = '300px'
    const ctx: any = cvs?.getContext('2d')
    
    ctx.beginPath()
    ctx.arc(
      cvs.width / 2,
      cvs.height / 2, 
      20 * devicePixelRatio, // 半径 * 缩放倍率
      0, 
      2 * Math.PI
    )
    ctx.fillStyle = 'red'
    ctx.fill()
    

    处理前后对比在这里插入图片描述在这里插入图片描述

  3. canvas处理图片

    • 通过index = (y * width + x) * 4可以得到点击位置(x,y)在上下文对象获取的图片像素数组中的rgba的R的下标
    const cvs: any = document.querySelector('canvas')
    const ctx: any = cvs?.getContext('2d')
    const img = new Image()
    img.src = '../picture.png'
    img.onload = () => {
      // 将画布宽高设置为图片宽高
      cvs.width = img.width
      cvs.height = img.height
      ctx.drawImage(img, 0, 0)
    }
    // 通过监听或vue获取画布的点击事件
    function canvasClick(e) {
      // 得到点击位置的坐标 (x,y)
      const x = e.offsetX
      const y = e.offsetY
      // canvas上下文能够获得图片上每一个像素点的颜色值
      const imgData = ctx.getImageData(0, 0, cvs.width, cvs.height)
      const colorClicked = getRgbaFromImgData(x, y, cvs.width, imgData)
      console.log(colorClicked) // [251, 217, 173, 255] 即 r、g、b、a
    }
    
    // 工具函数
    function getRgbaFromImgData(x, y, width, imgData) {
      const index = (y * width + x) * 4 // 获取点击位置在imgData中的下标的公式
      return [
        imgData.data[index], // R
        imgData.data[index + 1], // G
        imgData.data[index + 2], // B
        imgData.data[index + 3] // A
      ]
    }
    
    • 接下来只需要根据需求修改点击位置甚至附近的像素点颜色即可,即修改imgData.data[index]
    • 修改完成后用ctx.putImageData应用修改
    const greenColor = [0, 255, 0, 255]
    imgData.data.set(greenColor, index)
    ctx.putImageData(imgData, 0, 0)
    

    在这里插入图片描述
    在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChristmasFox&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值