zoukankan      html  css  js  c++  java
  • Canvas处理头像上传

     

    未分类


    最近社区系统需要支持移动端,其中涉及到用户头像上传,头像有大中小三种尺寸,在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提出了一个方案:对图片进行若干次的等比例缩小,最后再放大到目标尺寸:

    canvas高清缩放

    参考这个方案,我们可以实现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));
  • 相关阅读:
    meta标签设置(移动端)
    清除浮动
    响应式设计
    堆和堆排序
    O(n^2)以及O(nlogn)时间复杂度的排序算法
    求数组的最大连续子数组和
    HTTP缓存原理
    将两个有序数组合并为一个有序数组
    如何实现居中对齐
    查找字符串中出现最多的字符
  • 原文地址:https://www.cnblogs.com/zhangxiaolei521/p/5720547.html
Copyright © 2011-2022 走看看