调整画布质量以实现预定的图像文件大小

时间:2015-09-07 07:24:25

标签: javascript html5 canvas

我想调整大小(如在超出限制时减少文件大小)一个使用画布的图像。我看到在画布上绘制的结果图像大小,保持宽度和高度不变,导致不同的文件大小取决于在画布上绘制的内容。

如果我得到所需的文件大小,我愿意进一步降低分辨率。

如何确定我在文件(图像)大小限制内达到的分辨率?

请参阅this fiddle

var el = document.getElementById('f');

el.addEventListener('change', onChange);

function onChange(e) {
  var file = this.files[0];

  console.log("CHANGING");

  var fr = new FileReader();
  fr.onload = function(e) {
    drawOnCanvas(e.target.result);
  }
  fr.readAsDataURL(file);
}


function drawOnCanvas(imgSrc) {
  var canv = document.createElement('canvas'),
    img = new Image();

  console.log("DRAWING");

  canv.width = canv.height = 500;
  ctx = canv.getContext('2d');

  img.onload = function() {
    console.log("IMAGE LOADED");
    ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canv.width, canv.height);

    try {
      document.body.removeChild(document.querySelector('canvas'));
    } catch (e) {}

    document.body.appendChild(canv);

    blob = canvasToFile(canv.toDataURL());

    alert("FILE SIZE " + blob.size);
  }

  img.src = imgSrc;
}

function canvasToFile(dataUrl) {
  var encData = dataUrl.split(',')[1],
    binString = atob(encData),
    binData = new Uint8Array(binString.length),
    i, blob;

  for (i = 0; i < binString.length; i++) {
    binData[i] = binString.charCodeAt(i);
  }

  blob = new Blob([binData], {
    type: 'image/jpeg'
  });

  return blob;
}
<p>Try uploading different image file and see the difference in size for same output resolution:</p>
<input type="file" id="f">

1 个答案:

答案 0 :(得分:3)

您遇到的问题是canvas.toDataURL(type, encoderOptions)的默认type参数为image/png,而且它实际上是图像数据的base64编码。

  • 对于前者,大多数浏览器都支持image/jpeg作为图像类型。由于jpg压缩,这可能会减小数据大小,但这首先会启用方法的encoderOptions参数,该参数可以转换为质量参数并需要0到1的浮动。
    请注意,如果转换为jpg,您无法确定数据是否会更亮:如果图像主要由透明像素组成,则png将更亮。 另外,请记住,jpeg压缩会降低图像的质量,请参阅最后一个片段以查看0的结果:)

  • 对于后者,(您可以找到原因here的解释),解决方法是将dataURL转换回blob。

以下是演示它的片段:

var input = document.querySelector('input');
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');

function draw(){
  var img = new Image();
  img.src = URL.createObjectURL(this.files[0]);
  img.onload = function(){
      canvas.width = this.naturalWidth;
      canvas.height = this.naturalHeight;
      ctx.drawImage(this, 0,0);
      var noData = canvas.toDataURL();
      png_data.innerHTML = 'dataURL length: '+noData.length;
      var noBlob = dataURItoBlob(noData);
      png_blob.innerHTML = 'blob size: '+ noBlob.size;
      var jpgData10 = canvas.toDataURL('image/jpeg', 1),
          jpgData5 = canvas.toDataURL('image/jpeg', 0.5),
          jpgData0 = canvas.toDataURL('image/jpeg', 0);
      jpg_data.innerHTML = 'quality = 1 : '+ jpgData10.length+'<br>';
      jpg_data.innerHTML += 'quality = 0.5 : '+ jpgData5.length+'<br>';
      jpg_data.innerHTML += 'quality = 0 :'+ jpgData0.length+'<br>';
      var jpgBlob10 = dataURItoBlob(jpgData10);
      jpg_blob.innerHTML = 'blob quality = 1 : '+jpgBlob10.size+'<br>';
      var jpgBlob5 = dataURItoBlob(jpgData5);
      jpg_blob.innerHTML += 'blob quality = .5 : '+jpgBlob5.size+'<br>';
      var jpgBlob0 = dataURItoBlob(jpgData0);
      jpg_blob.innerHTML += 'blob quality = 0 : '+jpgBlob0.size;
    }
  this.previousElementSibling.innerHTML = 'orginal size : '+this.files[0].size;
  }
input.onchange = draw;


// Taken from https://stackoverflow.com/a/5100158/3702797
function dataURItoBlob(dataURI) {
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ia], {type:mimeString});
}
<div></div>
<input type="file"/>
<br>PNG : 
<div id="png_data"></div>
<div id="png_blob"></div>
<br>Jpeg :
<div id="jpg_data"></div>
<div id="jpg_blob"></div>

canvas.toDataURL('image/jpeg', 0)演示?

var canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d');
document.querySelector('input').onchange = function(){
    var img = new Image();
    img.src = URL.createObjectURL(this.files[0]);
    img.onload = function(){
        canvas.width = this.naturalWidth;
        canvas.height = this.naturalHeight;
        ctx.drawImage(this, 0,0);
        document.images[0].src = canvas.toDataURL('image/jpeg', 0);
      }
    }
<input type="file"/>
<img/>

此外,如果您需要使文件比特定大小更轻,我在another question上写了一些东西,它将数据大小限制为3Mb(只需根据需要更改maxSize并删除localStorage部分)。 / p>