未分类


最近社区系统需要支持移动端,其中涉及到用户头像上传,头像有大中小三种尺寸,在PC端,社区用Flash来处理头像编辑和生成,但该Flash控件的界面不友好而且移动端对Flash的支持不好,考虑到这些问题,最后我们选用Canvas来完成图像尺寸缩放和图片数据获取。

 

等边处理

头像一般都是正方形,首先我们需要获取图片宽度和高度的最小值,用该最小值作为边长居中裁剪图片,最终得到一个正方形的图片:

 
  1. var ImageEditor = function() {
  2. // 用离线canvas处理图片数据
  3. this.canvas = document.createElement('canvas');
  4. this.context = this.canvas.getContext('2d');
  5. };
  6. var fn = ImageEditor.prototype;
  7. fn.resizeCanvas = function(width, height) {
  8. this.canvas.width = width;
  9. this.canvas.height = height;
  10. };
  11. fn.clipSquareImage = function(url, callback) {
  12. var that = this,
  13. img = new Image();
  14. img.src = url;
  15. img.onload = function() {
  16. // 取宽高最小值作为正方形边长
  17. var eLength = Math.min(img.width, img.height),
  18. picture = img;
  19. // canvas不支持局部截屏,截屏前必须先调节canvas的宽高
  20. that.resizeCanvas(eLength, eLength);
  21. // 将图片以居中裁剪的方式画到canvas中。
  22. // drawImage支持9个参数:图片对象,图片上的剪切坐标XY,
  23. // 剪切宽高,图片在canvas上的坐标XY及图片宽高
  24. that.context.drawImage(picture,
  25. (picture.width - eLength) / 2, (picture.height - eLength) / 2,
  26. eLength, eLength, 0, 0, eLength, eLength);
  27. // 截屏,即获取base64数据
  28. callback.call(that, that.canvas.toDataURL('image/png'));
  29. };
  30. };
 

Canvas元素大小限制问题

上述clipSquareImage函数中,由于canvas.toDataURL接口不提供宽高参数,只能够一次性把整个canvas的屏幕数据截取下来,所以在对Canvas截屏前,我们必须先设置Canvas元素的大小。然而移动端拍照的分辨率极高,宽高大多会在3000以上,当我们根据相片宽高的最小值来设置Canvas的尺寸时,Canvas元素的最小宽度也高达到3000以上。

问题在于,每个平台对Canvas的大小都有限制,如果Canvas的宽度或高度任意一个值超过了平台限制,Canvas将无法进行渲染,canvas.toDataURL只能获取一张透明的图片数据。

Maximum size of a canvas element中提到了部分平台下Canvas的尺寸限制:

 
  1. chrome = 32767x32767
  2. iPod Touch 16GB = 1448x1448
  3. iPad Mini = 2290x2289
  4. iPhone 3 = 1448x1448
  5. iPhone 5 = 2290x2289

参考以上数据,我们先给Canvas设置一个最大的宽度:

 
  1. var MAX_WIDTH = 1000;

clipSquareImage函数中加入最大宽度的检测,如果超过限制,则创建一个临时的canvas进行图片缩放处理,最后对该临时的Canvas进行居中剪切:

 
  1. fn.clipSquareImage = function(url, callback) {
  2. var that = this,
  3. img = new Image();
  4. img.src = url;
  5. img.onload = function() {
  6. // 取图片宽高和Canvas的最大宽度的最小值作为等边长
  7. var eLength = Math.min(img.width, img.height, MAX_WIDTH),
  8. // 剪切对象
  9. picture = img,
  10. tempEditor,
  11. ratio;
  12. // 如果图片尺寸超出限制
  13. if (eLength === MAX_WIDTH) {
  14. // 创建一个临时editor
  15. tempEditor = new ImageEditor();
  16. ratio = img.width / img.height;
  17. // 按图片比例缩放canvas
  18. img.width < img.height ?
  19. tempEditor.resizeCanvas(MAX_WIDTH * ratio, MAX_WIDTH) :
  20. tempEditor.resizeCanvas(MAX_WIDTH, MAX_WIDTH / ratio);
  21. tempEditor.context.drawImage(img, 0, 0, tempEditor.canvas.width, tempEditor.canvas.height);
  22. // 将临时Canvas作为剪切对象
  23. picture = tempEditor.canvas;
  24. eLength = Math.min(tempEditor.canvas.width, tempEditor.canvas.height);
  25. }
  26. // 居中剪切
  27. // ... ...
  28. // 截屏操作
  29. // ... ...
  30. };
  31. };
 

