canvasを軽量な画像として保存する

canvasを軽量な画像として保存する

canvasの保存は下記のようなコードで簡単に画像として保存できますが、canvasを全画面での表示等を行っていた場合、canvasの容量も大きくなり、保存する際にかなりのファイル容量になってしまい、場合によっては、保存に失敗することがあります。これらの問題を解決するために保存する画像を軽量化しなくてはいけません。

<canvas id="canvas"></canvas>
<a href="#" download="download.jpg" id="save">保存</button>
document.getElementById('save').addEventListener('click', function(){
  this.href = document.getElementById('canvas').toDataURL();
});

canvas画像の軽量化

今回は、canvasのデータを軽量化するために2つの処理を行っていきます。

1. 画像のリサイズ

base64を指定サイズ内にリサイズする関数です。 これでどんなに大きなcanvasでも小さくすることができます。

resizeImage(base64, function(e){});
resizeImage = function(base64, callback) {
   const MIN_SIZE = 800;
   var canvas = document.createElement('canvas');
   var ctx = canvas.getContext('2d');
   var image = new Image();
   image.crossOrigin = "Anonymous";
   image.onload = function(event){
       var dstWidth, dstHeight;
       if (this.width > this.height) {
           dstWidth = MIN_SIZE;
           dstHeight = this.height * MIN_SIZE / this.width;
       } else {
           dstHeight = MIN_SIZE;
           dstWidth = this.width * MIN_SIZE / this.height;
       }
       canvas.width = dstWidth;
       canvas.height = dstHeight;
       ctx.drawImage(this, 0, 0, this.width, this.height, 0, 0, dstWidth, dstHeight);
       callback(canvas.toDataURL());
   };
   image.src = base64;
};

2. base64からblobへの変換

base64からblobへの変換するとファイルサイズを小さくすることができます。

function base64toBlob(base64) {
  var bin = atob(base64.replace(/^.*,/, ''));
  var buffer = new Uint8Array(bin.length);
  for (var i = 0; i < bin.length; i++) {
    buffer[i] = bin.charCodeAt(i);
  }
  try{
    var blob = new Blob([buffer.buffer], {
      type: 'image/jpeg'
    });
  }catch (e){
    return false;
  }
  return blob;
}

サンプルコード

<canvas id="canvas"></canvas>
<button id="save">保存</button>
$('#save').on('click', function (e) {
  var base64 = $('#canvas').toDataURL();
  resizeImage(base64, function(base64) {
    var blob = base64toBlob(base64);
    var url = (window.URL || window.webkitURL);
    var dataUrl = url.createObjectURL(blob);
 
        var a = document.createElement('a');
        a.download = "download.jpg";
        a.href = dataUrl;
 
        var evt = document.createEvent('MouseEvent');
        evt.initEvent("click", true, false);
        a.dispatchEvent( evt );
  });
  return false;
});
 
/**
 * 画像のリサイズ
 * @param  {string}   base64   [base64]
 * @param  {Function} callback [Function]
 * @return {string}            [base64]
 */
resizeImage = function(base64, callback) {
    const MIN_SIZE = 800;
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    var image = new Image();
    image.crossOrigin = "Anonymous";
    image.onload = function(event){
        var dstWidth, dstHeight;
        if (this.width > this.height) {
            dstWidth = MIN_SIZE;
            dstHeight = this.height * MIN_SIZE / this.width;
        } else {
            dstHeight = MIN_SIZE;
            dstWidth = this.width * MIN_SIZE / this.height;
        }
        canvas.width = dstWidth;
        canvas.height = dstHeight;
        ctx.drawImage(this, 0, 0, this.width, this.height, 0, 0, dstWidth, dstHeight);
        callback(canvas.toDataURL());
    };
    image.src = base64;
};
 
/**
 * base64からBlobにコンパイル
 * @param  {string} base64 [base64]
 * @return {string}        [blob]
 */
function base64toBlob(base64) {
  var bin = atob(base64.replace(/^.*,/, ''));
  var buffer = new Uint8Array(bin.length);
  for (var i = 0; i < bin.length; i++) {
    buffer[i] = bin.charCodeAt(i);
  }
  try{
    var blob = new Blob([buffer.buffer], {
      type: 'image/jpeg'
    });
  }catch (e){
    return false;
  }
  return blob;
}