20-12-13 Object.create / 二分查找法压缩图片

134 阅读1分钟

Object.create和new Object

new Object() 通过构造函数来创建对象, 添加的属性是在自身实例下。
Object.create() es6创建对象的另一种方式,可以理解为继承一个对象, 添加的属性是在原型下。

// new Object() 方式创建
var a = {  rep : 'apple' }
var b = new Object(a)
console.log(b) // {rep: "apple"}
console.log(b.__proto__) // {}
console.log(b.rep) // "apple"

// Object.create() 方式创建
var a = { rep: 'apple' }
var b = Object.create(a)
console.log(b)  // {}
console.log(b.__proto__) // {rep: "apple"}
console.log(b.rep) // "apple"

二分查找法压缩图片

methods: {
    uploadFile() {
      const file = this.$refs.frontRef.files[0];
      this.compress(file, 50)
      .then(res => {
          console.log(res)
      })
    },
    getObjectUrl(file) {
      let url = null;
      if (window.createObjectURL != undefined) {
        // basic
        url = window.createObjectURL(file);
      } else if (window.URL != undefined) {
        // mozilla(firefox)
        url = window.URL.createObjectURL(file);
      } else if (window.webkitURL != undefined) {
        // webkit or chrome
        url = window.webkitURL.createObjectURL(file);
      }
      return url;
    },
    fileToImage(blob) {
      return new Promise((resolve) => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.src = this.getObjectUrl(blob);
      });
    },
    imgToCanvas(img) {
      return new Promise((resolve) => {
        const canvas = document.createElement("canvas");
        const context = canvas.getContext("2d");
        const imgWidth = img.width;
        const imgHeight = img.height;
        canvas.width = imgWidth;
        canvas.height = imgHeight;
        // 尺寸与原图保持一致,因此等高等宽
        context.clearRect(0, 0, imgWidth, imgHeight);
        context.drawImage(img, 0, 0, imgWidth, imgHeight);
        resolve(canvas);
      });
    },

    canvasToBlob(canvas, type, encoderOptions){
        return new Promise(resolve => {
            canvas.toBlob(function(blob){
                return resolve(blob);
            }, type, encoderOptions);
        })
    },

    compress(originfile, limitedSize) {
      return new Promise(async (resolve, reject) => {
        const originSize = originfile.size / 1024; // 计算文件大小 单位为kb
        // 若小于limitedSize,则不需要压缩,直接返回
        if (originSize < limitedSize) {
          resolve({ file: originfile, msg: "体积小于期望值,无需压缩!" });
          return;
        }
        // 将获取到的文件恢复成图片
        const img = await this.fileToImage(originfile);
        // 使用此图片生成需要的canvas
        const canvas = await this.imgToCanvas(img);
        // 为规避js精度问题,将encoderOptions乘100为整数,初始最大size为MAX_SAFE_INTEGER
        const maxQualitySize = {
          encoderOptions: 100,
          size: Number.MAX_SAFE_INTEGER,
        };
        // 初始最小size为0
        const minQualitySize = { encoderOptions: 0, size: 0 };
        let encoderOptions = 100; // 初始质量参数
        let count = 0; // 压缩次数
        let errorMsg = ""; // 出错信息
        let compressBlob = null; // 压缩后的文件Blob
        //  压缩思路,用二分法找最佳的压缩点 最多尝试8次即可覆盖全部可能
        while (count < 8) {
          compressBlob = await this.canvasToBlob(
            canvas,
            "image/jpeg",
            encoderOptions / 100
          );
          const compressSize = compressBlob.size / 1024;
          count++;
          if (compressSize === limitedSize) {
            // 压缩后的体积与期望值相等  压缩完成,总共压缩了count次
            break;
          } else if (compressSize > limitedSize) {
            // 压缩后的体积比期望值大
            maxQualitySize.encoderOptions = encoderOptions;
            maxQualitySize.size = compressSize;
          } else {
            // 压缩后的体积比期望值小
            minQualitySize.encoderOptions = encoderOptions;
            minQualitySize.size = compressSize;
          }

          encoderOptions = (maxQualitySize.encoderOptions + minQualitySize.encoderOptions) >> 1;
          
          if (maxQualitySize.encoderOptions - minQualitySize.encoderOptions < 2) {
            if (!minQualitySize.size && encoderOptions) {
              encoderOptions = minQualitySize.encoderOptions;
            } else if (!minQualitySize.size && !encoderOptions) {
              errorMsg = "压缩失败,无法压缩到指定大小";
              break;
            } else if (minQualitySize.size > limitedSize) {
              errorMsg = "压缩失败,无法压缩到指定大小";
              break;
            } else {
              //  压缩完成
              encoderOptions = minQualitySize.encoderOptions;
              compressBlob = await this.canvasToBlob(
                canvas,
                "image/jpeg",
                encoderOptions / 100
              );
              break;
            }
          }
        }
        // 压缩失败,则返回原始图片的信息
        if (errorMsg) {
          reject({
            msg: errorMsg,
            file: originfile,
          });
          return;
        }
        const compressSize = compressBlob.size / 1024;
        
        console.log(`最后一次压缩后,encoderOptions为:${encoderOptions},大小:${compressSize}`);
        // 生成文件
        const compressedFile = new File([compressBlob], originfile.name, {type: "image/jpeg"});
        
        resolve({ file: compressedFile, compressBlob, msg: "压缩成功!" });
      });
    },
  },