未分类


最近社区系统需要支持移动端,其中涉及到用户头像上传,头像有大中小三种尺寸,在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. JAVA WEB中如何让数据库连接对开发人员完全透明?

    书上的技术,确实开了眼界. 列相关测试代码如下,慢慢体会开发的模式. PropsUtil package org.smart4j.chapter2.util; import java.io.FileN ...

  2. [主席树]ZOJ2112 && BZOJ1901 Dynamic Rankings

    题意:n个数,q个询问 (n<=50000, q<=10000) Q x y z 代表询问[x, y]区间里的第z小的数 C x y    代表将(从左往右数)第x个数变成y 上篇介绍了在 ...

  3. BFS 模板

    转自:欣哥 下面是bfs一般的形式,谈不上模板但一般都这么来做有点乱有什么多交流 bfs一般用于求最短时间 #include<stdio.h>#include<queue>us ...

  4. lintcode:Palindrome Partitioning 分割回文串

    题目: 分割回文串 给定一个字符串s,将s分割成一些子串,使每个子串都是回文串. 返回s所有可能的回文串分割方案. 样例 给出 s = "aab",返回 [ ["aa&q ...

  5. pymongo 例子

    import pymongo class dbUtil(object): def __init__(self, tablename='functional_testing'): con = pymon ...

  6. 基于Struts2框架实现登录案例 之 程序国际化

    国际化牵涉的知识非常多,这里只能简单的介绍,程序国际化的一般做法是:在jsp页面时, 不是直接输出信息,而是输出一个key值,该key值在不同语言环境下找到对应资源文件下的 对应信息,因此首先要创建满 ...

  7. 在java程序中访问windows有用户名和密码保护的共享目录

    在java程序中访问windows有用户名和密码保护的共享目录 Posted on 2015-11-20 14:03 云自无心水自闲 阅读(3744) 评论(0)  编辑  收藏 --> Jav ...

  8. gdb 基本命令

    backtrace(或bt) 查看各级函数调用及参数 finish 连续运行到当前函数返回为止,然后停下来等待命令 frame(或f) 帧编号 选择栈帧 info(或i) locals 查看当前栈帧局 ...

  9. C#编写媒体播放器--Microsoft的Directx提供的DirectShow组件,该组件的程序集QuartzTypeLib.dll.

    使用C#编写媒体播放器时,需要用到Microsoft的Directx提供的DirectShow组件.用该组件前需要先注册程序集QuartzTypeLib.dll. 1.用QuartzTypeLib.d ...

  10. n人比赛,可轮空,比赛轮数和场数

    #include<stdio.h> int chang(int x,int s){ ) return s; ) ; !=){ s+=(x-)/; )/,s); } else{ s+=x/; ...