55

我想使用 HTML5 Canvas 调整从客户端的 iOS 相机拍摄的图像的大小,但我一直在这个奇怪的错误中运行,如果图像大于 ~1.5mb,则图像的比例错误

它适用于桌面,但不适用于带有媒体上传 API 的最新 iOS 版本。

你可以在这里看到一个例子:http: //jsbin.com/ekuros/1

知道如何解决这个问题吗?这是内存问题吗?

$('#file').on('change', function (e) {
    var file = e.currentTarget.files[0];
    var reader = new FileReader();
    reader.onload = function (e) {
        var image = $('<img/>');
        image.on('load', function () {
            var square = 320;
            var canvas = document.createElement('canvas');

            canvas.width = square;
            canvas.height = square;

            var context = canvas.getContext('2d');
            context.clearRect(0, 0, square, square);
            var imageWidth;
            var imageHeight;
            var offsetX = 0;
            var offsetY = 0;

            if (this.width > this.height) {
                imageWidth = Math.round(square * this.width / this.height);
                imageHeight = square;
                offsetX = - Math.round((imageWidth - square) / 2);
            } else {
                imageHeight = Math.round(square * this.height / this.width);
                imageWidth = square;    
                offsetY = - Math.round((imageHeight - square) / 2);            
            }

            context.drawImage(this, offsetX, offsetY, imageWidth, imageHeight);
            var data = canvas.toDataURL('image/jpeg');

            var thumb = $('<img/>');
            thumb.attr('src', data);
            $('body').append(thumb);
        });
        image.attr('src', e.target.result);
    };
    reader.readAsDataURL(file);
});
4

5 回答 5

54

如果您仍然需要使用 drawImage 函数的长版本,您可以更改:

context.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);

对此:

drawImageIOSFix(context, img, sx, sy, sw, sh, dx, dy, dw, dh);

您只需要在某处包含这两个函数:

/**
 * Detecting vertical squash in loaded image.
 * Fixes a bug which squash image vertically while drawing into canvas for some images.
 * This is a bug in iOS6 devices. This function from https://github.com/stomita/ios-imagefile-megapixel
 * 
 */
function detectVerticalSquash(img) {
    var iw = img.naturalWidth, ih = img.naturalHeight;
    var canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = ih;
    var ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    var data = ctx.getImageData(0, 0, 1, ih).data;
    // search image edge pixel position in case it is squashed vertically.
    var sy = 0;
    var ey = ih;
    var py = ih;
    while (py > sy) {
        var alpha = data[(py - 1) * 4 + 3];
        if (alpha === 0) {
            ey = py;
        } else {
            sy = py;
        }
        py = (ey + sy) >> 1;
    }
    var ratio = (py / ih);
    return (ratio===0)?1:ratio;
}

/**
 * A replacement for context.drawImage
 * (args are for source and destination).
 */
function drawImageIOSFix(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {
    var vertSquashRatio = detectVerticalSquash(img);
 // Works only if whole image is displayed:
 // ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
 // The following works correct also when only a part of the image is displayed:
    ctx.drawImage(img, sx * vertSquashRatio, sy * vertSquashRatio, 
                       sw * vertSquashRatio, sh * vertSquashRatio, 
                       dx, dy, dw, dh );
}

无论是在 iOS 还是其他平台上运行,这都可以正常工作。

这是基于stomita的出色工作,您应该将他的工作归功于他。

于 2013-06-13T11:49:18.200 回答
30

有一个 JavaScript 画布调整大小库,可以解决在 iOS 设备上的画布上绘制缩放图像时遇到的二次采样和垂直挤压问题:http: //github.com/stomita/ios-imagefile-megapixel

使用 alpha 通道缩放图像时(因为它使用 alpha 通道进行问题检测)以及尝试调整现有画布元素的大小时存在副作用,但这是我发现的第一个实际解决手头问题的解决方案。

stomita 也是 StackOverflow 用户,并在此处发布了他的解决方案: https ://stackoverflow.com/a/12615436/644048

于 2012-10-10T09:46:07.213 回答
6

看起来这是一个 iOS 6 错误。方面没有理由脱离您的代码。我有同样的问题,它只在 iOS 6 中引入。似乎他们的子采样例程给出了错误的高度。我向 Apple 提交了错误报告,您也应该这样做。他们为此获得的错误报告越多越好。

于 2012-09-26T17:45:35.063 回答
2

我遇到过同样的问题。似乎这是 iOS 的限制,超过 2 兆像素的 jpg 被二次采样。

请参阅为 iPhone 上的 Safari 创建兼容的 Web 内容

于 2012-09-23T09:13:55.260 回答
-2

上述代码的修改版本。

编辑:在http://jsfiddle.net/gWY2a/24/看到 L0LN1NJ4 的代码.. 猜猜那个好一点...

function drawImageIOSFix (ctx, img) {
 var vertSquashRatio = detectVerticalSquash (img)
 var arg_count = arguments.length
 switch (arg_count) {
  case 4  : ctx.drawImage (img, arguments[2], arguments[3] / vertSquashRatio); break
  case 6  : ctx.drawImage (img, arguments[2], arguments[3], arguments[4], arguments[5] / vertSquashRatio); break
  case 8  : ctx.drawImage (img, arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7] / vertSquashRatio); break
  case 10 : ctx.drawImage (img, arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9] / vertSquashRatio); break
 }

 // Detects vertical squash in loaded image.
 // Fixes a bug which squash image vertically while drawing into canvas for some images.
 // This is a bug in iOS6 (and IOS7) devices. This function from https://github.com/stomita/ios-imagefile-megapixel
 function detectVerticalSquash (img) {
  var iw = img.naturalWidth, ih = img.naturalHeight
  var canvas = document.createElement ("canvas")
  canvas.width  = 1
  canvas.height = ih
  var ctx = canvas.getContext('2d')
  ctx.drawImage (img, 0, 0)
  var data = ctx.getImageData(0, 0, 1, ih).data
  // search image edge pixel position in case it is squashed vertically.
  var sy = 0, ey = ih, py = ih
  while (py > sy) {
   var alpha = data[(py - 1) * 4 + 3]
   if (alpha === 0) {ey = py} else {sy = py}
   py = (ey + sy) >> 1
  }
  var ratio = (py / ih)
  return (ratio === 0) ? 1 : ratio
 }
}
于 2013-10-21T03:12:39.107 回答