canvas(一)移動端拍照調整

之前在作移動端拍照調整圖片時遇到一些問題,現整理一下也當總結,有不對的地方望不吝賜教。
問題1:移動圖片時畫面卡頓。
問題2:旋轉圖片時夾角問題(這個問題就是知道了不難,不知道就難,這也算是本身新瞭解到的一個知識點,因此提出來講一下)。
問題3:canvas繪圖iphone圖片被旋轉。git

解決1:
避免直接改變元素的left和top,這樣會形成頁面的重繪,引發卡頓。可使用translategithub

解決2:
用向量叉乘,首先定義兩個手指的開始點和結束點、調整數據和圖片的默認樣式,imgPosition在選擇圖片後顯示根據須要設置canvas

// 手指A
        fingerA: {
          startX: 0,
          startY: 0,
          endX: 0,
          endY: 0
        },
        // 手指B
        fingerB: {
          startX: 0,
          startY: 0,
          endX: 0,
          endY: 0
        },
        // 調整數據
        move: {
          x: 0,
          y: 0,
          temX: 0,
          temY: 0,
          scale: 1,
          temScale: 1,
          allDeg: 0,
          temDeg: 0
        },
        // 默認樣式
        imgPosition: {
          left: 0,
          top: 0,
          width: 0,
          height: 0
        },

因爲用的translate和scale,因此本次調整的數據必定要加上上一次的(temX,temY,temScale,temDeg這幾個屬性是用來臨時存放上一次的數據,以便累加)iphone

分別計算開始和結束時兩個手指的向量:公式this

function Vector (x1, y1, x2, y2) {
    this.x = x2 - x1
    this.y = y2 - y1
  }
// 開始兩個手指的向量
var vector1 = new Vector(this.fingerA.startX, this.fingerA.startY, this.fingerB.startX, this.fingerB.startY)
// 結束時兩個手指的向量
var vector2 = new Vector(this.fingerA.endX, this.fingerA.endY, this.fingerB.endX, this.fingerB.endY)

計算兩個向量的角度:spa

var cos = calculateVM(vector1, vector2)
var angle = Math.acos(cos) * 180 / Math.PI
function calculateVM (vector1, vector2) {
  /*
  * 向量夾角公式:cosθ=向量a×向量b/|向量a|×|向量b|
  * 設向量a=(x1,y1),向量b=(x2,y2)
  * 則 cosθ= 向量a.向量b/|向量a|×|向量b| =(x1x2+y1y2)/[√(x1²+y1²)*√(x2²+y2²)]
  */
  return (vector1.x * vector2.x + vector1.y * vector2.y) / (Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y) * Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y))
  }

而後計算方向:插件

var direction = calculateVC(vector1, vector2)
function calculateVC (vector1, vector2) {
  // 叉乘公式
  return (vector1.x * vector2.y - vector2.x * vector1.y) > 0 ? 1 : -1
}

獲得最終的旋轉角度rest

this.move.allDeg = direction * angle + this.move.temDeg

附上部分代碼:code

// 調整開始
      adjustStart: function (e) {
        let event = e.targetTouches
        this.fingerA.startX = event[0].pageX
        this.fingerA.startY = event[0].pageY
        // 移動
        if (event.length === 1) {
          this.isDrag = true
          this.isScale = false
        // 縮放
        } else if (event.length === 2) {
          this.isScale = true
          this.isDrag = false
          this.fingerB.startX = event[1].pageX
          this.fingerB.startY = event[1].pageY
        }
      },
      
      // 調整中,移動或縮放
      adjustIng: function (e) {
        let event = e.targetTouches
        this.fingerA.endX = event[0].pageX
        this.fingerA.endY = event[0].pageY
        // 移動
        if (this.isDrag) {
          // 本次移動距離要加上以前移動的距離
          this.move.x = this.fingerA.endX - this.fingerA.startX + this.move.temX
          this.move.y = this.fingerA.endY - this.fingerA.startY + this.move.temY
        } else if (this.isScale) {
          // 縮放
          this.fingerB.endX = event[1].pageX
          this.fingerB.endY = event[1].pageY
          // 兩手指間距離
          let distanceStart = Math.sqrt(Math.pow(this.fingerA.startX - this.fingerB.startX, 2) + Math.pow(this.fingerA.startY - this.fingerB.startY, 2))
          let distanceEnd = Math.sqrt(Math.pow(this.fingerA.endX - this.fingerB.endX, 2) + Math.pow(this.fingerA.endY - this.fingerB.endY, 2))
          this.move.scale = distanceEnd / distanceStart * this.move.temScale
          // 向量叉乘,求出旋轉方向及角度
          // 開始兩個手指的向量
          var vector1 = new Vector(this.fingerA.startX, this.fingerA.startY, this.fingerB.startX, this.fingerB.startY)
          // 結束時兩個手指的向量
          var vector2 = new Vector(this.fingerA.endX, this.fingerA.endY, this.fingerB.endX, this.fingerB.endY)
          var cos = calculateVM(vector1, vector2)
          var angle = Math.acos(cos) * 180 / Math.PI
          var direction = calculateVC(vector1, vector2)
          this.move.allDeg = direction * angle + this.move.temDeg
        }
      },
      
      // 調整結束
      adjustEnd: function (e) {
        this.move.temX = this.move.x
        this.move.temY = this.move.y
        this.move.temScale = this.move.scale
        this.move.temDeg = this.move.allDeg
        this.isDrag = false
        this.isScale = false
      },

