在上一節中已經提到了預覽,預覽能夠經過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)上傳的控件就用「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步和第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);
在上圖中點擊確認就會自動合成。
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」都不行
參考資料: