查看内容

Canvas处理头像上传

  • 2020-03-02 17:06
  • Web前端技术
  • Views

近年来社区体系须求支持移动端,当中提到到顾客头像上传,头像有大中型Mini二种尺寸,在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裁剪出一张星型的图样,接下去大家还须要管理头像图片大中型Mini三种尺寸。在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建议了贰个方案:对图纸张开多少次的等比例裁减,最终再放手到对象尺寸:

图片 1

参照他事他说加以考察那个方案,大家得以兑现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));

参考