javascript - 调整 Canvas 质量以达到预定的图像文件大小

标签 javascript html canvas

我想使用 Canvas 调整图像大小(例如,如果超出限制则减小文件大小)。我发现在 Canvas 上绘制的最终图像大小保持宽度和高度不变,会根据 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">

最佳答案

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

  • 对于前者,大多数浏览器都支持 image/jpeg 作为图像类型。由于 jpg 压缩,这可能会减少数据大小,但这首先会启用该方法的 encoderOptions 参数,该参数可以转换为质量参数,并且需要 0 到 1 的 float 。
    请注意,您不能确定转换为 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 部分)。

关于javascript - 调整 Canvas 质量以达到预定的图像文件大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32433211/

相关文章:

javascript - Angular ng-click、ng-mouseover 表达式和鼠标悬停

javascript - jQuery - 使具有固定标题的表格可排序

javascript - 加载时堆叠图像 - Slick

android - 旋转 Canvas 后坐标计算问题

html - Chrome Canvas 检查器 2015

javascript - react 验证以及清除输入

javascript - 我如何告诉 webdriver-io 使用 firefox 开发者版?

javascript - 第三部分不在屏幕上

javascript - 在 LightSwitch HTML5 客户端中为超链接加下划线

javascript - HTML Canvas (SVG) - 在圆柱体表面绘制图像