未分类


最近社区系统需要支持移动端,其中涉及到用户头像上传,头像有大中小三种尺寸,在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. Oracle函数+for循环

    create or replace function FilterMinganci(str in varchar) return varchar2 is filterWorld varchar2(10 ...

  2. 【转载】Dom篇

    一. 初探Dom     1. Dom介绍 二. Dom基础     1. window顶级对象     2. body.document对象事件     3. 通用的HTML元素的事件     4. ...

  3. Spring的lazy-init详解

    1.Spring中lazy-init详解ApplicationContext实现的默认行为就是在启动服务器时将所有singleton bean提前进行实例化(也就是依赖注入).提前实例化意味着作为初始 ...

  4. 国内为什么没有好的 Stack Overflow 的模仿者?

    国内为什么没有好的 Stack Overflow 的模仿者? 个人觉得, 高端的程序员会直接上stackoverflow提问, 所以国内中文的stackoverflow必然面对低端程序员. 鉴于中国程 ...

  5. AC题目简解-线段树

    线段树: http://www.notonlysuccess.com/index.php/segment-tree-complete/鉴于notonlysuccess大牛的博客对于题目的思路写的很简陋 ...

  6. 使用CXF暴露您的REST服务

    使用CXF暴露您的REST服务 REST应用服务器SpringBeanServlet  1.  前言 现在互联网Open API流行,将您的Web应用也可以开放Open API给其他第三方使用.达到一 ...

  7. 4、处理方法中获取请求参数、请求头、Cookie及原生的servlet API等

    1.请求参数和请求头 使用@RequestParam绑定请求参数,在处理方法的入参处使用该注解可以把请求参数传递给请求方法 —— value :参数名 —— required : 是否必须,默认为tr ...

  8. 【Spring】如何在单个Boot应用中配置多数据库?

    原创 BOOT 为什么需要多数据库? 默认情况下,Spring Boot使用的是单数据库配置(通过spring.datasource.*配置具体数据库连接信息).对于绝大多数Spring Boot应用 ...

  9. 图像二值化----otsu(最大类间方差法、大津算法)

    最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定的方法,又叫大津 法,简称OTSU.它是按图像的灰度特性,将图像分成背景和目标2部分.背景和目标之间的类间方差越大,说明构成图像 ...

  10. iOSTab bar

    http://www.apkbus.com/android-130504-1-1.html #import #import "FirstViewController.h"#impo ...