最近接到一個需求,微信中用戶上傳圖片生成海報。這個需求比較常規,實現思路也比較簡單,經過利用用戶的input輸入,對所上傳的圖片進行處理,最後經過第三方庫html2canvas合成對應的圖片便可。思路雖然簡單,可是在實現的過程當中會遇到各類各樣的小問題,如今就將遇到的問題進行一次總結。html
一、iphone以及部分android機型經過攝像頭拍攝的照片被旋轉了90度前端
緣由:因爲目前的手機拍照基本都在2M以上,而ios中只要超過2M圖片就會自動旋轉。拍照後直接取出來的UIimage(用UIImagePickerControllerOriginalImage取出),它自己的imageOrientation屬性是3,即UIImageOrientationRight。若是這個圖片直接使用則沒事,可是若是對它進行裁剪、縮放等操做後,它的這個imageOrientation屬性會變成0。此時這張圖片用在別的地方就會發生旋轉。imageOrientation是隻讀的,不能直接修改其值。android
解決方法: 當拍照後,獲取input中的圖片數據,利用exif.js(Exif.js 提供了 JavaScript 讀取圖像的原始數據的功能擴展,例如:拍照方向、相機設備型號、拍攝時間、ISO 感光度、GPS 地理位置等數據。)獲取到Orientation屬性,此屬性有四個值ios
1 表示旋轉0度,也就是沒有旋轉。
6 表示順時針旋轉90度
8 表示逆時針旋轉90度
3 旋轉180度
咱們要作的就是在拍照後,從input中獲取到圖片,而後獲得它的Orientation值,根據Orientation值再對圖片進行進一步處理,處理代碼以下:nginx
import EXIF from 'exif-js'; var file = { upload: function(e) { var file = e.target.files[0]; var type = file.type.split('/')[0]; if (type != 'image') { alert('請上傳圖片'); return; } var size = Math.floor(file.size / 1024 / 1024); if (size > 3) { alert('圖片大小不得超過3M'); return; }; var reader = new FileReader(); reader.readAsDataURL(file); var orientation = null; //獲取照片方向角屬性,用戶旋轉控制 EXIF.getData(file, function() { EXIF.getAllTags(this); orientation = EXIF.getTag(this, 'Orientation'); }); reader.onloadstart = function() {}; reader.onloadend = function(e) { var dataURL = reader.result; var imaged = new Image(); imaged.src = dataURL; imaged.onload = function() { var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); //普通環境下設置canvas的寬高 var w = 0, h = 0; if (this.width < 750) { w = this.width; h = this.height; canvas.width = w; canvas.height = h; } else { w = 750; canvas.width = w; var scale = this.width / this.height; w = w > this.width ? this.width : w; h = w / scale; canvas.height = h; } // if (navigator.userAgent.match(/iphone/i) || navigator.userAgent.match(/samsung/i)) { if (orientation != "") { switch (orientation) { case 3: ctx.rotate(180 * Math.PI / 180); ctx.drawImage(this, -w, -h, w, h); break; case 6: //這裏因爲將圖片糾正了回來,因此也要從新設置canvas的高已達到高度自適應 canvas.width = 750; var scale = this.height / this.width; canvas.height = canvas.width / scale; h = 750 > this.height ? this.height : 750; w = h / scale; ctx.rotate(90 * Math.PI / 180); ctx.drawImage(this, 0, -h, w, h); break; case 8: ctx.rotate(270 * Math.PI / 180); ctx.drawImage(this, -h, 0, h, w); break; case 2: ctx.translate(w, 0); ctx.scale(-1, 1); ctx.drawImage(this, 0, 0, w, h); break; case 4: ctx.translate(w, 0); ctx.scale(-1, 1); ctx.rotate(180 * Math.PI / 180); ctx.drawImage(this, -w, -h, w, h); break; case 5: ctx.translate(w, 0); ctx.scale(-1, 1); ctx.rotate(90 * Math.PI / 180); ctx.drawImage(this, 0, -w, h, w); break; case 7: ctx.translate(w, 0); ctx.scale(-1, 1); ctx.rotate(270 * Math.PI / 180); ctx.drawImage(this, -h, 0, h, w); break; default: ctx.drawImage(this, 0, 0, w, h); } } else { ctx.drawImage(this, 0, 0, w, h); } //接下來對圖片進行操做 }; }; }, event: function() { $(".uploadfile").change(function(e) { file.upload(e); }); }, init: function() { this.event(); } }; module.exports = file;
二、獲取微信用戶頭像生成海報沒法顯示canvas
緣由: 因爲微信用戶的頭像是微信的域名,canvas因爲安全機制的緣由,限制了使用跨域的圖片,因此沒法顯示用戶頭像的圖片。後端
解決方法:跨域
(1)後端在獲取用戶頭像的時候,將頭像圖片保存到服務器,生成該域名下新的圖片連接安全
(2)使用nginx做爲代理,將對應請求轉發到微信頭像圖片的服務器。這裏展現第二種方法的實現服務器
前端將圖片地址修改成對應的代理服務器域名地址
data.avatar.replace('thirdwx.qlogo.cn/', 'proxy.newmedium.xyz/wximg/')
nginx 代理配置
server { listen 80; server_name xxxxxx; add_header Access-Control-Allow-Origin *; location /wximg/ { proxy_pass http://thirdwx.qlogo.cn/; } }
因爲項目靜態文件域名上了cdn,因此咱們只能採用不一樣的域名做爲代理域名,這裏又涉及到跨域問題,還須要在服務器和前端處理一部,設置header 爲Access-Control-Allow-Origin *,前端設置爲容許跨域
html2canvas(document.querySelector(".poster"), { backgroundColor: '#ffffff', useCORS: true })
三個步驟,一步都不能少。
三、頁面有聲音播放時調用html2canvas生成海報後,iphone手機會有重複聲音
測試發現html2canvas在使用的時候若是頁面有音頻播放,在ios系統中,音頻會被複制一份,而且保持播放的狀態。
剛開始一直找不到緣由,最後沒有辦法,從源碼中找html2canvas的實現原理找到了問題所在。 html2canvas在將dom轉化爲canvas的時候,會把頁面複製到一個iframe,而後在計算出對應元素標籤所在的位置進行處理。因此在複製頁面的時候,將音頻播放標籤aduio也複製到了iframe,隨後canvas完成後會刪除iframe標籤,可是複製到了iframe的音頻已經開始播放進程,沒法跟隨iframe內容的清除而銷燬。
最後在html2canvas 的官方文檔中找到了解決方法。Html2canvas暴露接口ignoreElements,咱們只要將aduio標籤置爲忽略就好了。
html2canvas(document.querySelector(".poster"), { useCORS: true, ignoreElements: function(el) { if (el.tagName == 'AUDIO') { return true; } });
四、海報上傳數據太大
將canvas轉化爲base64數據的時候,有時候用戶選擇文件圖片有點大,致使上傳性能差。最後查閱資料,能夠經過toDataURL方法指定canvas 轉化爲base64 圖片的質量來解決這個問題。
canvas.toDataURL("images/jpeg",0) ,第一個參數就是把圖片編碼爲jpeg格式,第二個參數(0-1)就是指定圖片質量,數值越大質量越高