成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

canvas(一)移動(dòng)端拍照調(diào)整

Markxu / 2129人閱讀

摘要:以前在做移動(dòng)端拍照調(diào)整圖片時(shí)遇到一些問(wèn)題,現(xiàn)整理一下也當(dāng)總結(jié),有不對(duì)的地方望不吝賜教。問(wèn)題移動(dòng)圖片時(shí)畫面卡頓。解決避免直接改變?cè)氐暮?,這樣會(huì)造成頁(yè)面的重繪,引起卡頓。

以前在做移動(dòng)端拍照調(diào)整圖片時(shí)遇到一些問(wèn)題,現(xiàn)整理一下也當(dāng)總結(jié),有不對(duì)的地方望不吝賜教。
問(wèn)題1:移動(dòng)圖片時(shí)畫面卡頓。
問(wèn)題2:旋轉(zhuǎn)圖片時(shí)夾角問(wèn)題(這個(gè)問(wèn)題就是知道了不難,不知道就難,這也算是自己新了解到的一個(gè)知識(shí)點(diǎn),所以提出來(lái)說(shuō)一下)。
問(wèn)題3:canvas繪圖iphone圖片被旋轉(zhuǎn)。

解決1:
避免直接改變?cè)氐膌eft和top,這樣會(huì)造成頁(yè)面的重繪,引起卡頓??梢允褂胻ranslate

解決2:
用向量叉乘,首先定義兩個(gè)手指的開(kāi)始點(diǎn)和結(jié)束點(diǎn)、調(diào)整數(shù)據(jù)和圖片的默認(rèn)樣式,imgPosition在選擇圖片后顯示根據(jù)需要設(shè)置

        // 手指A
        fingerA: {
          startX: 0,
          startY: 0,
          endX: 0,
          endY: 0
        },
        // 手指B
        fingerB: {
          startX: 0,
          startY: 0,
          endX: 0,
          endY: 0
        },
        // 調(diào)整數(shù)據(jù)
        move: {
          x: 0,
          y: 0,
          temX: 0,
          temY: 0,
          scale: 1,
          temScale: 1,
          allDeg: 0,
          temDeg: 0
        },
        // 默認(rèn)樣式
        imgPosition: {
          left: 0,
          top: 0,
          width: 0,
          height: 0
        },

由于用的translate和scale,所以本次調(diào)整的數(shù)據(jù)一定要加上上一次的(temX,temY,temScale,temDeg這幾個(gè)屬性是用來(lái)臨時(shí)存放上一次的數(shù)據(jù),以便累加)

分別計(jì)算開(kāi)始和結(jié)束時(shí)兩個(gè)手指的向量:公式

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

計(jì)算兩個(gè)向量的角度:

var cos = calculateVM(vector1, vector2)
var angle = Math.acos(cos) * 180 / Math.PI
function calculateVM (vector1, vector2) {
  /*
  * 向量夾角公式:cosθ=向量a×向量b/|向量a|×|向量b|
  * 設(shè)向量a=(x1,y1),向量b=(x2,y2)
  * 則 cosθ= 向量a.向量b/|向量a|×|向量b| =(x1x2+y1y2)/[√(x12+y12)*√(x22+y22)]
  */
  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))
  }

然后計(jì)算方向:

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

得到最終的旋轉(zhuǎn)角度

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

附上部分代碼:

      // 調(diào)整開(kāi)始
      adjustStart: function (e) {
        let event = e.targetTouches
        this.fingerA.startX = event[0].pageX
        this.fingerA.startY = event[0].pageY
        // 移動(dòng)
        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
        }
      },
      
      // 調(diào)整中,移動(dòng)或縮放
      adjustIng: function (e) {
        let event = e.targetTouches
        this.fingerA.endX = event[0].pageX
        this.fingerA.endY = event[0].pageY
        // 移動(dòng)
        if (this.isDrag) {
          // 本次移動(dòng)距離要加上之前移動(dòng)的距離
          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
          // 向量叉乘,求出旋轉(zhuǎn)方向及角度
          // 開(kāi)始兩個(gè)手指的向量
          var vector1 = new Vector(this.fingerA.startX, this.fingerA.startY, this.fingerB.startX, this.fingerB.startY)
          // 結(jié)束時(shí)兩個(gè)手指的向量
          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
        }
      },
      
      // 調(diào)整結(jié)束
      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:
現(xiàn)在已得到圖片的調(diào)整數(shù)據(jù),開(kāi)始進(jìn)行canvas繪制,這里有兩種方式,可以把this.move.scale換算成left和top,也可以直接用canvas的scale,這里我用第二種方式。
正常情況下直接從圖庫(kù)里面選擇圖片,不會(huì)有被默認(rèn)選旋轉(zhuǎn)的問(wèn)題,直接畫出來(lái)就行了

        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()

