canvas實現圖片馬賽克

ylethe發表於2019-02-27

1. 原生canvas實現用到的API

1) getContext(contextID) —返回一個用於在畫布上繪圖的環境

Canvas.getContext(`2d`) // 返回一個 CanvasRenderingContext2D 物件,使用它可以繪製到 Canvas 元素中
複製程式碼

2)drawImage

drawImage(imgObj, x, y)  // 按原圖大小繪製, x、y為圖片在畫布中的位置座標
drawImage(imgObj, x, y, width, height) // 按指定寬高繪製
drawImage(imgObj, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight) // 從原來圖片上某一個位置開始(sourceX,sourceY),指定長寬進行剪下(sourceWidth,sourceHeight),然後將剪下的內容放到位置為(destX,destY),寬度為(destWidth),高度為(destHeight)的位置上
複製程式碼
Image text

3) getImageData(x, y, width, height) —獲取矩形區域的影像資訊

ctx.getImageData(0, 0, 10, 10) // 獲取左上角座標為(0, 0),寬高為區域內的影像資訊
// 返回ImageData: { width: 10, height: 10, data: Uint8ClampedArray[400] }
複製程式碼

4)beginPath() —開始一條路徑,或重置當前的路徑
5)rect(x, y, width, height) —繪製矩形
6)lineWidth —設定或返回當前線條的寬度
7)fillStyle —設定或返回用於填充繪畫的顏色、漸變或模式

ctx.fillStyle = color|gradient|pattern
複製程式碼

8)strokeStyle —設定或返回用於筆觸的顏色、漸變或模式
9)globalAlpha —設定或返回繪圖的當前透明值
10)fill() —填充當前的影像(路徑)。預設顏色是黑色

【注】如果路徑未關閉,那麼 fill() 方法會從路徑結束點到開始點之間新增一條線,以關閉該路徑,然後填充該路徑。
複製程式碼

11)stroke() —會實際地繪製出通過 moveTo() 和 lineTo() 方法定義的路徑。預設顏色是黑色
12)toDataURL(type, encoderOptions) —匯出圖片,type為圖片型別, encoderOptions圖片質量,[0, 1]

Canvas.toDataURL("image/png", 1)
複製程式碼

2. fabric.js

簡化canvas編寫的庫,為canvas提供所缺少的物件模型

fabric.js能做的事

1)在canvas上建立、填充圖形(包括圖片、文字、規則圖形和複雜路徑組成圖形)
2)給圖形填充漸變顏色
3)組合圖形(包括組合圖形、圖形文字、圖片等)
4)設定圖形動畫集使用者互動
5)生成JSON, SVG資料等

3.使用fabric.js實現用到的API

1)宣告畫布

let canvas =new fabric.Canvas(`canvas`) {
   width: 200,
   height: 200
}
複製程式碼
  1. 插入圖片
let imgInstance = new fabric.Image(imgElement,{
  left: 0,
  top: 0,
  width: 100,
  height: 100,
  angle: 0
}
複製程式碼

3)設定背景圖片 setBackgroundImage

canvas.setBackgroundImage(imgInstance)
複製程式碼

4)renderAll() 重新繪製
5)on() 使用者互動

canvas.on(`mouse:down`, function(options) {  
   console.log(options.e.clientX, options.e.clientY)  
})

// 監聽事件
/* 
   mouse:down :滑鼠按下時
   mouse:move :滑鼠移動時
   mouse:up :滑鼠抬起時
   after:render :畫布重繪後
   object:selected:物件被選中
   object:moving:物件移動
   object:rotating:物件被旋轉
   object:added:物件被加入
   object:removed物件被移除 
*/
複製程式碼

6)getPointer()
7)setWidth()、setHeight() 設定canvas的寬高
8)畫矩形

let rect = new fabric.Rect({
 left: 0,
 top: 0,
 width: 100,
 height: 100
})

複製程式碼
  1. add(obj) 新增圖形
canvas.add(rect)
複製程式碼

10)remove(obj) 移除圖形
11)set() 設定物件內容
12)toDataURL(obj)

4.學習推薦

1)學習原生canvas

慕課網:HTML5小遊戲—愛心魚

2)學習fabric.js

  • API文件:
    http://fabricjs.com/docs/index.html
  • 相關文章:
    http://blog.csdn.net/mnhn456/article/details/76869672
    https://segmentfault.com/a/1190000004864237

5.原生canvas實現程式碼

<template>
<div class="container">
  <div class="operations">
    <ul>
      <li @click="mosaic">馬賽克</li>
      <li @click="addText">新增文字</li>
      <li @click="tailor">裁剪</li>
      <li @click="rotate">旋轉</li>
      <li @click="exportImg">匯出圖片</li>
    </ul>
  </div>
  <canvas ref="imgContent" class="img-wrap">
    你的瀏覽器太low?
  </canvas>
</div>
</template>

