移動端圖片操做(二)——預覽、旋轉、合成

在上一節中已經提到了預覽,預覽能夠經過data: URL格式或URL對象。javascript

var file = upload.files[0];
//URL對象
var url = URL.createObjectURL(file);
var img = new Image();
img.style.width = '100%';
img.src = url;
img.onload = function(e) {
  window.URL.revokeObjectURL(this.src); //銷燬
}
//data:URL格式
var img = new Image();
img.style.width = '100%';
var reader = new FileReader();
reader.onload = function() {
  img.src = this.result;
};
reader.readAsDataURL(file);

一切都很順利,但其實有不少的坑,須要慢慢講來。先從前面作的一個小功能提及。html

這個小功能就是將兩張圖片合成起來,組成一張新的圖片前端

 

1、技術實現

1)上傳的控件就用「input[type=file]」實現html5

Android的webview不支持此控件,怪不得按鈕怎麼按都按不動,後面只得用客戶端提供的接口作上傳java

期間發現UC瀏覽器會自動將上傳控件包裝成三個按鈕的樣子,只需設置爲「opacity:0;」便可。ios

2)使用「FileReader」,經過方法readAsDataURL獲取data URL格式的圖片內容,賦值給image。git

IOS在上傳圖片或拍照的時候,顯示出來的寬高是反的,這個時候咱們增長了一步旋轉的操做,讓用戶本身來控制。github

在給image賦src值的同時,賦「crossOrigin」的值,用於後面的圖片跨域web

3)將圖片畫在「canvas」上,旋轉其實也就是轉這個canvas。經過計算座標和角度實現旋轉gulp

4)將旋轉後的「canvas」與剩下的兩張文字圖片,經過一個新的「canvas」合併到一塊兒。

在將兩個「canvas」畫在一塊兒後,發現經過webview上傳的圖片,不能使用「toDataURL()」方法了,這是由於畫布被污染,第一個畫布中的圖片是跨域的。

5)最後將data URL格式的內容上傳到服務器中保存。

總共開發時間是3天,在這個過程當中有過幾回技術推翻重作或者邏輯重整,兼容性方面也還有很大問題,包括圖片的寬高的自適應等,還很不完善。

下圖是最終效果,挑了張鬆獅的寫真照:

 

2、預覽

原先我在作的時候,第2步和第3布是合在一塊兒的,也就是圖片顯示出來的時候,已經校準好位置。

只是在實現的過程當中碰到了不少麻煩,而且時間倉促,BUG修復起來比較費時,只得分兩步。

先說說原先的過程

1)IOS圖片上傳或拍照,寬高相反

我用的是Android手機,剛開始並無發現這個問題,後面別人幫忙測試的時候,才發現了這個重大問題,接下來就是折騰這個事兒,想要自動校訂方向。

1. 經過exif.js獲取圖片元數據,經過獲取「Orientation」屬性判斷方向【個人Android機中這個屬性爲undefined,IOS有值】,總共有8個值

 

左邊是說明,右邊是展現。表格中帶「*」的是指翻轉過來了。

接下來就是作計算,網上有不少計算方式,對於「>IOS7」的系統,計算邏輯能夠參考下localResizeIMG插件195行

順便說下,這個插件使用到了gulp的開發方式,若是要調試就要配置相關代碼,能夠參考《前端自動化構建工具gulp記錄

插件中還大量使用了Promises/A+規範,關於這個規範能夠參考《JavaScript中Promises/A+規範的實現

exif.getData(typeof file === 'object' ? file : img, function() {
  orientation = exif.getTag(this, "Orientation");
  //計算邏輯....
});

2. 「<=IOS7」系統的計算方式略有不一樣。IOS6中圖片拍照上傳會被壓扁(當照片超過2M時),這是IOS6的BUG,較大的圖片可能會發生。

計算邏輯也能夠參考localResizeIMG插件165行。要解決這個BUG須要引入megapix-image.js

require(['megapix-image'], function(MegaPixImage) {
  var mpImg = new MegaPixImage(img);
  //計算邏輯
});

 

2)將畫布「canvas」經過方法「toDataURL」,變成data URL格式

1. 在手機QQ瀏覽器中,canvas對象使用toDataURL方法獲取不到任何數據,須要引用jpeg-encoder.js解決。

var cvs = document.createElement('canvas');
var ctx = cvs.getContext("2d");
ctx.drawImage(theImg, 0, 0);
var theImgData = ctx.getImageData(0, 0, cvs.width, cvs.height);
// Encode the image and get a URI back, toRaw is false by default
var jpegURI = encoder.encode(theImgData, quality);

 

在開發的時候方向自動旋轉花了不少時間。碰到各類問題,例如「orientation」屬性獲取不到、自動旋轉的角度不對。

後面貪簡單,就找了個canvasResize插件,雖然會自動校訂,可是圖片有點模糊,並且圖片的寬高不容易控制。效果不盡如意,只好分兩步作。

最終的過程

1)添加旋轉頁面

先計算起始座標點X與Y,簡單點就是寬度和高度各除以2,再經過canvas的「translate」,再用「rotate」計算角度。在「drawImage」的時候X和Y點變成負數,移位過去。

var xpos = canvas_width / 2;
var ypos = canvas_height / 2;
ctx.translate(xpos, ypos);
ctx.rotate(angle * Math.PI / 180);
ctx.drawImage(rotate_img, -width / 2, -height / 2, width, height);

 

3、合成

在上圖中點擊確認就會自動合成。

1)圖片寬高自適應

畫布的寬高只作了簡單的比率,畫布的寬度除以圖片的寬度,我把頭部的圖片和底部的圖片寬度都作的相同,下圖所示:

 

var multiple = width / header.width;//畫布寬度除以圖片寬度

var header_height = header.height * multiple;//頂部圖片
var footer_height = footer.height * multiple;//底部圖片
var footer_y = canvas_height - footer_height;

 

2)將頂部與底部圖片、與上一個旋轉後的canvas畫一塊兒在第二個畫布上

ctx.drawImage(rotate_canvas, 0, 0, width, height);
ctx.drawImage(header, 0, 25, width, header_height);
ctx.drawImage(footer, 0, footer_y, width, footer_height);

這裏碰到了一個畫布污染的問題。上面的「rotate_canvas」,圖片的獲取有兩種,一個是經過「file」控件上傳的,另外一個是在webview提供的接口上傳的。

兩個惟一的不一樣是跨域,第二個接口返回的是另一個域名中的圖片。圖片跨域了,那麼rotate_canvas也算是跨域了。

跨域的話,會影響「toDataURL()」方法。這個時候就須要img圖片跨域。

1. 圖片設置crossOrigin屬性,這是一個HTML5屬性,兼容性不是很好,測試了幾臺Android機,有的行,有的不行。「crossOrigin」屬性設置要在「src」以前,不然IOS不可以使用

rotate_img.crossOrigin = "Anonymous";

2. 服務器須要設置,能夠正確響應 Access-Control-Allow-Origin 頭。

調試的時候,若是服務器還沒配置,能夠用Fiddler模擬響應頭。

a. 先找到filter選項

 

b. 設置請求頭

 

c. 設置響應頭,注意與請求頭中的內容要如出一轍,少個「http」都不行

 

 

參考資料:

H5拍照應用開發經歷的那些坑兒

圖片自動旋轉的前端實現方案

JPEG Rotation and EXIF Orientation

利用exif.js解決ios手機上傳豎拍照片旋轉90度問題

Html5 create thumbnail

相關文章
相關標籤/搜索