注:我的到的調(diào)整數(shù)據(jù)都是基于圖片的中心點(diǎn),所以在旋轉(zhuǎn)縮放canvas時(shí)也要設(shè)置中心點(diǎn),即上面代碼里面的(cx,cy),由(left + w/2, top + h/2)得到,下面解決圖片被默認(rèn)旋轉(zhuǎn)的情況,思路:先把拍照的默認(rèn)角度糾正,在按照普通圖片處理方式進(jìn)行,這里用了插件exif-js.js得到圖片的默認(rèn)信息(默認(rèn)被旋轉(zhuǎn)的角度),這里以90°為例。
如圖:一張圖片本來(lái)應(yīng)該像虛線那樣,而在實(shí)際被旋轉(zhuǎn)成了實(shí)線那樣

直接將canvas旋轉(zhuǎn),最后畫出來(lái)看到的是這樣

實(shí)際上我們什么都看不到,應(yīng)為超出了可見(jiàn)范圍的左邊緣,canvas默認(rèn)的源點(diǎn)都是坐標(biāo)系的左上角,注意看圖,現(xiàn)在我們用drawImage畫圖,里面的參數(shù)都是left和top互換,w和h互換,要讓圖再畫到可視區(qū)域,和沒(méi)有旋轉(zhuǎn)一樣,y方向要變成負(fù),即是-l,drawImage(img, t, -l, h, w),解決默認(rèn)旋轉(zhuǎn)后,在進(jìn)行接下來(lái)的操作,中心點(diǎn)也要相應(yīng)改變:

        ctxTemp.save()
        ctxTemp.rotate(Math.PI / 180 * 90)
        // 坐標(biāo)系變化注意正負(fù)
        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),現(xiàn)在是(cy, -cx)(90°以外的情況做相應(yīng)改變)
部分代碼:

/*
  * @imgPosition:圖片信息
  * @orient: 系統(tǒng)旋轉(zhuǎn)的角度標(biāo)識(shí)
  * @move:調(diào)整數(shù)據(jù)
  * @fileData: 文件信息
  * @clip:剪裁框信息
  * @fun:回調(diào)
  */
  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
      // 中心點(diǎn)
      var cx = (imgPosition.left + move.x + imgPosition.width / 2) * ratio
      var cy = (imgPosition.top + move.y + imgPosition.height / 2) * ratio
      // 圖片相對(duì)于canvas的left
      let moveLeft = (imgPosition.left + move.x) * ratio
      // 圖片相對(duì)于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)
        // 坐標(biāo)系變化注意正負(fù)
        ctxTemp.translate(cy, -cx)
        ctxTemp.rotate(Math.PI / 180 * move.allDeg)
        ctxTemp.scale(move.scale, move.scale)
        // 還原坐標(biāo)系
        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)
        // 還原坐標(biāo)系
        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)
    }

寫得有錯(cuò)或不好還望大家賜教demo地址

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94469.html

相關(guān)文章

  • 用exfe.js和canvas解決移動(dòng) IOS 拍照上傳圖片翻轉(zhuǎn)問(wèn)題

    前言 記得16年的時(shí)候我初入前端差不多一年,公司做了一個(gè)webapp,有上傳頭像功能,當(dāng)時(shí)這個(gè)項(xiàng)目不是我在負(fù)責(zé),測(cè)試的時(shí)候發(fā)現(xiàn)蘋果用戶拍照上傳頭像會(huì)翻轉(zhuǎn),當(dāng)時(shí)幾個(gè)前端的同學(xué)捯飭了一下午也沒(méi)解決,結(jié)果問(wèn)題轉(zhuǎn)到我這里,還有半個(gè)小時(shí)下班;當(dāng)時(shí)也是一臉懵逼,首先想到的是,這怎么判斷它是否翻轉(zhuǎn)了呢?安卓沒(méi)問(wèn)題啊,有些蘋果手機(jī)相冊(cè)里面的圖片也沒(méi)問(wèn)題啊,js能有這種功能判斷嗎?上網(wǎng)查資料,果不其然,有!那就是e...

    leap_frog 評(píng)論0 收藏0
  • H5拍照、選擇圖片上傳組件核心

    摘要:決定自己寫一個(gè)移動(dòng)端圖片上傳組件。允許多選,加上事件的回調(diào)函數(shù)。在的回調(diào)函數(shù)中,我們能通過(guò)拿到所選擇的文件,但是文件是無(wú)法展示在頁(yè)面上的,通常的做法是使用轉(zhuǎn)為然后展示在頁(yè)面上。 背景 前段時(shí)間項(xiàng)目重構(gòu),改成SSR的項(xiàng)目,但之前用的圖片選擇上傳組件不支持SSR(server-side-render)。遂進(jìn)行了調(diào)研,發(fā)現(xiàn)很多的工具。但有的太大,有的使用麻煩,有的不滿足使用需求。決定自己寫一...

    Guakin_Huang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<