<script>
  export default {
    data () {
      return {
        context: ``,
        canvas: ``,
        isMasic: false,
        isText: false,
        isTailor: false,
        isTranslate: false,
        squareEdgeLength: 20,
        angle: 0,
        img: ``
      }
    },
    mounted () {
      this.initData()
    },
    methods: {
      initData () {
        let imgContent = this.$refs.imgContent
        this.canvas = imgContent
        this.context = imgContent.getContext(`2d`)
        let  Img = new Image()
        this.image = Img
        Img.crossOrigin = "Anonymous"
        Img.src = `http://oia85104s.bkt.clouddn.com/PictureUnlock_193139.pictureunlock.jpg`
        this.canvas.setAttribute(`width`, Img.width)
        this.canvas.setAttribute(`height`, Img.height)
        let self = this
        Img.onload = () => {
          let beginX, beginY, endX, endY
          self.context.drawImage(Img, 0, 0)
          self.context.save()

          self.canvas.addEventListener(`mousedown`, e => {
            beginX = e.offsetX
            beginY = e.offsetY
            self.canvas.addEventListener(`mouseup`, e => {
              endX = e.offsetX
              endY = e.offsetY
              if (self.isMasic) {
                self.makeGrid(beginX, beginY, endX - beginX, endY - beginY)
                return
              }
              if (self.isTailor) {
                self.context.drawImage(Img, beginX, beginY, endX - beginX, endY - beginY, 0, 0, endX - beginX, endY - beginY)
                return
              }
            })
          })
        }
      },
      drawRect  (x, y, width, height, fillStyle, lineWidth, strokeStyle, globalAlpha) {
        this.context.beginPath()
        this.context.rect(x, y, width, height)
        this.context.lineWidth = lineWidth
        this.context.strokeStyle = strokeStyle
        fillStyle && (this.context.fillStyle = fillStyle)
        globalAlpha && (this.context.globalAlpha = globalAlpha)

        this.context.fill()
        this.context.stroke()
      },
      // 打馬賽克
      mosaic () {
        let self = this
        this.resetClickStatus()
        this.isMasic = true
      },
      makeGrid (beginX, beginY, rectWidth, rectHight) {
        const row = Math.round(rectWidth / this.squareEdgeLength) + 1
        const column = Math.round(rectHight / this.squareEdgeLength) + 1
        for (let i = 0; i < row * column; i++) {
          let x = (i % row) * this.squareEdgeLength + beginX
          let y = parseInt(i / row) * this.squareEdgeLength + beginY
          this.setColor(x, y)
        }
      },
      setColor (x, y) {
        const imgData = this.context.getImageData(x, y, this.squareEdgeLength, this.squareEdgeLength).data
        let r = 0, g = 0, b = 0
        console.log(this.context.getImageData(x, y, this.squareEdgeLength, this.squareEdgeLength), JSON.stringify(imgData))
        for (let i = 0; i < imgData.length; i += 4) {
          r += imgData[i]
          g += imgData[i + 1]
          b += imgData[i + 2]
        }
        r = Math.round(r / (imgData.length / 4))
        g = Math.round(g / (imgData.length / 4))
        b = Math.round(b / (imgData.length / 4))
        this.drawRect(x, y, this.squareEdgeLength, this.squareEdgeLength, `rgb(${r}, ${g}, ${b})`, 2, `rgb(${r}, ${g}, ${b})`)
      },
      // 新增文字
      addText () {
        this.resetClickStatus()
        this.isText = true
        console.log(`新增文字`)
      },
      // 裁剪
      tailor () {
        this.resetClickStatus()
        this.isTailor = true
        console.log(`裁剪`)
      } ,
      // 旋轉
      rotate () {
        // if (this.angle === 360) {
        //   this.angle = 90
        // } else {
        //   this.angle += 90
        // }
        // if ([90, 270].includes(this.angle)) {
        //   this.canvas.setAttribute(`width`, this.image.height)
        //   this.canvas.setAttribute(`height`, this.image.width)
        // } else {
        //   this.canvas.setAttribute(`width`, this.image.width)
        //   this.canvas.setAttribute(`height`, this.image.height)
        // }
        const x = this.image.width / 2
        const y = this.image.height / 2
        this.context.clearRect(0,0, this.canvas.width, this.canvas.height)  // 清理畫布內容
        this.context.translate(x, y)
        this.context.rotate(90 * Math.PI / 180)
        this.context.translate(-x, -y)
        this.context.drawImage(this.image, 0, 0)
      },
      resetClickStatus () {
        this.isMasic = false
        this.isText = false
        this.isTailor = false
        this.isTranslate = false
      },
      exportImg () {
        this.resetClickStatus()
        const exportUrl = this.canvas.toDataURL("image/jpeg")
        let a = document.createElement(`a`)
        a.setAttribute(`download`, ``)
        a.href = exportUrl
        document.body.appendChild(a)
        a.click()
      }
    }
  }
</script>

<style scoped lang="less">
.operations {
  width: 1200px;
  margin: 0 auto;
  ul {
    display: flex;
    align-items: center;
    margin-bottom: 30px;
    li {
      list-style: none;
      margin-right: 20px;
      cursor: pointer;
    }
  }
}
.img-wrap {
  display: block;
  margin: 0 auto;
}
</style>
複製程式碼

效果圖如下:

Image text

相關文章