canvas给图片打马赛克

494 阅读2分钟

image.png 开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

前言

自己尝试写的一个马赛克效果,

在图片上拖动鼠标给经过的地方打上马赛克

效果如下:

image.png

一,源码

<canvas width="800px" height="400px"></canvas>
<button>清除马赛克</button>
    const canvas = document.querySelector('canvas')
    const button = document.querySelector('button')
    if(canvas.getContext){
      const context = canvas.getContext('2d')
      var oldImageData = null
      //1 插入图片到画布
      let img = new Image()
      img.src = '../img/test2.png'
      img.onload = ()=>{
        context.drawImage(img, 0, 0, 800, 400);
        oldImageData = context.getImageData(0, 0, 800, 400);
      }  
      let flag = false
      let size = 20
      //2获取xy
      canvas.onmousedown = (e)=>{
        flag = true
        const x = e.clientX - canvas.offsetLeft
        const y = e.clientY - canvas.offsetTop
        draw(size,x,y)
      }
      canvas.onmousemove = (e)=>{
        if(!flag) return
        const x = e.clientX - canvas.offsetLeft
        const y = e.clientY - canvas.offsetTop
        draw(size,x,y)
      }
      canvas.onmouseup = (e)=>{
        flag = false
      }
      //马赛克
      function draw(size,x,y){
        let imageData = context.getImageData(0, 0, 800, 400);
       //3,随机像素点 = x 加上 [0,size)的随机数
       let color = getPXInfo(imageData,
              x + Math.floor(Math.random()*size),
              y + Math.floor(Math.random()*size))
       //4,更改矩形颜色,xy位置加上0-size,来变更矩形rgba为color
        for(let a=0; a<size; a++) {
            for(let b=0; b<size; b++){
              setPXInfo(imageData,x+a,y+b,color)
          }
        }
        context.putImageData(imageData, 0, 0)
      }
      button.onclick = ()=>{
        let imageData = context.getImageData(0, 0, 800, 400);
        for(let i=0; i<oldImageData.data.length; i++){
           imageData.data[i] = oldImageData.data[i]
        }
        context.putImageData(imageData, 0, 0);
                
      }
      //封装好获取的单像素方法
      function getPXInfo(imageData,x,y){
        let data = imageData.data
        let w = imageData.width
        let h = imageData.height
        let color = []
        //(y*w+x)*4  y多少行*宽+x列前面多少个*4(4代表rgba) 得到坐标最终索引获取color
        color[0] = data[(y*w+x)*4]//R -> 0~255
        color[1] = data[(y*w+x)*4+1]//G -> 0~255
        color[2] = data[(y*w+x)*4+2]//B -> 0~255
        color[3] = data[(y*w+x)*4+3]//A -> 0~255
        return color
      }
      function setPXInfo(imageData,x,y,color) { 
        let data = imageData.data
        let w = imageData.width
        let h = imageData.height 
        data[(y*w+x)*4] = color[0]
        data[(y*w+x)*4+1] = color[1]
        data[(y*w+x)*4+2] = color[2]
        data[(y*w+x)*4+3] = color[3]
      }
      }
  }

二,解析代码

  1. 马赛克实现思路
    • 获取鼠标xy坐标,并且设置一个size矩形范围
    • x+size,y+size 就是一个矩形啦, 从这个矩形中随机抽取一个像素点
    • 将抽取的像素点,改为整个矩形的颜色
  2. canvas中一个像素点是由rgba组成,值 为0-255
  3. 由于我的图片太大,我就设置固定了

相关api

插入图片: ctx.drawImage(image图片对象,x,y,width,height);xy为起始坐标,
获取像素:const myImageData = ctx.getImageData(sx,sy,sw,sh) 包含画布场景数据的ImageData对象
        返回值:width
              height
              data:数组包含每一个像素点          
                    100width * 100height 是一万个像素点,但是data中会显示长度4万个
                    原因就是一个像素点由RGBA组成,RGBA都是(0~255 黑->白  0~255透明到不透明)

写入像素:ctx.putImageData(myImageData,dx,dy)方法对场景进行像素数据的写入             
            dx,dy左上角绘制坐标。            
            myImageData要插入的像素数据,