html5 canvas处理头像上传-mile米乐体育
最近社区系统需要支持移动端,其中涉及到用户头像上传,头像有大中小三种尺寸,在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.todata); }; };
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.todata); } callback.call(that, result); }); };
php存储base64图片数据
canvas.todata
获取的默认图像数据格式是:data:image/png;base64,
base64数据:
data:image/png;base64,ivborw0kggoaaaansuheugaaaauaaaafcayaaacnbyblaaaadeleqvqimwngobmaaabpaafei8araaaaaelftksuqmcc
当把canvas截屏数据传给后台时,后台需要截断开头的字段data:image/png;base64,
,获取后面那串真正的base64数据:
参考
- 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