Canvas锯齿问题

上面我们已经能够通过Canvas裁剪出一张正方形的图片,接下来我们还需要处理头像图片大中小三种尺寸。在Canvas中,drawImage接口提供非常方便的缩放功能:

 
  1. var editor = new ImageEditor;
  2. // 将图片缩放到300x300
  3. // drawImage支持5个参数:图片对象,及图片在canvas上的坐标和宽高
  4. editor.context.drawImage(squareImage, 0, 0, 300, 300);

然而大尺寸图片直接用drawImage进行缩小处理会导致图片出现锯齿。在stack overflow上HTML5 canvas drawImage: how to apply antialiasing提出了一个方案:对图片进行若干次的等比例缩小,最后再放大到目标尺寸:

参考这个方案,我们可以实现antialiasScale抗锯齿缩放函数:

 
  1. fn.antialisScale = function(img, width, height) {
  2. var offlineCanvas = document.createElement('canvas'),
  3. offlineCtx = offlineCanvas.getContext('2d'),
  4. sourceWidth = img.width,
  5. sourceHeight = img.height,
  6. // 缩小操作的次数
  7. steps = Math.ceil(Math.log(sourceWidth / width) / Math.log(2)) - 1,
  8. i;
  9. // 渲染图片
  10. offlineCanvas.width = sourceWidth;
  11. offlineCanvas.height = sourceHeight;
  12. offlineCtx.drawImage(img, 0, 0, offlineCanvas.width, offlineCanvas.height);
  13. // 缩小操作
  14. // 进行steps次的减半缩小
  15. for(i = 0; i < steps; i++) {
  16. offlineCtx.drawImage(offlineCanvas, 0, 0,
  17. offlineCanvas.width * 0.5, offlineCanvas.height * 0.5);
  18. }
  19. // 放大操作
  20. // 进行steps次的两倍放大
  21. this.context.drawImage(offlineCanvas, 0, 0,
  22. offlineCanvas.width * Math.pow(0.5, steps),
  23. offlineCanvas.height * Math.pow(0.5, steps),
  24. 0, 0, width, height);
  25. };

我们可以用这个函数代替drawImage完成缩放工作,生成头像图片的三种尺寸:

 
  1. fn.scaleSquareImage = function(url, sizes, callback) {
  2. var that = this;
  3. // 先裁剪一个正方形
  4. that.clipSquareImage(url, sizes, function(data) {
  5. var squareImage = new Image(),
  6. result = [],
  7. i;
  8. squareImage.src = data;
  9. // 抗锯齿缩放
  10. for (i = 0; i < sizes.length; i++) {
  11. that.antialisScale(squareImage, sizes[i], size[i]);
  12. result.push(that.canvas.toDataURL('image/png'));
  13. }
  14. callback.call(that, result);
  15. });
  16. };
 

PHP存储base64图片数据

Canvas.toDataURL()获取的默认图像数据格式是:data:image/png;base64, + base64数据:

 
  1. data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC

当把Canvas截屏数据传给后台时,后台需要截断开头的字段data:image/png;base64,,获取后面那串真正的base64数据:

 
  1. <?php
  2. $imgData = $_POST['imgData'];
  3. // 截取有用的部分
  4. list($type, $imgData) = explode(';', $imgData);
  5. list(, $imgData) = explode(',', $imgData);
  6. // base64 编码中使用了加号,
  7. // 如果通过url传递base64数据,+号会转换成空格
  8. $imgData = str_replace(' ', '+', $imgData);
  9. // 存储文件
  10. $success = file_put_contents('PATH/XXX.png', base64_decode($imgData));

