客户端PNG压缩使用javascript如pngcrush

Client PNG compression using javascript like pngcrush?

本文关键字:javascript pngcrush PNG 压缩 客户端      更新时间:2023-09-26

我正在开发HTML5应用。

用户从手机上传图片时,图片大小过大。

我想把图像压缩为PNG,就像pngcrush的方式。

有什么好的方法来选择前端(如javascript库)?

或者是否可以将pngcrush库移植到javascript?

有几个项目似乎是基于使用emscripten(一个LLVM-to-JavaScript编译器)的想法来实际编译从pngcrush到浏览器的工作JavaScript的源代码。

  • JavaScript-Packer/PNGCrush.html -基于pngcrush-1.7.27
  • richardassar/pngcrush.js -基于pngcrush-1.7.27
  • pngcrush-crushed -基于pngcrush-1.7.58

pngcrush-1.7.27的版本是目前唯一一个似乎不会产生损坏的图像对我来说。我在这里放了一个使用承诺的例子:http://plnkr.co/edit/iLpbOjlYiacR04oGdXSI?p=preview

下面是一个基本用法的例子:

var instance = new pngcrush();
instance.exec(inputFile, function (stdoutEvent) {
  console.log(stdoutEvent.data.line);
}).then(function (doneEvent) {
  var outputFile = new Blob([doneEvent.data.data], { type: 'image/png' });
  // do something with the outputFile
});

以下是上述柱塞pngcrush-class.js文件的内容,供参考:

(function(exports) {
  var noop = function () {};
  function pngcrush () {
    this.callbacks = {
      'error':  [],
      'done':   [],
      'start':  [],
      'stdout': []
    };
  }
  pngcrush.prototype.exec = function (file, notify) {
    var self = this;
    if (this.execPromise) {
      return this.execPromise.catch(noop).then(function () {
        return self.exec(file, notify);
      });
    }
    if (file.type !== 'image/png') {
      return Promise.reject(file);
    }
    var promise = this.execPromise = this.readAsArrayBuffer(file).then(function (event) {
      var arrayBuffer = event.target.result;
      return self.initWorker().then(function (worker) {
        var done = new Promise(function (resolve, reject) {
          var offDone, offError, offStdout;
          offDone = self.once('done', function (event) {
            offError();
            offStdout();
            resolve(event);
          });
          offError = self.once('error', function (event) {
            offDone();
            offStdout();
            reject(event);
          });
          offStdout = self.on('stdout', function (event) {
            if (typeof notify === 'function') {
              notify.call(self, event);
            }
          });
          worker.postMessage({
            'type': 'file',
            'data': new Uint8Array(arrayBuffer)
          });
          worker.postMessage({
            'type': 'command',
            'command': 'go'
          });
        });
        done.catch(noop).then(function () {
          worker.terminate();
        });
        return done;
      });
    });
    promise.catch(noop).then(function () {
      if (promise === self.execPromise) {
        delete self.execPromise;
      }
    });
    return promise;
  };
  pngcrush.prototype.initWorker = function () {
    var self = this;
    if (this.workerPromise) {
      return this.workerPromise;
    }
    var promise = this.workerPromise = new Promise(function (resolve, reject) {
      var worker = new Worker('worker.js');
      worker.onerror = function (event) {
        var callbacks = [];
        reject(event);
        Array.prototype.push.apply(callbacks, self.callbacks.error);
        while (callbacks.length) {
          callbacks.shift().call(self, event);
        }
      };
      worker.onmessage = function (event) {
        if (event.data.type === 'ready') {
          worker.onmessage = function (event) {
            var name = event.data.type;
            if (typeof self.callbacks[name] !== 'undefined') {
              var callbacks = [];
              Array.prototype.push.apply(callbacks, self.callbacks[name]);
              while (callbacks.length) {
                callbacks.shift().call(self, event);
              }
            }
          };
          resolve(worker);
        }
      };
    });
    promise.catch(noop).then(function () {
      if (promise === self.workerPromise) {
        delete self.workerPromise;
      }
    });
    return promise;
  };
  pngcrush.prototype.on = function (name, callback) {
    var self = this;
    if (typeof this.callbacks[name] !== 'undefined' && typeof callback === 'function') {
      this.callbacks[name].push(callback);
      var off = (function () {
        var ran = false;
        return function () {
          if (ran === true) {
            return;
          }
          ran = true;
          var idx = self.callbacks[name].lastIndexOf(callback);
          if (idx !== -1) {
            self.callbacks[name].splice(idx - 1, 1);
          }
        };
      })();
      return off;
    }
    return noop;
  };
  pngcrush.prototype.once = function (name, callback) {
    var off = this.on(name, function () {
      off();
      callback.apply(this, arguments);
    });
    return off;
  };
  pngcrush.prototype.readAsArrayBuffer = function (file) {
    var fileReader = new FileReader();
    return new Promise(function (resolve, reject) {
      fileReader.onerror = reject;
      fileReader.onload  = resolve;
      fileReader.readAsArrayBuffer(file);
    });
  };
  pngcrush.prototype.readAsDataURL = function (file) {
    var fileReader = new FileReader();
    return new Promise(function (resolve, reject) {
      fileReader.onerror = reject;
      fileReader.onload  = resolve;
      fileReader.readAsDataURL(file);
    });
  };
  exports.pngcrush = pngcrush;
})(this);