摘要:以前在做移動端拍照調整圖片時遇到一些問題,現整理一下也當總結,有不對的地方望不吝賜教。問題移動圖片時畫面卡頓。解決避免直接改變元素的和,這樣會造成頁面的重繪,引起卡頓。
以前在做移動端拍照調整圖片時遇到一些問題,現整理一下也當總結,有不對的地方望不吝賜教。
問題1:移動圖片時畫面卡頓。
問題2:旋轉圖片時夾角問題(這個問題就是知道了不難,不知道就難,這也算是自己新了解到的一個知識點,所以提出來說一下)。
問題3:canvas繪圖iphone圖片被旋轉。
解決1:
避免直接改變元素的left和top,這樣會造成頁面的重繪,引起卡頓。可以使用translate
解決2:
用向量叉乘,首先定義兩個手指的開始點和結束點、調整數據和圖片的默認樣式,imgPosition在選擇圖片后顯示根據需要設置
// 手指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這幾個屬性是用來臨時存放上一次的數據,以便累加)
分別計算開始和結束時兩個手指的向量:公式
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)
計算兩個向量的角度:
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)/[√(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)) }
然后計算方向:
var direction = calculateVC(vector1, vector2) function calculateVC (vector1, vector2) { // 叉乘公式 return (vector1.x * vector2.y - vector2.x * vector1.y) > 0 ? 1 : -1 }
得到最終的旋轉角度
this.move.allDeg = direction * angle + this.move.temDeg
附上部分代碼:
// 調整開始 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,這里我用第二種方式。
正常情況下直接從圖庫里面選擇圖片,不會有被默認選旋轉的問題,直接畫出來就行了
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°為例。
如圖:一張圖片本來應該像虛線那樣,而在實際被旋轉成了實線那樣
直接將canvas旋轉,最后畫出來看到的是這樣
實際上我們什么都看不到,應為超出了可見范圍的左邊緣,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地址
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94469.html
前言 記得16年的時候我初入前端差不多一年,公司做了一個webapp,有上傳頭像功能,當時這個項目不是我在負責,測試的時候發現蘋果用戶拍照上傳頭像會翻轉,當時幾個前端的同學捯飭了一下午也沒解決,結果問題轉到我這里,還有半個小時下班;當時也是一臉懵逼,首先想到的是,這怎么判斷它是否翻轉了呢?安卓沒問題啊,有些蘋果手機相冊里面的圖片也沒問題啊,js能有這種功能判斷嗎?上網查資料,果不其然,有!那就是e...
摘要:決定自己寫一個移動端圖片上傳組件。允許多選,加上事件的回調函數。在的回調函數中,我們能通過拿到所選擇的文件,但是文件是無法展示在頁面上的,通常的做法是使用轉為然后展示在頁面上。 背景 前段時間項目重構,改成SSR的項目,但之前用的圖片選擇上傳組件不支持SSR(server-side-render)。遂進行了調研,發現很多的工具。但有的太大,有的使用麻煩,有的不滿足使用需求。決定自己寫一...
閱讀 1814·2021-09-22 15:54
閱讀 2923·2021-09-01 10:42
閱讀 3442·2019-08-30 15:56
閱讀 1432·2019-08-29 18:46
閱讀 2465·2019-08-29 10:57
閱讀 2703·2019-08-28 17:57
閱讀 3659·2019-08-23 18:14
閱讀 833·2019-08-23 17:03