解決3:
如今已獲得圖片的調整數據,開始進行canvas繪製,這裏有兩種方式,能夠把this.move.scale換算成left和top,也能夠直接用canvas的scale,這裏我用第二種方式。
正常狀況下直接從圖庫裏面選擇圖片,不會有被默認選旋轉的問題,直接畫出來就好了blog

ctxTemp.save()
        ctxTemp.translate(cx, cy)
        ctxTemp.rotate(Math.PI / 180 * move.allDeg)
        ctxTemp.scale(move.scale, move.scale)
        ctxTemp.translate(-cx, -cy)
        ctxTemp.drawImage(fileData.image, moveLeft, moveTop, drawWidth, drawHeight)
        ctxTemp.restore()

注:個人到的調整數據都是基於圖片的中心點,因此在旋轉縮放canvas時也要設置中心點,即上面代碼裏面的(cx,cy),由(left + w/2, top + h/2)獲得,下面解決圖片被默認旋轉的狀況,思路:先把拍照的默認角度糾正,在按照普通圖片處理方式進行,這裏用了插件exif-js.js獲得圖片的默認信息(默認被旋轉的角度),這裏以90°爲例。
如圖:一張圖片原本應該像虛線那樣,而在實際被旋轉成了實線那樣
clipboard.png

直接將canvas旋轉,最後畫出來看到的是這樣

clipboard.png

實際上咱們什麼都看不到,應爲超出了可見範圍的左邊緣,canvas默認的源點都是座標系的左上角,注意看圖,如今咱們用drawImage畫圖,裏面的參數都是left和top互換,w和h互換,要讓圖再畫到可視區域,和沒有旋轉同樣,y方向要變成負,便是-l,drawImage(img, t, -l, h, w),解決默認旋轉後,在進行接下來的操做,中心點也要相應改變:

ctxTemp.save()
        ctxTemp.rotate(Math.PI / 180 * 90)
        // 座標系變化注意正負
        ctxTemp.translate(cy, -cx)
        ctxTemp.rotate(Math.PI / 180 * move.allDeg)
        ctxTemp.scale(move.scale, move.scale)
        ctxTemp.translate(-cy, cx)
        ctxTemp.drawImage(fileData.image, moveTop, -(moveLeft + drawWidth), drawHeight, drawWidth)
        ctxTemp.restore()

以前是(cx, cy),如今是(cy, -cx)(90°之外的狀況作相應改變)
部分代碼:

/*
  * @imgPosition:圖片信息
  * @orient: 系統旋轉的角度標識
  * @move:調整數據
  * @fileData: 文件信息
  * @clip:剪裁框信息
  * @fun:回調
  */
  function drawImg (imgPosition, orient, move, fileData, clip, fun) {
    if (fileData.image) {
      var canvasTemp = document.createElement('canvas')
      var ctxTemp = canvasTemp.getContext('2d')
      // canvas寬
      var w = 480
      // 剪裁框和canvas之比
      var ratio = w / clip.clipWidth
      // canvas高
      var h = ratio * clip.clipHeight
      canvasTemp.height = h
      canvasTemp.width = w
      // 中心點
      var cx = (imgPosition.left + move.x + imgPosition.width / 2) * ratio
      var cy = (imgPosition.top + move.y + imgPosition.height / 2) * ratio
      // 圖片相對於canvas的left
      let moveLeft = (imgPosition.left + move.x) * ratio
      // 圖片相對於canvas的top
      let moveTop = (imgPosition.top + move.y) * ratio
      // 圖片和canvas的等比寬
      let drawWidth = imgPosition.width * ratio
      // 圖片和canvas的等比高
      let drawHeight = imgPosition.height * ratio
      // 90°
      if (orient === 6) {
        ctxTemp.save()
        ctxTemp.rotate(Math.PI / 180 * 90)
        // 座標系變化注意正負
        ctxTemp.translate(cy, -cx)
        ctxTemp.rotate(Math.PI / 180 * move.allDeg)
        ctxTemp.scale(move.scale, move.scale)
        // 還原座標系
        ctxTemp.translate(-cy, cx)
        ctxTemp.drawImage(fileData.image, moveTop, -(moveLeft + drawWidth), drawHeight, drawWidth)
        ctxTemp.restore()
      } else {
        ctxTemp.save()
        ctxTemp.translate(cx, cy)
        ctxTemp.rotate(Math.PI / 180 * move.allDeg)
        ctxTemp.scale(move.scale, move.scale)
        // 還原座標系
        ctxTemp.translate(-cx, -cy)
        ctxTemp.drawImage(fileData.image, moveLeft, moveTop, drawWidth, drawHeight)
        ctxTemp.restore()
      }
      var base64 = canvasTemp.toDataURL('image/jpeg', 0.8)
      // console.log(base64)
      fun(base64)
    }

寫得有錯或很差還望你們賜教demo地址

相關文章
相關標籤/搜索