HTML5 Canvas處理頭像上傳
最近社群系統需要支援移動端,其中涉及到使用者頭像上傳,頭像有大中小三種尺寸,在PC端,社群用Flash來處理頭像編輯和生成,但該Flash控制元件的介面不友好而且移動端對Flash的支援不好,考慮到這些問題,最後我們選用Canvas來完成影像尺寸縮放和圖片資料獲取。
等邊處理
頭像一般都是正方形,首先我們需要獲取圖片寬度和高度的最小值,用該最小值作為邊長居中裁剪圖片,最終得到一個正方形的圖片:
var ImageEditor = function() { // 用離線canvas處理圖片資料 this.canvas = document.createElement('canvas'); this.context = this.canvas.getContext('2d'); }; var fn = ImageEditor.prototype; fn.resizeCanvas = function(width, height) { this.canvas.width = width; this.canvas.height = height; }; fn.clipSquareImage = function(url, callback) { var that = this, img = new Image(); img.src = url; img.onload = function() { // 取寬高最小值作為正方形邊長 var eLength = Math.min(img.width, img.height), picture = img; // canvas不支援區域性截圖,截圖前必須先調節canvas的寬高 that.resizeCanvas(eLength, eLength); // 將圖片以居中裁剪的方式畫到canvas中。 // drawImage支援9個引數:圖片物件,圖片上的剪下座標XY, // 剪下寬高,圖片在canvas上的座標XY及圖片寬高 that.context.drawImage(picture, (picture.width - eLength) / 2, (picture.height - eLength) / 2, eLength, eLength, 0, 0, eLength, eLength); // 截圖,即獲取base64資料 callback.call(that, that.canvas.toDataURL('image/png')); }; };
Canvas元素大小限制問題
上述clipSquareImage
函式中,由於canvas.toDataURL
介面不提供寬高引數,只能夠一次性把整個canvas的螢幕資料擷取下來,所以在對Canvas截圖前,我們必須先設定Canvas元素的大小。然而移動端拍照的解析度極高,寬高大多會在3000以上,當我們根據相片寬高的最小值來設定Canvas的尺寸時,Canvas元素的最小寬度也高達到3000以上。
問題在於,每個平臺對Canvas的大小都有限制,如果Canvas的寬度或高度任意一個值超過了平臺限制,Canvas將無法進行渲染,canvas.toDataURL
只能獲取一張透明的圖片資料。
Maximum size of a canvas element中提到了部分平臺下Canvas的尺寸限制:
chrome = 32767x32767 iPod Touch 16GB = 1448x1448 iPad Mini = 2290x2289 iPhone 3 = 1448x1448 iPhone 5 = 2290x2289
參考以上資料,我們先給Canvas設定一個最大的寬度:
var MAX_WIDTH = 1000;
在clipSquareImage
函式中加入最大寬度的檢測,如果超過限制,則建立一個臨時的canvas進行圖片縮放處理,最後對該臨時的Canvas進行居中剪下:
fn.clipSquareImage = function(url, callback) { var that = this, img = new Image(); img.src = url; img.onload = function() { // 取圖片寬高和Canvas的最大寬度的最小值作為等邊長 var eLength = Math.min(img.width, img.height, MAX_WIDTH), // 剪下物件 picture = img, tempEditor, ratio; // 如果圖片尺寸超出限制 if (eLength === MAX_WIDTH) { // 建立一個臨時editor tempEditor = new ImageEditor(); ratio = img.width / img.height; // 按圖片比例縮放canvas img.width < img.height ? tempEditor.resizeCanvas(MAX_WIDTH * ratio, MAX_WIDTH) : tempEditor.resizeCanvas(MAX_WIDTH, MAX_WIDTH / ratio); tempEditor.context.drawImage(img, 0, 0, tempEditor.canvas.width, tempEditor.canvas.height); // 將臨時Canvas作為剪下物件 picture = tempEditor.canvas; eLength = Math.min(tempEditor.canvas.width, tempEditor.canvas.height); } // 居中剪下 // ... ... // 截圖操作 // ... ... }; };
Canvas鋸齒問題
上面我們已經能夠通過Canvas裁剪出一張正方形的圖片,接下來我們還需要處理頭像圖片大中小三種尺寸。在Canvas中,drawImage
介面提供非常方便的縮放功能:
var editor = new ImageEditor; // 將圖片縮放到300x300 // drawImage支援5個引數:圖片物件,及圖片在canvas上的座標和寬高 editor.context.drawImage(squareImage, 0, 0, 300, 300);
然而大尺寸圖片直接用drawImage
進行縮小處理會導致圖片出現鋸齒。在stack overflow上HTML5 canvas drawImage: how to apply antialiasing提出了一個方案:對圖片進行若干次的等比例縮小,最後再放大到目標尺寸:
參考這個方案,我們可以實現antialiasScale
抗鋸齒縮放函式:
fn.antialisScale = function(img, width, height) { var offlineCanvas = document.createElement('canvas'), offlineCtx = offlineCanvas.getContext('2d'), sourceWidth = img.width, sourceHeight = img.height, // 縮小操作的次數 steps = Math.ceil(Math.log(sourceWidth / width) / Math.log(2)) - 1, i; // 渲染圖片 offlineCanvas.width = sourceWidth; offlineCanvas.height = sourceHeight; offlineCtx.drawImage(img, 0, 0, offlineCanvas.width, offlineCanvas.height); // 縮小操作 // 進行steps次的減半縮小 for(i = 0; i < steps; i++) { offlineCtx.drawImage(offlineCanvas, 0, 0, offlineCanvas.width * 0.5, offlineCanvas.height * 0.5); } // 放大操作 // 進行steps次的兩倍放大 this.context.drawImage(offlineCanvas, 0, 0, offlineCanvas.width * Math.pow(0.5, steps), offlineCanvas.height * Math.pow(0.5, steps), 0, 0, width, height); };
我們可以用這個函式代替drawImage完成縮放工作,生成頭像圖片的三種尺寸:
fn.scaleSquareImage = function(url, sizes, callback) { var that = this; // 先裁剪一個正方形 that.clipSquareImage(url, sizes, function(data) { var squareImage = new Image(), result = [], i; squareImage.src = data; // 抗鋸齒縮放 for (i = 0; i < sizes.length; i++) { that.antialisScale(squareImage, sizes[i], size[i]); result.push(that.canvas.toDataURL('image/png')); } callback.call(that, result); }); };
PHP儲存base64圖片資料
Canvas.toDataURL()
獲取的預設影像資料格式是:data:image/png;base64,
+ base64資料:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC
當把Canvas截圖資料傳給後臺時,後臺需要截斷開頭的欄位data:image/png;base64,
,獲取後面那串真正的base64資料:
<?php $imgData = $_POST['imgData']; // 擷取有用的部分 list($type, $imgData) = explode(';', $imgData); list(, $imgData) = explode(',', $imgData); // base64 編碼中使用了加號, // 如果通過url傳遞base64資料,+號會轉換成空格 $imgData = str_replace(' ', '+', $imgData); // 儲存檔案 $success = file_put_contents('PATH/XXX.png', base64_decode($imgData));
參考
- Save a Base64 Encoded Canvas image to a png file using PHP
- Html5 canvas drawImage: how to apply antialiasing
- Maximum size of a canvas element
- How to save a PNG image server-side, from a base64 data string
- How to send FormData objects with Ajax-requests in jQuery
相關文章
- WebRTC從攝像頭獲取圖片傳入canvasWebCanvas
- laravel圖片/頭像上傳通用方法Laravel
- HTML5:使用Canvas實時處理VideoHTMLCanvasIDE
- 如何給Dockerhub使用者上傳頭像Docker
- html5中呼叫攝像頭拍照並上傳(附繞過https的想法)HTMLHTTP
- 完美實現類似QQ的自拍頭像、上傳頭像功能!(Demo 原始碼)原始碼
- 如何給Docker hub使用者上傳頭像Docker
- django-restful-ios 使用者頭像上傳DjangoRESTiOS
- html5呼叫攝像頭功能HTML
- 如何使用HTML5實現利用攝像頭拍照上傳功能HTML
- canvas之自定義頭像功能實現Canvas
- C#上傳頭像剪裁,並生成多種尺寸C#
- 對Jquery上傳頭像擷取程式碼的更新jQuery
- 微信小程式獲取base64頭像上傳微信小程式
- html5呼叫攝像頭截圖HTML
- html5呼叫手機攝像頭HTML
- laravel處理檔案上傳Laravel
- TP開發的 HTML5頭像擷取案例,會員頭像功能HTML
- canvas繪製熊貓頭像程式碼例項Canvas
- web呼叫攝像頭拍照並上傳到伺服器Web伺服器
- Android圖片上傳(頭像裁切+原圖原樣)Android
- jq實現上傳頭像並實時預覽功能
- Vue+axios+Node+express實現檔案上傳(使用者頭像上傳)VueiOSExpress
- 使用 HTML5 Canvas 實現圓形圖片裁剪並上傳HTMLCanvas
- html5呼叫攝像頭實現拍照HTML
- 圖片上傳處理架構架構
- 專案需求討論-Retrofit中文提交及上傳頭像功能
- Web呼叫網路攝像頭及各類錯誤處理Web
- AngularJS中使用HTML5攝像頭拍照AngularJSHTML
- 如何在 Laravel 專案中輕鬆實現上傳頭像功能?Laravel
- 快速開始api開發(六)檔案上傳,設定頭像API
- Android 用MultiImageSelector實現上傳頭像的拍照跟相簿Android
- 圖片上傳及圖片處理
- spring 對上傳圖片處理Spring
- canvas繪製機器貓頭像程式碼例項Canvas
- 美國禁令“特赦”中國AI攝像頭,FDA:疫情期間,酌情處理AI
- 基於 Serverless 架構的頭像漫畫風處理小程式Server架構
- .NET Core 如何上傳檔案及處理大檔案上傳