Canvas处理头像上传的更多相关文章

  1. 【javascript】html5中使用canvas编写头像上传截取功能

    [javascript]html5中使用canvas编写头像上传截取功能 本人对canvas很是喜欢,于是想仿照新浪微博头像上传功能(前端使用canvas) 本程序目前在谷歌浏览器和火狐浏览器测试可用 ...

  2. vue头像上传与文件压缩

    工作中遇到的问题记录:vue开发头像上传组件,后端提供接口,需求为可相册上传,可相机拍摄上传,文件大小限制为2M 需求点分析 移动端调用相册/摄像头实现拍照 图片压缩,当前高像素的相机拍出来的图片都有 ...

  3. jquery头像上传剪裁插件cropper的前后台demo

    因为一个项目要做一个头像上传的功能,因此选择了使用jquery的头像插件cropper,cropper是一款使用简单且功能强大的图片剪裁jQuery插件,但是在使用的时候,有一个很大的坑需要注意,那就 ...

  4. java web 站点头像上传处理 (springmvc +bootstrap+cropper)

    制作头像上传.请依据您的实际需求.改动代码,不全然正确.仅供參考! 前端页面设计使用bootstrap ,头像预览和剪裁工具使用cropper 后台使用springmvc. 如今来看前端的页面设计 前 ...

  5. 强大的flash头像上传插件(支持旋转、拖拽、剪裁、生成缩略图等)

    今天介绍的这款flash上传头像功能非常强大,支持php,asp,jsp,asp.net 调用 头像剪裁,预览组件插件. 本组件需要安装Flash Player后才可使用,请从http://dl.pc ...

  6. 【Bootstrap-插件使用】Jcrop+fileinput组合实现头像上传功能

    作者:Dreawer链接:https://zhuanlan.zhihu.com/p/24465742来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者:梦游的龙猫(转 ...

  7. [Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能

    很久没有更新博客了,再不写点东西都烂了. 这次更新一个小内容,是两个插件的组合使用,实现头像上传功能. 业务需求: 头像上传功能,要对上传的文件进行剪切,且保证头像到服务器时必须是正方形的. 优化&l ...

  8. struts 头像上传

    java代码: 1 package cn.itcast.nsfw.user.action; import java.io.File; import java.io.IOException; impor ...

  9. js会员头像上传拖动处理头像类

    js会员头像上传拖动处理头像类 点击下载源码文件

随机推荐

  1. tornado解析http body的过程分析

    tornado解析http body的过程分析 在最近写的一个RESTful API Server过程中,发现tornaod对解析POST BODY的内容有限制. 而在以前用web.py则没有这个限制 ...

  2. EF提供的三种查询方式

    這邊簡單介紹一下,ADO.Net Entity Framework 提供的三種查詢方式, Linq to Entities Query Builder Mothed Entity SQL Langua ...

  3. PHP Zend Studio9.0怎么把代码搞成和服务器端的同步(就是直接在服务器端修改)

    Zend Studio 可以直接通过Remote System的方式直接连接服务器端的代码,就是可以直接修改服务器端的代码,不过修改的时间小心点,修改就会立即生效的. 选择Remote Systems ...

  4. 【二叉树遍历模版】前序遍历&&中序遍历&&后序遍历&&层次遍历&&Root->Right->Left遍历

    [二叉树遍历模版]前序遍历     1.递归实现 test.cpp: 12345678910111213141516171819202122232425262728293031323334353637 ...

  5. Ubuntu环境下手动配置HBase0.94.25

    /×××××××××××××××××××××××××××××××××××××××××/ Author:xxx0624 HomePage:http://www.cnblogs.com/xxx0624/ ...

  6. IIS常见错误及解决

    IIS常见错误 1.HTTP 错误 404.3 - Not Found由于扩展配置问题而无法提供您请求的页面.如果该页面是脚本,请添加处理程序.如果应下载文件,请添加 MIME 映射. 解决办法: w ...

  7. [杂题]HDOJ5515 Game of Flying Circus

    嗯...这是一道水题... 鉴于还没人写这题的题解, 那我就来写一发. 题意:有个边长为300米的正方形 嗯  这样标号 有两个人A和S,开始的时候A.S都在1(左下角)那个位置. 两个人都要按照2. ...

  8. 初识io流条件状态

    一  流状态    C++中的输入输出系统负责记录每一个输入输出操作的结果信息,这些当前的状态信息被包含在io_state类型的对象中.io_state是一个枚举类型(就像open_mode一样),以 ...

  9. import java.util.Scanner;

    一.扫描控制台输入     当通过new Scanner(System.in)创建一个Scanner,控制台会一直等待输入,,,,,,,直到敲回车键结束,把所输入的内容传给Scanner,作为扫描对象 ...

  10. vc2005中没有classwizard这个命令

    vc2005中没有classwizard这个命令了 2005下怎么添加鼠标事件 vc2005中没有classwizard这个命令了 取代classwizard 中的添加消息映射,添加类,等等的功能主要 ...