首頁  >  文章  >  web前端  >  HTML5 Canvas處理頭像上傳的圖文程式碼詳細介紹

HTML5 Canvas處理頭像上傳的圖文程式碼詳細介紹

黄舟
黄舟原創
2017-03-07 16:02:151753瀏覽

最近社群系統需要支援行動端,其中涉及到用戶頭像上傳,頭像有大中小三種尺寸,在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提出了一個方案:對圖片進行若干次的等比例縮小,最後再放大到目標尺寸:

HTML5 Canvas處理頭像上傳的圖文程式碼詳細介紹

#參考這個方案,我們可以實作antialiasScale抗鋸齒縮放函數:

fn.antialisScale = function(img, width, height) {
    var offlineCanvas = document.createElement(&#39;canvas&#39;),
        offlineCtx = offlineCanvas.getContext(&#39;2d&#39;),
        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(&#39;image/png&#39;));    
        }
        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[&#39;imgData&#39;];
    // 截取有用的部分
    list($type, $imgData) = explode(&#39;;&#39;, $imgData);
    list(, $imgData)      = explode(&#39;,&#39;, $imgData);
    // base64 编码中使用了加号,
    // 如果通过url传递base64数据,+号会转换成空格
    $imgData = str_replace(&#39; &#39;, &#39;+&#39;, $imgData);
    // 存储文件
    $success = file_put_contents(&#39;PATH/XXX.png&#39;, base64_decode($imgData));

參考

  • Save a Base64 Encoded Canvas image to a png file using PHP

  • ##Html5 canvas drawImage: how to apply antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly antialiasing##ly 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

 以上就是HTML5 Canvas處理頭像上傳的圖文程式碼詳細介紹的內容,更多相關內容請關注PHP中文網(www.php.cn)!




#### ###
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn