[
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "README-CN.md",
    "content": "## 简介\n\n`js-image-compressor` 是一个实现轻量级图片压缩的 `javascript` 库，压缩后仅有 `5kb`，在前端页面即可实现对图片的压缩。在提供基本图片压缩功能同时，还暴露出图片处理相关公用方法，以及进行边界情况处理：\n\n- 可以对待转化图片大小设置一定的阈值，使得图片转化成 `png` 格式在不理想情况下不至于过大，同时大于这个阈值则可以自动转化成 `jpeg` 格式，实现更优压缩；\n- 可以限制输出图片宽高大小，从而防止意外情况发生，比如压缩运算过大使得浏览器奔溃；\n- 默认对 `png` 输出图片添加透明底色，其他格式设为白色，避免“黑屏”发生；\n- 读取 `jpeg` 格式图片的 `EXIF` 信息，矫正图片方位；\n- 提供一些图片处理的常用工具函数（`image2Canvas`、`canvas2Blob` 和 `canvas2DataUrl` 等），用户还可以自定义图片输出的样式特征（比如可以灰度处理、加水印）。\n\n文档语言：\n\n- [英文](./README.md)\n- [中文](./README-CN.md)\n\n## 使用\n\n### 安装引入\n\n你可以通过npm去安装依赖：\n\n```js\nnpm install js-image-compressor --save-dev\n```\n\n也可以在下载后，在 `dist` 目录下找到 `image-compress.min.js` 文件在页面中通过 `script` 引入：\n\n```html\n<script src=\"../dist/image-compressor.js\"></script>\n```\n\n### 简单使用\n\n你可以只传入待压缩图片对象，其他参数都是非必须的，插件按照默认参数自动完成图片压缩处理。不过这样输出的压缩图片符合以下特征：\n\n- 默认按照 `0.8` 压缩率配置；\n- 输出图片宽/高维持源图片宽/高；\n- 一般的，输出图片格式保持源图片格式；\n- 当 `png` 图片的 `size` 大于 `2m` 时，默认转化成 `jpeg` 格式图片；\n- 给 `png` 图片填充透明色；\n- 当输出图片 `size` 大于源图片时，将源图片当作输出图片返回；\n- `jpeg` 格式图片，矫正翻转/旋转方向；\n\n如果这些默认配置不能满足你的需求，可能需要其他参数配置。以下是一个简单使用配置：\n\n```js\nvar options = {\n  file: file,\n\n  // 压缩前回调\n  beforeCompress: function (result) {\n    console.log('压缩之前图片尺寸大小: ', result.size);\n    console.log('mime 类型: ', result.type);\n  },\n\n  // 压缩成功回调\n  success: function (result) {\n    console.log('result: ', result)\n    console.log('压缩之后图片尺寸大小: ', result.size);\n    console.log('mime 类型: ', result.type);\n    console.log('实际压缩率： ', ((file.size - result.size) / file.size * 100).toFixed(2) + '%');\n  }\n};\n\nnew ImageCompressor(options);\n```\n\n其中，钩子函数 `beforeCompress` 发生在读取图片之后，创建画布之前；钩子函数 `success` 函数发生在压缩完成生成图片之后。它们回调参数 `result` 是整合来尺寸、图片类型和大小等相关信息的 `blob` 对象。\n\n### 标准使用\n\n在标准使用中，我们可以根据自身需求自定义配置压缩比（`quality`）、输出图片类型（`mimeType`）、宽（`width`）、高（`height`）、最大宽（`maxWidth`）、最大高（`maxHeight`）、最小宽（`minWidth`）、最大高（`minHeight`）、png转jpeg阈值（`convertSize`）、是否矫正jpeg方向（`redressOrientation`）和是否宽松模式（`loose`）。\n\n- 是否矫正jpeg方向（`redressOrientation`），`jpeg` 格式图片在某些iOS浏览器会按其方向呈现图像，这个选项可以控制恢复初始方向，默认为 `true`；\n- 是否宽松模式（`loose`）、的意思是控制当压缩的图片 `size` 大于源图片，输出源图片，否则输出压缩后图片，默认是 `true`。\n\n以下是标准配置：\n\n```js\nvar options = {\n  file: file,\n  quality: 0.6,\n  mimeType: 'image/jpeg',\n  maxWidth: 2000,\n  maxHeight: 2000,\n  width: 1000,\n  height: 1000,\n  minWidth: 500,\n  minHeight: 500,\n  convertSize: Infinity,\n  loose: true,\n  redressOrientation: true,\n\n  // 压缩前回调\n  beforeCompress: function (result) {\n    console.log('压缩之前图片尺寸大小: ', result.size);\n    console.log('mime 类型: ', result.type);\n  },\n\n  // 压缩成功回调\n  success: function (result) {\n    console.log('压缩之后图片尺寸大小: ', result.size);\n    console.log('mime 类型: ', result.type);\n    console.log('实际压缩率： ', ((file.size - result.size) / file.size * 100).toFixed(2) + '%');\n  },\n\n  // 发生错误\n  error: function (msg) {\n    console.error(msg);\n  }\n};\n\nnew ImageCompressor(options);\n```\n\n`error` 钩子函数是图片压缩过程中错误回调，没有这个回调错误则会在插件中 `throw new Error(msg)` 形式抛出。\n\n### 其他钩子函数\n\n在压缩输出图片之前，我们还可以对画布进行一些自定义处理，融入元素。\n\n以下是对图片进行灰度和加水印处理：\n\n```js\nvar options = {\n  file: file,\n\n  // 图片绘画前\n  beforeDraw: function (ctx) {\n    vm.btnText = '准备绘图...';\n    console.log('准备绘图...');\n    ctx.filter = 'grayscale(100%)';\n  },\n\n  // 图片绘画后\n  afterDraw: function (ctx, canvas) {\n    ctx.restore();\n    vm.btnText = '绘图完成...';\n    console.log('绘图完成...');\n    ctx.fillStyle = '#fff';\n    ctx.font = (canvas.width * 0.1) + 'px microsoft yahei';\n    ctx.fillText(vm.watermarkText, 10, canvas.height - 20);\n  },\n};\n\nnew ImageCompressor(options);\n```\n\n`beforeDraw` 是在画布创建后，图片绘画前的钩子函数，`afterDraw` 是在图绘画后的钩子函数。\n\n### 工具函数\n\n下图归纳了 `js-image-compressor` 插件从用户图片通过 `input` 的 `file` 本地上传到对图片压缩的详细过程，同时暴露出这些工具方法供用户使用。\n\n![js-image-compressor](./relation-chart.jpg)\n"
  },
  {
    "path": "README.md",
    "content": "## Introduction\n\n`js-image-compressor` is a `javascript` library that implements lightweight image compression. After compression, it is only `5kb`, and the image can be compressed on the front-end page. While providing basic image compression functions, it also exposes related public methods of image processing, as well as border case processing:\n\n- A certain threshold can be set for the size of the converted image, so that the image converted to `png` format will not be too large under undesirable conditions, and at the same time larger than this threshold, it can be automatically converted to `jpeg` format for better compression;\n- You can limit the width and height of the output image to prevent accidents, such as excessive compression operations that cause the browser to crash;\n- By default, a transparent background color is added to the output image of `png`, and other formats are set to white to avoid \"black screen\";\n- Read the `EXIF` information of `jpeg` format pictures, and correct the picture orientation;\n- Provide some common tool functions for image processing (`image2Canvas`, `canvas2Blob` and `canvas2DataUrl`, etc.), and users can also customize the style features of image output (for example, grayscale processing, watermarking).\n\nDocument language:\n\n- [English](./README.md)\n- [Chinese](./README-CN.md)\n\n## Use\n\n### Installation and introduction\n\nYou can install dependencies through npm:\n\n```js\nnpm install js-image-compressor --save-dev\n```\n\nYou can also find the file `image-compress.min.js` in the `dist` directory after downloading and import it through `script` on the page:\n\n```html\n<script src=\"../dist/image-compressor.js\"></script>\n```\n\n### Simple to use\n\nYou can only pass in the image object to be compressed, other parameters are optional, and the plug-in automatically completes the image compression processing according to the default parameters. However, the compressed image output in this way meets the following characteristics:\n\n- The default configuration is based on `0.8` compression ratio;\n- Output picture width/height maintains the source picture width/height;\n- Generally, the output image format keeps the original image format;\n- When the `size` of the `png` image is greater than `2m`, it will be converted into a `jpeg` format image by default;\n- Fill the `png` picture with a transparent color;\n- When the output picture `size` is larger than the source picture, the source picture will be returned as the output picture;\n- `jpeg` format picture, correct the flip/rotation direction;\n\nIf these default configurations cannot meet your needs, other parameter configurations may be required. The following is a simple configuration:\n\n```js\nvar options = {\n  file: file,\n\n  // Callback before compression\n  beforeCompress: function (result) {\n    console.log('Image size before compression:', result.size);\n    console.log('mime type:', result.type);\n  },\n\n  // Compression success callback\n  success: function (result) {\n    console.log('result:', result)\n    console.log('Image size after compression:', result.size);\n    console.log('mime type:', result.type);\n    console.log('Actual compression ratio:', ((file.size-result.size) / file.size * 100).toFixed(2) +'%');\n  }\n};\n\nnew ImageCompressor(options);\n```\n\nAmong them, the hook function `beforeCompress` occurs after the image is read and before the canvas is created; the hook function `success` function occurs after the compression is completed to generate the image. Their callback parameter `result` is a `blob` object that integrates relevant information such as size, picture type and size.\n\n### Standard use\n\nIn standard use, we can customize the compression ratio (`quality`), output image type (`mimeType`), width (`width`), height (`height`), and maximum width (`maxWidth`) according to our own needs. ), maximum height (`maxHeight`), minimum width (`minWidth`), maximum height (`minHeight`), png to jpeg threshold (`convertSize`), whether to correct the jpeg direction (`redressOrientation`) and whether the loose mode ( `loose`).\n\n- Whether to correct the jpeg orientation (`redressOrientation`), the `jpeg` format image will be presented according to its orientation in some iOS browsers, this option can control the restoration of the initial orientation, the default is `true`;\n- Whether it is loose mode (`loose`), which means to control when the compressed image `size` is larger than the source image, output the source image, otherwise output the compressed image, the default is `true`.\n\nThe following is the standard configuration:\n\n```js\nvar options = {\n  file: file,\n  quality: 0.6,\n  mimeType:'image/jpeg',\n  maxWidth: 2000,\n  maxHeight: 2000,\n  width: 1000,\n  height: 1000,\n  minWidth: 500,\n  minHeight: 500,\n  convertSize: Infinity,\n  loose: true,\n  redressOrientation: true,\n\n  // Callback before compression\n  beforeCompress: function (result) {\n    console.log('Image size before compression:', result.size);\n    console.log('mime type:', result.type);\n  },\n\n  // Compression success callback\n  success: function (result) {\n    console.log('Image size after compression:', result.size);\n    console.log('mime type:', result.type);\n    console.log('Actual compression ratio:', ((file.size-result.size) / file.size * 100).toFixed(2) +'%');\n  },\n\n  // An error occurred\n  error: function (msg) {\n    console.error(msg);\n  }\n};\n\nnew ImageCompressor(options);\n```\n\nThe `error` hook function is an error callback during the image compression process. Without this callback error, it will be thrown in the form of `throw new Error(msg)` in the plugin.\n\n### Other hook functions\n\nBefore compressing the output image, we can also customize the canvas to incorporate elements.\n\nThe following is the grayscale and watermark processing of the picture:\n\n```js\nvar options = {\n  file: file,\n\n  // Before picture painting\n  beforeDraw: function (ctx) {\n    vm.btnText ='Ready to draw...';\n    console.log('Ready to draw...');\n    ctx.filter ='grayscale(100%)';\n  },\n\n  // After the picture is painted\n  afterDraw: function (ctx, canvas) {\n    ctx.restore();\n    vm.btnText ='Drawing completed...';\n    console.log('Drawing completed...');\n    ctx.fillStyle ='#fff';\n    ctx.font = (canvas.width * 0.1) +'px microsoft yahei';\n    ctx.fillText(vm.watermarkText, 10, canvas.height-20);\n  },\n};\n\nnew ImageCompressor(options);\n```\n\n`beforeDraw` is the hook function before the picture is drawn after the canvas is created, and `afterDraw` is the hook function after the picture is drawn.\n\n### Tool functions\n\nThe following figure summarizes the detailed process of the `js-image-compressor` plug-in from uploading user pictures locally through the `file` of `input` to image compression, and at the same time exposes these tools and methods for users to use.\n\n![js-image-compressor](./relation-chart.jpg)"
  },
  {
    "path": "demo/default/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>js-image-compressor</title>\n  <link rel=\"stylesheet\" href=\"../index.css\">\n</head>\n\n<body>\n  <div id=\"app\">\n    <div class=\"row\">\n      <div class=\"column upload\">\n        <div class=\"drop-content\" @drop=\"dropFile\" @dragenter=\"dragFileEnter\" @dragover=\"drageFileOver\">\n          <input id=\"file-elem\" type=\"file\" accept=\"image/*\" @change=\"inputChange\">\n          <span>拖入文件或</span><label for=\"file-elem\">点击选择文件</label>\n        </div>\n      </div>\n      <div class=\"column\">\n        <div>\n          <h3>设置参数</h3>\n          <p>\n            <span>mime 类型：</span>\n            <select v-model=\"mimeType\">\n              <option value=\"auto\">auto</option>\n              <option value=\"image/png\">image/png</option>\n              <option value=\"image/jpeg\">image/jpeg</option>\n              <option value=\"image/webp\">image/webp</option>\n              <option value=\"image/gif\">image/gif</option>\n            </select>\n          </p>\n          <p>\n            <span>maxWidth：</span>\n            <input type=\"text\" v-model=\"maxWidth\">\n          </p>\n          <p>\n            <span>maxHeight：</span>\n            <input type=\"text\" v-model=\"maxHeight\">\n          </p>\n          <p>\n            <span>width：</span>\n            <input type=\"text\" v-model=\"width\">\n          </p>\n          <p>\n            <span>height：</span>\n            <input type=\"text\" v-model=\"height\">\n          </p>\n          <p>\n            <span>minWidth：</span>\n            <input type=\"text\" v-model=\"minWidth\">\n          </p>\n          <p>\n            <span>minHeigth：</span>\n            <input type=\"text\" v-model=\"minHeight\">\n          </p>\n          <p>\n            <span>png转jpeg阈值：</span>\n            <select v-model=\"convertSize\">\n              <option :value=\"1024000\">1mb</option>\n              <option :value=\"2048000\">2mb</option>\n              <option :value=\"5120000\">5mb</option>\n              <option :value=\"10240000\">10mb</option>\n              <option :value=\"Infinity\">Infinity</option>\n            </select>\n          </p>\n          <p>\n            <span>宽松模式：</span>\n            <select v-model=\"loose\">\n              <option :value=\"true\">是</option>\n              <option :value=\"false\">否</option>\n            </select>\n          </p>\n          <p>\n            <span>矫正jpeg方向：</span>\n            <select v-model=\"redressOrientation\">\n              <option :value=\"true\">是</option>\n              <option :value=\"false\">否</option>\n            </select>\n          </p>\n          <p>\n            <span>压缩率：</span>\n            <input type=\"number\" max=\"1\" min=\"0.05\" step=\"0.05\" v-model=\"quality\">\n          </p>\n          <p>\n            <button @click=\"submit\">{{btnText}}</button>\n          </p>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"column origin\">\n        <img :src=\"originImgUrl\" />\n      </div>\n      <div class=\"column\">\n        <h3>源图片信息</h3>\n        <div>\n          <span>mime类型：{{originMimeType}}</span>\n        </div>\n        <div>\n          <span>尺寸：{{originImgWidth}} x {{originImgHeight}}</span>\n        </div>\n        <div>\n          <span>大小：{{originSize}}</span>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"column output\">\n        <img :src=\"outputImgUrl\" />\n      </div>\n      <div class=\"column\">\n        <h3>压缩图片信息</h3>\n        <div>\n          <span>mime类型：{{outputMimeType}}</span>\n        </div>\n        <div>\n          <span>尺寸：{{outputImgWidth}} x {{outputImgHeight}}</span>\n        </div>\n        <div>\n          <span>大小：{{outputSize}}</span>\n        </div>\n        <div>\n          <span>实际压缩率：{{compressRatio}}</span>\n        </div>\n        <div>\n          <a :href=\"outputImgUrl\" :download=\"imgName\">下载</a>\n        </div>\n      </div>\n    </div>\n  </div>\n  <script src=\"../vue.min.js\"></script>\n  <script src=\"../../dist/image-compressor.js\"></script>\n  <script src=\"./main.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "demo/default/main.js",
    "content": "var BTN_OK = '确定';\n\nnew Vue({\n  el: '#app',\n  data: function () {\n    return {\n      file: null,\n      btnText: BTN_OK,\n      imgName: '',\n      mimeType: 'auto',\n      originImgUrl: '',\n      originMimeType: 'auto',\n      originSize: 0,\n      originImgWidth: 'auto',\n      originImgHeight: 'auto',\n      outputImgUrl: '',\n      outputMimeType: 'auto',\n      outputImgWidth: 'auto',\n      outputImgHeight: 'auto',\n      outputSize: 0,\n      compressRatio: 0,\n      quality: 0.6,\n      maxWidth: 0,\n      maxHeight: 0,\n      width: 0,\n      height: 0,\n      minWidth: 0,\n      minHeight: 0,\n      convertSize: Infinity,\n      loose: true,\n      redressOrientation: true\n    }\n  },\n\n  methods: {\n    /**\n     * 拖拽文件进入元素\n     * @param {Event} ev 事件\n     */\n    dragFileEnter: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n    },\n\n    /**\n     * 拖拽文件在元素上\n     * @param {Event} ev 事件\n     */\n    drageFileOver: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n    },\n\n    /**\n     * 放开文件拖拽\n     * @param {Event} ev 事件\n     */\n    dropFile: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n\n      var dt = ev.dataTransfer;\n      var file = dt.files[0];\n      this.file = file;\n      this.compressImage(file);\n    },\n\n    /**\n     * 上传文件改变事件\n     * @param {Event} ev 事件\n     */\n    inputChange: function (ev) {\n      var file = ev.target.files[0];\n      this.file = file;\n      this.compressImage(file);\n    },\n\n    /**\n     * 确定提交\n     */\n    submit: function () {\n      this.compressImage(this.file);\n    },\n\n    /**\n     * 压缩图片\n     * @param {File} file `File` 对象\n     */\n    compressImage: function (file) {\n      var vm = this;\n      vm.btnText = '读取中...';\n      var options = {\n        file: file,\n        quality: this.quality,\n        mimeType: this.mimeType,\n        maxWidth: this.maxWidth,\n        maxHeight: this.maxHeight,\n        width: this.width,\n        height: this.height,\n        minWidth: this.minWidth,\n        minHeight: this.minHeight,\n        convertSize: this.convertSize,\n        loose: this.loose,\n        redressOrientation: this.redressOrientation,\n\n        // 压缩前回调\n        beforeCompress: function (result) {\n          vm.btnText = '处理中...';\n          vm.originImgWidth = result.width;\n          vm.originImgHeight = result.height;\n          vm.originSize = result.size;\n          vm.originMimeType = result.type;\n          console.log('压缩之前图片尺寸大小: ', result.size);\n          console.log('mime 类型: ', result.type);\n          // 将上传图片在页面预览\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.originImgUrl = url;\n          })\n        },\n\n        // 压缩成功回调\n        success: function (result) {\n          vm.btnText = BTN_OK;\n          vm.imgName = result.name;\n          console.log('result: ', result)\n          console.log('压缩之后图片尺寸大小: ', result.size);\n          console.log('mime 类型: ', result.type);\n          console.log('实际压缩率： ', ((file.size - result.size) / file.size * 100).toFixed(2) + '%');\n\n          vm.outputImgWidth = result.width;\n          vm.outputImgHeight = result.height;\n          vm.outputSize = result.size;\n          vm.outputMimeType = result.type;\n          vm.compressRatio = ((file.size - result.size) / file.size * 100).toFixed(2) + '%';\n\n          // 生成压缩后图片在页面展示\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.outputImgUrl = url;\n          })\n          // 上传到远程服务器\n          // util.upload('/upload.png', result);\n        },\n\n        // 发生错误\n        error: function (msg) {\n          vm.btnText = BTN_OK;\n          console.error(msg);\n        }\n      };\n\n      new ImageCompressor(options);\n    }\n  }\n})"
  },
  {
    "path": "demo/grayscale/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>grayscale demo</title>\n  <link rel=\"stylesheet\" href=\"../index.css\">\n</head>\n\n<body>\n  <div id=\"app\">\n    <div class=\"row\">\n      <div class=\"column upload\">\n        <div class=\"drop-content\" @drop=\"dropFile\" @dragenter=\"dragFileEnter\" @dragover=\"drageFileOver\">\n          <input id=\"file-elem\" type=\"file\" accept=\"image/*\" @change=\"inputChange\">\n          <span>拖入文件或</span><label for=\"file-elem\">点击选择文件</label>\n        </div>\n      </div>\n      <div class=\"column\">\n        <div>\n          <h3>设置参数</h3>\n          <p>\n            <span>压缩率：</span>\n            <input type=\"number\" max=\"1\" min=\"0.05\" step=\"0.05\" v-model=\"quality\">\n          </p>\n          <p>\n            <button @click=\"submit\">{{btnText}}</button>\n          </p>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"column origin\">\n        <img :src=\"originImgUrl\" />\n      </div>\n      <div class=\"column\">\n        <h3>源图片信息</h3>\n        <div>\n          <span>mime类型：{{originMimeType}}</span>\n        </div>\n        <div>\n          <span>尺寸：{{originImgWidth}} x {{originImgHeight}}</span>\n        </div>\n        <div>\n          <span>大小：{{originSize}}</span>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"column output\">\n        <img :src=\"outputImgUrl\" />\n      </div>\n      <div class=\"column\">\n        <h3>压缩图片信息</h3>\n        <div>\n          <span>mime类型：{{outputMimeType}}</span>\n        </div>\n        <div>\n          <span>尺寸：{{outputImgWidth}} x {{outputImgHeight}}</span>\n        </div>\n        <div>\n          <span>大小：{{outputSize}}</span>\n        </div>\n        <div>\n          <span>实际压缩率：{{compressRatio}}</span>\n        </div>\n        <div>\n          <a :href=\"outputImgUrl\" :download=\"imgName\">下载</a>\n        </div>\n      </div>\n    </div>\n  </div>\n  <script src=\"../vue.min.js\"></script>\n  <script src=\"../../dist/image-compressor.js\"></script>\n  <script src=\"./main.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "demo/grayscale/main.js",
    "content": "var BTN_OK = '确定';\n\nnew Vue({\n  el: '#app',\n  data: function () {\n    return {\n      file: null,\n      btnText: BTN_OK,\n      imgName: '',\n      originImgUrl: '',\n      originMimeType: 'auto',\n      originSize: 0,\n      originImgWidth: 'auto',\n      originImgHeight: 'auto',\n      outputImgUrl: '',\n      outputMimeType: 'auto',\n      outputImgWidth: 'auto',\n      outputImgHeight: 'auto',\n      outputSize: 0,\n      compressRatio: 0,\n      quality: 0.6\n    }\n  },\n\n  methods: {\n    /**\n     * 拖拽文件进入元素\n     * @param {Event} ev 事件\n     */\n    dragFileEnter: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n    },\n\n    /**\n     * 拖拽文件在元素上\n     * @param {Event} ev 事件\n     */\n    drageFileOver: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n    },\n\n    /**\n     * 放开文件拖拽\n     * @param {Event} ev 事件\n     */\n    dropFile: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n\n      var dt = ev.dataTransfer;\n      var file = dt.files[0];\n      this.file = file;\n      this.compressImage(file);\n    },\n\n    /**\n     * 上传文件改变事件\n     * @param {Event} ev 事件\n     */\n    inputChange: function (ev) {\n      var file = ev.target.files[0];\n      this.file = file;\n      this.compressImage(file);\n    },\n\n    /**\n     * 确定提交\n     */\n    submit: function () {\n      this.compressImage(this.file);\n    },\n\n    /**\n     * 压缩图片\n     * @param {File} file `File` 对象\n     */\n    compressImage: function (file) {\n      var vm = this;\n      vm.btnText = '读取中...';\n      var options = {\n        file: file,\n        quality: this.quality,\n\n        // 压缩前回调\n        beforeCompress: function (result) {\n          vm.btnText = '处理中...';\n          vm.imgName = result.name;\n          vm.originImgWidth = result.width;\n          vm.originImgHeight = result.height;\n          vm.originSize = result.size;\n          vm.originMimeType = result.type;\n          console.log('压缩之前图片尺寸大小: ', result.size);\n          console.log('mime 类型: ', result.type);\n          // 将上传图片在页面预览\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.originImgUrl = url;\n          })\n        },\n\n        // 图片绘画前\n        beforeDraw: function (ctx) {\n          vm.btnText = '准备绘图...';\n          console.log('准备绘图...');\n          ctx.filter = 'grayscale(100%)';\n        },\n\n        // 压缩成功回调\n        success: function (result) {\n          vm.btnText = BTN_OK;\n          console.log('result: ', result)\n          console.log('压缩之后图片尺寸大小: ', result.size);\n          console.log('mime 类型: ', result.type);\n          console.log('实际压缩率： ', ((file.size - result.size) / file.size * 100).toFixed(2) + '%');\n\n          vm.outputImgWidth = result.width;\n          vm.outputImgHeight = result.height;\n          vm.outputSize = result.size;\n          vm.outputMimeType = result.type;\n          vm.compressRatio = ((file.size - result.size) / file.size * 100).toFixed(2) + '%';\n\n          // 生成压缩后图片在页面展示\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.outputImgUrl = url;\n          })\n          // 上传到远程服务器\n          // util.upload('/upload.png', result);\n        }\n      };\n\n      new ImageCompressor(options);\n    }\n  }\n})"
  },
  {
    "path": "demo/index.css",
    "content": ".row {\n  display: flex;\n  border-bottom: 1px dashed #999;\n}\n\n.column {\n  padding: 20px;\n}\n\n.upload,\n.origin,\n.output {\n  width: 300px;\n  max-height: 300px;\n  overflow: hidden;\n  text-align: center;\n}\n\n.origin img,\n.output img {\n  max-width: 100%;\n  max-height: 300px;\n  vertical-align: middle;\n}\n\n.drop-content {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border: 1px dotted #999;\n  border-radius: 4px;\n  min-height: 100px;\n  height: 100%;\n}\n\n.drop-content label {\n  color: #3db8fd;\n}\n\n.drop-content label:hover {\n  cursor: pointer;\n}\n\n#file-elem {\n  position: absolute !important;\n  height: 1px;\n  width: 1px;\n  overflow: hidden;\n  clip: rect(1px, 1px, 1px, 1px);\n}\n\n#file-elem:focus+label {\n  outline: thin dotted;\n}\n\n#file-elem:focus-within+label {\n  outline: thin dotted;\n}"
  },
  {
    "path": "demo/simple/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>simple demo</title>\n  <link rel=\"stylesheet\" href=\"../index.css\">\n</head>\n\n<body>\n  <div id=\"app\">\n    <div class=\"row\">\n      <div class=\"column upload\">\n        <div class=\"drop-content\" @drop=\"dropFile\" @dragenter=\"dragFileEnter\" @dragover=\"drageFileOver\">\n          <input id=\"file-elem\" type=\"file\" accept=\"image/*\" @change=\"inputChange\">\n          <span>拖入文件或</span><label for=\"file-elem\">点击选择文件</label>\n        </div>\n      </div>\n      <div class=\"column\">\n        <div>\n          <h3>设置参数</h3>\n          <p>\n            <span>压缩率：</span>\n            <input type=\"number\" max=\"1\" min=\"0.05\" step=\"0.05\" v-model=\"quality\">\n          </p>\n          <p>\n            <button @click=\"submit\">{{btnText}}</button>\n          </p>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"column origin\">\n        <img :src=\"originImgUrl\" />\n      </div>\n      <div class=\"column\">\n        <h3>源图片信息</h3>\n        <div>\n          <span>mime类型：{{originMimeType}}</span>\n        </div>\n        <div>\n          <span>尺寸：{{originImgWidth}} x {{originImgHeight}}</span>\n        </div>\n        <div>\n          <span>大小：{{originSize}}</span>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"column output\">\n        <img :src=\"outputImgUrl\" />\n      </div>\n      <div class=\"column\">\n        <h3>压缩图片信息</h3>\n        <div>\n          <span>mime类型：{{outputMimeType}}</span>\n        </div>\n        <div>\n          <span>尺寸：{{outputImgWidth}} x {{outputImgHeight}}</span>\n        </div>\n        <div>\n          <span>大小：{{outputSize}}</span>\n        </div>\n        <div>\n          <span>实际压缩率：{{compressRatio}}</span>\n        </div>\n        <div>\n          <a :href=\"outputImgUrl\" :download=\"imgName\">下载</a>\n        </div>\n      </div>\n    </div>\n  </div>\n  <script src=\"../vue.min.js\"></script>\n  <script src=\"../../dist/image-compressor.js\"></script>\n  <script src=\"./main.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "demo/simple/main.js",
    "content": "var BTN_OK = '确定';\n\nnew Vue({\n  el: '#app',\n  data: function () {\n    return {\n      file: null,\n      btnText: BTN_OK,\n      imgName: '',\n      originImgUrl: '',\n      originMimeType: 'auto',\n      originSize: 0,\n      originImgWidth: 'auto',\n      originImgHeight: 'auto',\n      outputImgUrl: '',\n      outputMimeType: 'auto',\n      outputImgWidth: 'auto',\n      outputImgHeight: 'auto',\n      outputSize: 0,\n      compressRatio: 0,\n      quality: 0.6\n    }\n  },\n\n  methods: {\n    /**\n     * 拖拽文件进入元素\n     * @param {Event} ev 事件\n     */\n    dragFileEnter: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n    },\n\n    /**\n     * 拖拽文件在元素上\n     * @param {Event} ev 事件\n     */\n    drageFileOver: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n    },\n\n    /**\n     * 放开文件拖拽\n     * @param {Event} ev 事件\n     */\n    dropFile: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n\n      var dt = ev.dataTransfer;\n      var file = dt.files[0];\n      this.file = file;\n      this.compressImage(file);\n    },\n\n    /**\n     * 上传文件改变事件\n     * @param {Event} ev 事件\n     */\n    inputChange: function (ev) {\n      var file = ev.target.files[0];\n      this.file = file;\n      this.compressImage(file);\n    },\n\n    /**\n     * 确定提交\n     */\n    submit: function () {\n      this.compressImage(this.file);\n    },\n\n    /**\n     * 压缩图片\n     * @param {File} file `File` 对象\n     */\n    compressImage: function (file) {\n      var vm = this;\n      vm.btnText = '读取中...';\n      var options = {\n        file: file,\n        quality: this.quality,\n\n        // 压缩前回调\n        beforeCompress: function (result) {\n          vm.btnText = '处理中...';\n          vm.imgName = result.name;\n          vm.originImgWidth = result.width;\n          vm.originImgHeight = result.height;\n          vm.originSize = result.size;\n          vm.originMimeType = result.type;\n          console.log('压缩之前图片尺寸大小: ', result.size);\n          console.log('mime 类型: ', result.type);\n          // 将上传图片在页面预览\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.originImgUrl = url;\n          })\n        },\n\n        // 压缩成功回调\n        success: function (result) {\n          vm.btnText = BTN_OK;\n          console.log('result: ', result)\n          console.log('压缩之后图片尺寸大小: ', result.size);\n          console.log('mime 类型: ', result.type);\n          console.log('实际压缩率： ', ((file.size - result.size) / file.size * 100).toFixed(2) + '%');\n\n          vm.outputImgWidth = result.width;\n          vm.outputImgHeight = result.height;\n          vm.outputSize = result.size;\n          vm.outputMimeType = result.type;\n          vm.compressRatio = ((file.size - result.size) / file.size * 100).toFixed(2) + '%';\n\n          // 生成压缩后图片在页面展示\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.outputImgUrl = url;\n          })\n          // 上传到远程服务器\n          // util.upload('/upload.png', result);\n        }\n      };\n\n      new ImageCompressor(options);\n    }\n  }\n})"
  },
  {
    "path": "demo/watermark/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>watermark demo</title>\n  <link rel=\"stylesheet\" href=\"../index.css\">\n</head>\n\n<body>\n  <div id=\"app\">\n    <div class=\"row\">\n      <div class=\"column upload\">\n        <div class=\"drop-content\" @drop=\"dropFile\" @dragenter=\"dragFileEnter\" @dragover=\"drageFileOver\">\n          <input id=\"file-elem\" type=\"file\" accept=\"image/*\" @change=\"inputChange\">\n          <span>拖入文件或</span><label for=\"file-elem\">点击选择文件</label>\n        </div>\n      </div>\n      <div class=\"column\">\n        <div>\n          <h3>设置参数</h3>\n          <p>\n            <span>压缩率：</span>\n            <input type=\"number\" max=\"1\" min=\"0.05\" step=\"0.05\" v-model=\"quality\">\n          </p>\n          <p>\n            <span>水印：</span>\n            <input type=\"text\" v-model=\"watermarkText\">\n          </p>\n          <p>\n            <button @click=\"submit\">{{btnText}}</button>\n          </p>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"column origin\">\n        <img :src=\"originImgUrl\" />\n      </div>\n      <div class=\"column\">\n        <h3>源图片信息</h3>\n        <div>\n          <span>mime类型：{{originMimeType}}</span>\n        </div>\n        <div>\n          <span>尺寸：{{originImgWidth}} x {{originImgHeight}}</span>\n        </div>\n        <div>\n          <span>大小：{{originSize}}</span>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"column output\">\n        <img :src=\"outputImgUrl\" />\n      </div>\n      <div class=\"column\">\n        <h3>压缩图片信息</h3>\n        <div>\n          <span>mime类型：{{outputMimeType}}</span>\n        </div>\n        <div>\n          <span>尺寸：{{outputImgWidth}} x {{outputImgHeight}}</span>\n        </div>\n        <div>\n          <span>大小：{{outputSize}}</span>\n        </div>\n        <div>\n          <span>实际压缩率：{{compressRatio}}</span>\n        </div>\n        <div>\n          <a :href=\"outputImgUrl\" :download=\"imgName\">下载</a>\n        </div>\n      </div>\n    </div>\n  </div>\n  <script src=\"../vue.min.js\"></script>\n  <script src=\"../../dist/image-compressor.js\"></script>\n  <script src=\"./main.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "demo/watermark/main.js",
    "content": "var BTN_OK = '确定';\n\nnew Vue({\n  el: '#app',\n  data: function () {\n    return {\n      file: null,\n      btnText: BTN_OK,\n      imgName: '',\n      originImgUrl: '',\n      originMimeType: 'auto',\n      originSize: 0,\n      originImgWidth: 'auto',\n      originImgHeight: 'auto',\n      outputImgUrl: '',\n      outputMimeType: 'auto',\n      outputImgWidth: 'auto',\n      outputImgHeight: 'auto',\n      outputSize: 0,\n      compressRatio: 0,\n      quality: 0.6,\n      watermarkText: 'watermark'\n    }\n  },\n\n  methods: {\n    /**\n     * 拖拽文件进入元素\n     * @param {Event} ev 事件\n     */\n    dragFileEnter: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n    },\n\n    /**\n     * 拖拽文件在元素上\n     * @param {Event} ev 事件\n     */\n    drageFileOver: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n    },\n\n    /**\n     * 放开文件拖拽\n     * @param {Event} ev 事件\n     */\n    dropFile: function (ev) {\n      ev.preventDefault();\n      ev.stopPropagation();\n\n      var dt = ev.dataTransfer;\n      var file = dt.files[0];\n      this.file = file;\n      this.compressImage(file);\n    },\n\n    /**\n     * 上传文件改变事件\n     * @param {Event} ev 事件\n     */\n    inputChange: function (ev) {\n      var file = ev.target.files[0];\n      this.file = file;\n      this.compressImage(file);\n    },\n\n    /**\n     * 确定提交\n     */\n    submit: function () {\n      this.compressImage(this.file);\n    },\n\n    /**\n     * 压缩图片\n     * @param {File} file `File` 对象\n     */\n    compressImage: function (file) {\n      var vm = this;\n      vm.btnText = '读取中...';\n      var options = {\n        file: file,\n        quality: this.quality,\n\n        // 压缩前回调\n        beforeCompress: function (result) {\n          vm.btnText = '处理中...';\n          vm.imgName = result.name;\n          vm.originImgWidth = result.width;\n          vm.originImgHeight = result.height;\n          vm.originSize = result.size;\n          vm.originMimeType = result.type;\n          console.log('压缩之前图片尺寸大小: ', result.size);\n          console.log('mime 类型: ', result.type);\n          // 将上传图片在页面预览\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.originImgUrl = url;\n          })\n        },\n\n        // 图片绘画后\n        afterDraw: function (ctx, canvas) {\n          ctx.restore();\n          vm.btnText = '绘图完成...';\n          console.log('绘图完成...');\n          ctx.fillStyle = '#fff';\n          ctx.font = (canvas.width * 0.1) + 'px microsoft yahei';\n          ctx.fillText(vm.watermarkText, 10, canvas.height - 20);\n        },\n\n        // 压缩成功回调\n        success: function (result) {\n          vm.btnText = BTN_OK;\n          console.log('result: ', result)\n          console.log('压缩之后图片尺寸大小: ', result.size);\n          console.log('mime 类型: ', result.type);\n          console.log('实际压缩率： ', ((file.size - result.size) / file.size * 100).toFixed(2) + '%');\n\n          vm.outputImgWidth = result.width;\n          vm.outputImgHeight = result.height;\n          vm.outputSize = result.size;\n          vm.outputMimeType = result.type;\n          vm.compressRatio = ((file.size - result.size) / file.size * 100).toFixed(2) + '%';\n\n          // 生成压缩后图片在页面展示\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.outputImgUrl = url;\n          })\n          // 上传到远程服务器\n          // util.upload('/upload.png', result);\n        }\n      };\n\n      new ImageCompressor(options);\n    }\n  }\n})"
  },
  {
    "path": "dist/image-compressor.js",
    "content": "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ImageCompressor\"] = factory();\n\telse\n\t\troot[\"ImageCompressor\"] = factory();\n})(window, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\nvar WIN = window\nvar REGEXP_IMAGE_TYPE = /^image\\//\nvar REGEXP_EXTENSION = /\\.\\w+$/\nvar util = {}\nvar defaultOptions = {\n  file: null,\n  quality: 0.8,\n  convertSize: 2048000,\n  loose: true,\n  redressOrientation: true,\n}\n\n/**\n * 判断是否为函数\n * @param {Any} value 任意值\n * @returns {Boolean} 判断结果\n */\nvar isFunc = function (value) {\n  return typeof value === 'function'\n}\n\n/**\n * 判断是否为图片类型\n * @param {String} value 类型字符串\n * @returns {Boolean} 判断结果\n */\nvar isImageType = function (value) {\n  return REGEXP_IMAGE_TYPE.test(value)\n}\n\n/**\n * 图片类型转化为文件拓展名\n * @param {String} value\n */\nvar imageTypeToExtension = function (value) {\n  var extension = isImageType(value) ? value.substr(6) : ''\n  if (extension === 'jpeg') {\n    extension = 'jpg'\n  }\n  return '.' + extension\n}\n\n/**\n * 图片压缩构造函数\n * @param {Object} options 相关参数\n */\nfunction ImageCompressor(options) {\n  options = Object.assign({}, defaultOptions, options)\n  this.options = options\n  this.file = options.file\n  this.image = null\n  this.ParsedOrientationInfo = null\n  this.init()\n}\n\nvar _proto = ImageCompressor.prototype\n// WIN.ImageCompressor = ImageCompressor;\n/* harmony default export */ __webpack_exports__[\"default\"] = (ImageCompressor);\n\n/**\n * 初始化\n */\n_proto.init = function () {\n  var _this = this\n  var file = this.file\n  var options = this.options\n\n  if (!file || !isImageType(file.type)) {\n    _this.error('请上传图片文件!')\n    return\n  }\n\n  if (!isImageType(options.mimeType)) {\n    options.mimeType = file.type\n  }\n\n  util.file2Image(\n    file,\n    function (img) {\n      if (isFunc(_this.beforeCompress)) {\n        _this.image = img\n        file.width = img.naturalWidth\n        file.height = img.naturalHeight\n        _this.beforeCompress(file)\n      }\n\n      if (file.type === 'image/jpeg' && options.redressOrientation) {\n        _this.getParsedOrientationInfo(function (info) {\n          _this.parsedOrientationInfo = info\n          _this.rendCanvas()\n        })\n      } else {\n        _this.parsedOrientationInfo = {\n          rotate: 0,\n          scaleX: 1,\n          scaleY: 1,\n        }\n        _this.rendCanvas()\n      }\n    },\n    _this.error\n  )\n}\n\n/**\n * `Canvas` 渲染模块\n */\n_proto.rendCanvas = function () {\n  var _this = this\n  var options = this.options\n  var image = this.image\n  var edge = this.getExpectedEdge()\n  var dWidth = edge.dWidth\n  var dHeight = edge.dHeight\n  var width = edge.width\n  var height = edge.height\n\n  var canvas = util.image2Canvas(\n    image,\n    dWidth,\n    dHeight,\n    _this.beforeDraw.bind(_this),\n    _this.afterDraw.bind(_this),\n    width,\n    height\n  )\n\n  util.canvas2Blob(\n    canvas,\n    function (blob) {\n      if (blob) {\n        blob.width = canvas.width\n        blob.height = canvas.height\n      }\n      _this.success(blob)\n    },\n    options.quality,\n    options.mimeType\n  )\n}\n\n/**\n * 压缩之前，读取图片之后钩子函数\n */\n_proto.beforeCompress = function () {\n  if (isFunc(this.options.beforeCompress)) {\n    this.options.beforeCompress(this.file)\n  }\n}\n\n/**\n * 获取用户想要输出的边（宽高）\n */\n_proto.getExpectedEdge = function () {\n  var image = this.image\n  var parsedOrientationInfo = this.parsedOrientationInfo\n  var rotate = parsedOrientationInfo.rotate\n  var options = this.options\n  var naturalWidth = image.naturalWidth\n  var naturalHeight = image.naturalHeight\n\n  var is90DegreesRotated = Math.abs(rotate) % 180 === 90\n  var temp\n\n  if (is90DegreesRotated) {\n    temp = naturalHeight\n    naturalHeight = naturalWidth\n    naturalWidth = temp\n  }\n\n  var aspectRatio = naturalWidth / naturalHeight\n  var maxWidth = Math.max(options.maxWidth, 0) || Infinity\n  var maxHeight = Math.max(options.maxHeight, 0) || Infinity\n  var minWidth = Math.max(options.minWidth, 0) || 0\n  var minHeight = Math.max(options.minHeight, 0) || 0\n  var width = Math.max(options.width, 0) || naturalWidth\n  var height = Math.max(options.height, 0) || naturalHeight\n\n  if (maxWidth < Infinity && maxHeight < Infinity) {\n    if (maxHeight * aspectRatio > maxWidth) {\n      maxHeight = maxWidth / aspectRatio\n    } else {\n      maxWidth = maxHeight * aspectRatio\n    }\n  } else if (maxWidth < Infinity) {\n    maxHeight = maxWidth / aspectRatio\n  } else if (maxHeight < Infinity) {\n    maxWidth = maxHeight * aspectRatio\n  }\n\n  if (minWidth > 0 && minHeight > 0) {\n    if (minHeight * aspectRatio > minWidth) {\n      minHeight = minWidth / aspectRatio\n    } else {\n      minWidth = minHeight * aspectRatio\n    }\n  } else if (minWidth > 0) {\n    minHeight = minWidth / aspectRatio\n  } else if (minHeight > 0) {\n    minWidth = minHeight * aspectRatio\n  }\n\n  if (height * aspectRatio > width) {\n    height = width / aspectRatio\n  } else {\n    width = height * aspectRatio\n  }\n\n  width = Math.floor(Math.min(Math.max(width, minWidth), maxWidth))\n  height = Math.floor(Math.min(Math.max(height, minHeight), maxHeight))\n\n  var dWidth = width\n  var dHeight = height\n\n  if (is90DegreesRotated) {\n    temp = dHeight\n    dHeight = dWidth\n    dWidth = temp\n  }\n\n  return {\n    dWidth: dWidth,\n    dHeight: dHeight,\n    width: width,\n    height: height,\n  }\n}\n\n/**\n * 获取转化后的方向信息\n * @param {Function} callback 回调函数\n */\n_proto.getParsedOrientationInfo = function (callback) {\n  var _this = this\n  this.getOrientation(function (orientation) {\n    if (isFunc(callback)) {\n      callback(_this.parseOrientation(orientation))\n    }\n  })\n}\n\n/**\n * 获取方向\n * @param {Function} callback 回调函数\n */\n_proto.getOrientation = function (callback) {\n  var _this = this\n  util.file2ArrayBuffer(this.file, function (result) {\n    if (isFunc(callback)) {\n      callback(_this.resetAndGetOrientation(result))\n    }\n  })\n}\n\n/**\n * 从 `array buffer` 提取 `orientation` 值\n * @param {ArrayBuffer} arrayBuffer - array buffer\n * @returns {number} `orientation` 值\n */\n_proto.resetAndGetOrientation = function (arrayBuffer) {\n  var dataView = new DataView(arrayBuffer)\n  var orientation\n\n  // 当图像没有正确的 Exif 信息时忽略 range error\n  try {\n    var littleEndian\n    var app1Start\n    var ifdStart\n\n    // JPEG 图片 (以 0xFFD8 开头)\n    if (dataView.getUint8(0) === 0xff && dataView.getUint8(1) === 0xd8) {\n      var length = dataView.byteLength\n      var offset = 2\n\n      while (offset + 1 < length) {\n        if (\n          dataView.getUint8(offset) === 0xff &&\n          dataView.getUint8(offset + 1) === 0xe1\n        ) {\n          app1Start = offset\n          break\n        }\n\n        offset += 1\n      }\n    }\n\n    if (app1Start) {\n      var exifIDCode = app1Start + 4\n      var tiffOffset = app1Start + 10\n\n      if (util.getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n        var endianness = dataView.getUint16(tiffOffset)\n\n        littleEndian = endianness === 0x4949\n\n        if (littleEndian || endianness === 0x4d4d) {\n          if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002a) {\n            var firstIFDOffset = dataView.getUint32(\n              tiffOffset + 4,\n              littleEndian\n            )\n\n            if (firstIFDOffset >= 0x00000008) {\n              ifdStart = tiffOffset + firstIFDOffset\n            }\n          }\n        }\n      }\n    }\n\n    if (ifdStart) {\n      var length = dataView.getUint16(ifdStart, littleEndian)\n      var offset\n      var i\n\n      for (i = 0; i < length; i += 1) {\n        offset = ifdStart + i * 12 + 2\n\n        if (\n          dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */\n        ) {\n          // 8 是当前标签值的偏移\n          offset += 8\n\n          // 获取原始方向值\n          orientation = dataView.getUint16(offset, littleEndian)\n\n          // 以其默认值覆盖方向\n          dataView.setUint16(offset, 1, littleEndian)\n          break\n        }\n      }\n    }\n  } catch (e) {\n    console.error(e)\n    orientation = 1\n  }\n  return orientation\n}\n\n/**\n * 逆向转化Exif获取到图片的方向信息\n * @param {Number} orientation 方向标识\n * @returns {Object} 转化结果\n */\n_proto.parseOrientation = function (orientation) {\n  var rotate = 0\n  var scaleX = 1\n  var scaleY = 1\n\n  switch (orientation) {\n    // 水平翻转\n    case 2:\n      scaleX = -1\n      break\n    // 向左旋转180°\n    case 3:\n      rotate = -180\n      break\n    // 垂直翻转\n    case 4:\n      scaleY = -1\n      break\n    // 垂直翻转并且向右旋转90°\n    case 5:\n      rotate = 90\n      scaleY = -1\n      break\n    // 向右旋转90°\n    case 6:\n      rotate = 90\n      break\n    // 水平翻转并且向右旋转90°\n    case 7:\n      rotate = 90\n      scaleX = -1\n      break\n    // 向左旋转90°\n    case 8:\n      rotate = -90\n      break\n    default:\n      break\n  }\n\n  return {\n    rotate: rotate,\n    scaleX: scaleX,\n    scaleY: scaleY,\n  }\n}\n\n/**\n * 画布上绘制图片前的一些操作：设置画布一些样式，支持用户自定义\n * @param {CanvasRenderingContext2D} ctx Canvas 对象的上下文\n * @param {HTMLCanvasElement} canvas Canvas 对象\n */\n_proto.beforeDraw = function (ctx, canvas) {\n  var parsedOrientationInfo = this.parsedOrientationInfo\n  var rotate = parsedOrientationInfo.rotate\n  var scaleX = parsedOrientationInfo.scaleX\n  var scaleY = parsedOrientationInfo.scaleY\n  var file = this.file\n  var options = this.options\n  var fillStyle = 'transparent'\n  var width = canvas.width\n  var height = canvas.height\n\n  // `png` 格式图片大小超过 `convertSize`, 转化成 `jpeg` 格式\n  if (file.size > options.convertSize && options.mimeType === 'image/png') {\n    fillStyle = '#fff'\n    options.mimeType = 'image/jpeg'\n  }\n\n  // 覆盖默认的黑色填充色\n  ctx.fillStyle = fillStyle\n  ctx.fillRect(0, 0, width, height)\n\n  // 用户自定义画布样式\n  if (isFunc(options.beforeDraw)) {\n    options.beforeDraw.call(this, ctx, canvas)\n  }\n\n  ctx.save()\n\n  switch (rotate) {\n    case 90:\n      ctx.translate(width, 0)\n      break\n    case -90:\n      ctx.translate(0, height)\n      break\n    case -180:\n      ctx.translate(width, height)\n      break\n  }\n\n  ctx.rotate((rotate * Math.PI) / 180)\n  ctx.scale(scaleX, scaleY)\n}\n\n/**\n * 画布上绘制图片后的一些操作：支持用户自定义\n * @param {CanvasRenderingContext2D} ctx Canvas 对象的上下文\n * @param {HTMLCanvasElement} canvas Canvas 对象\n */\n_proto.afterDraw = function (ctx, canvas) {\n  var options = this.options\n  // 用户自定义画布样式\n  if (isFunc(options.afterDraw)) {\n    options.afterDraw.call(this, ctx, canvas)\n  }\n}\n\n/**\n * 错误触发函数\n * @param {String} msg 错误消息\n */\n_proto.error = function (msg) {\n  var options = this.options\n  if (isFunc(options.error)) {\n    options.error.call(this, msg)\n  } else {\n    throw new Error(msg)\n  }\n}\n\n/**\n * 成功触发函数\n * @param {File|Blob} result `Blob` 对象\n */\n_proto.success = function (result) {\n  var options = this.options\n  var file = this.file\n  var image = this.image\n  var edge = this.getExpectedEdge()\n  var naturalHeight = image.naturalHeight\n  var naturalWidth = image.naturalWidth\n\n  if (result && result.size) {\n    // 在非宽松模式下，用户期待的输出宽高没有大于源图片的宽高情况下，输出文件大小大于源文件，返回源文件\n    if (\n      !options.loose &&\n      result.size > file.size &&\n      !(edge.width > naturalWidth || edge.height > naturalHeight)\n    ) {\n      console.warn('当前设置的是非宽松模式，压缩结果大于源图片，输出源图片')\n      result = file\n    } else {\n      var date = new Date()\n\n      result.lastModified = date.getTime()\n      result.lastModifiedDate = date\n      result.name = file.name\n\n      // 文件 `name` 属性中的后缀转化成实际后缀\n      if (result.name && result.type !== file.type) {\n        result.name = result.name.replace(\n          REGEXP_EXTENSION,\n          imageTypeToExtension(result.type)\n        )\n      }\n    }\n  } else {\n    // 在某些情况下压缩后文件为 `null`，返回源文件\n    console.warn('图片压缩出了点意外，输出源图片')\n    result = file\n  }\n\n  if (isFunc(options.success)) {\n    options.success.call(this, result)\n  }\n}\n\n/**\n * 文件转化成 `data URL` 字符串\n * @param {File} file 文件对象\n * @param {Function} callback 成功回调函数\n * @param {Function} error 取消回调函数\n */\nutil.file2DataUrl = function (file, callback, error) {\n  var reader = new FileReader()\n  reader.onload = function () {\n    callback(reader.result)\n  }\n  reader.onerror = function () {\n    if (isFunc(error)) {\n      error('读取文件失败！')\n    }\n  }\n  reader.readAsDataURL(file)\n}\n\n/**\n * 文件转化成 `ArrayBuffer`\n * @param {File} file 文件对象\n * @param {Function} callback 成功回调函数\n * @param {Function} error 取消回调函数\n */\nutil.file2ArrayBuffer = function (file, callback, error) {\n  var reader = new FileReader()\n  reader.onload = function (ev) {\n    callback(ev.target.result)\n  }\n  reader.onerror = function () {\n    if (isFunc(error)) {\n      error('读取文件失败！')\n    }\n  }\n  reader.readAsArrayBuffer(file)\n}\n\n/**\n * 从 data view 中的字符代码获取字符字符串\n * @param {DataView} dataView - The data view for read.\n * @param {number} start - 起始位置\n * @param {number} length - 读取长度\n * @returns {string} 读取结果\n */\nutil.getStringFromCharCode = function (dataView, start, length) {\n  var str = ''\n  var i\n\n  length += start\n\n  for (i = start; i < length; i += 1) {\n    str += String.fromCharCode(dataView.getUint8(i))\n  }\n\n  return str\n}\n\n/**\n * 文件转化成 `Image` 对象\n * @param {File} file 文件对象\n * @param {Function} callback 成功回调函数\n * @param {Function} error 错误回调函数\n */\nutil.file2Image = function (file, callback, error) {\n  var image = new Image()\n  var URL = WIN.URL || WIN.webkitURL\n\n  if (\n    WIN.navigator &&\n    /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WIN.navigator.userAgent)\n  ) {\n    // 修复IOS上webkit内核浏览器抛出错误 `The operation is insecure` 问题\n    image.crossOrigin = 'anonymous'\n  }\n\n  image.alt = file.name\n  image.onerror = function () {\n    if (isFunc(error)) {\n      error('图片加载错误！')\n    }\n  }\n\n  if (URL) {\n    var url = URL.createObjectURL(file)\n    image.onload = function () {\n      callback(image)\n      URL.revokeObjectURL(url)\n    }\n    image.src = url\n  } else {\n    this.file2DataUrl(\n      file,\n      function (dataUrl) {\n        image.onload = function () {\n          callback(image)\n        }\n        image.src = dataUrl\n      },\n      error\n    )\n  }\n}\n\n/**\n * `url` 转化成 `Image` 对象\n * @param {File} url `url`\n * @param {Function} callback 成功回调函数\n * @param {Function} error 失败回调函数\n */\nutil.url2Image = function (url, callback, error) {\n  var image = new Image()\n  image.src = url\n  image.onload = function () {\n    callback(image)\n  }\n  image.onerror = function () {\n    if (isFunc(error)) {\n      error('图片加载错误！')\n    }\n  }\n}\n\n/**\n * `Image` 转化成 `Canvas` 对象\n * @param {File} image `Image` 对象\n * @param {Number} dWidth 目标宽度\n * @param {Number} dHeight 目标高度\n * @param {Function} beforeDraw 在图片绘画之前的回调函数\n * @param {Function} afterDraw 在图片绘画之后的回调函数\n * @param {Number} width 宽\n * @param {Number} height 高\n * @return {HTMLCanvasElement} `Canvas` 对象\n */\nutil.image2Canvas = function (\n  image,\n  dWidth,\n  dHeight,\n  beforeDraw,\n  afterDraw,\n  width,\n  height\n) {\n  var canvas = document.createElement('canvas')\n  var ctx = canvas.getContext('2d')\n  canvas.width = width || image.naturalWidth\n  canvas.height = height || image.naturalHeight\n  if (isFunc(beforeDraw)) {\n    beforeDraw(ctx, canvas)\n  }\n  ctx.save()\n  ctx.drawImage(image, 0, 0, dWidth, dHeight)\n  ctx.restore()\n  if (isFunc(afterDraw)) {\n    afterDraw(ctx, canvas)\n  }\n  return canvas\n}\n\n/**\n * `Canvas` 转化成 `data URL` 对象\n * @param {File} file  `Canvas` 对象\n * @param {Float} quality 输出质量比例\n * @return {String} `data URL` 字符串\n */\nutil.canvas2DataUrl = function (canvas, quality, type) {\n  return canvas.toDataURL(type || 'image/jpeg', quality)\n}\n\n/**\n * `data URL` 转化成 `Image` 对象\n * @param {File} dataUrl `data URL` 字符串\n * @param {Function} callback 成功回调函数\n * @param {Function} error 失败回调函数\n */\nutil.dataUrl2Image = function (dataUrl, callback, error) {\n  var image = new Image()\n  image.onload = function () {\n    callback(image)\n  }\n  image.error = function () {\n    if (isFunc(error)) {\n      error('图片加载错误！')\n    }\n  }\n  image.src = dataUrl\n}\n\n/**\n * `data URL` 转化成 `Blob` 对象\n * @param {File} dataUrl `data URL` 字符串\n * @param {String} type `mime`\n * @return {Blob} `Blob` 对象\n */\nutil.dataUrl2Blob = function (dataUrl, type) {\n  var data = dataUrl.split(',')[1]\n  var mimePattern = /^data:(.*?)(;base64)?,/\n  var mime = dataUrl.match(mimePattern)[1]\n  var binStr = atob(data)\n  var len = data.length\n  var arr = new Uint8Array(len)\n\n  for (var i = 0; i < len; i++) {\n    arr[i] = binStr.charCodeAt(i)\n  }\n  return new Blob([arr], { type: type || mime })\n}\n\n/**\n * `Blob` 对象转化成 `data URL`\n * @param {Blob} blob `Blob` 对象\n * @param {Function} callback 成功回调函数\n * @param {Function} error 失败回调函数\n */\nutil.blob2DataUrl = function (blob, callback, error) {\n  this.file2DataUrl(blob, callback, error)\n}\n\n/**\n * `Blob`对象 转化成 `Image` 对象\n * @param {Blob} blob `Blob` 对象\n * @param {Function} callback 成功回调函数\n * @param {Function} callback 失败回调函数\n */\nutil.blob2Image = function (blob, callback, error) {\n  this.file2Image(blob, callback, error)\n}\n\n/**\n * `Canvas` 对象转化成 `Blob` 对象\n * @param {HTMLCanvasElement} canvas `Canvas` 对象\n * @param {Function} callback 回调函数\n * @param {Float} quality 输出质量比例\n * @param {String} type `mime`\n */\nutil.canvas2Blob = function (canvas, callback, quality, type) {\n  var _this = this\n  if (!HTMLCanvasElement.prototype.toBlob) {\n    Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {\n      value: function (callback, type, quality) {\n        var dataUrl = this.toDataURL(type, quality)\n        callback(_this.dataUrl2Blob(dataUrl))\n      },\n    })\n  }\n  canvas.toBlob(\n    function (blob) {\n      callback(blob)\n    },\n    type || 'image/jpeg',\n    quality || 0.8\n  )\n}\n\n/**\n * 文件上传\n * @param {String} url 上传路径\n * @param {File} file 文件对象\n * @param {Function} callback 回调函数\n */\nutil.upload = function (url, file, callback) {\n  var xhr = new XMLHttpRequest()\n  var fd = new FormData()\n  fd.append('file', file)\n  xhr.onreadystatechange = function () {\n    if (xhr.readyState === 4 && xhr.status === 200) {\n      // 上传成功\n      callback && callback(xhr.responseText)\n    } else {\n      throw new Error(xhr)\n    }\n  }\n  xhr.open('POST', url, true)\n  xhr.send(fd)\n}\n\nfor (var key in util) {\n  if (util.hasOwnProperty(key)) {\n    ImageCompressor[key] = util[key]\n  }\n}\n\n\n/***/ })\n/******/ ])[\"default\"];\n});"
  },
  {
    "path": "examples/default/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>js-image-compressor</title>\n    <link rel=\"stylesheet\" href=\"../index.css\" />\n  </head>\n\n  <body>\n    <div id=\"app\">\n      <div class=\"row\">\n        <div class=\"column upload\">\n          <div\n            class=\"drop-content\"\n            @drop=\"dropFile\"\n            @dragenter=\"dragFileEnter\"\n            @dragover=\"drageFileOver\"\n          >\n            <input\n              id=\"file-elem\"\n              type=\"file\"\n              accept=\"image/*\"\n              @change=\"inputChange\"\n            />\n            <span>拖入文件或</span><label for=\"file-elem\">点击选择文件</label>\n          </div>\n        </div>\n        <div class=\"column\">\n          <div>\n            <h3>设置参数</h3>\n            <p>\n              <span>mime 类型：</span>\n              <select v-model=\"mimeType\">\n                <option value=\"auto\">auto</option>\n                <option value=\"image/png\">image/png</option>\n                <option value=\"image/jpeg\">image/jpeg</option>\n                <option value=\"image/webp\">image/webp</option>\n                <option value=\"image/gif\">image/gif</option>\n              </select>\n            </p>\n            <p>\n              <span>maxWidth：</span>\n              <input type=\"text\" v-model=\"maxWidth\" />\n            </p>\n            <p>\n              <span>maxHeight：</span>\n              <input type=\"text\" v-model=\"maxHeight\" />\n            </p>\n            <p>\n              <span>width：</span>\n              <input type=\"text\" v-model=\"width\" />\n            </p>\n            <p>\n              <span>height：</span>\n              <input type=\"text\" v-model=\"height\" />\n            </p>\n            <p>\n              <span>minWidth：</span>\n              <input type=\"text\" v-model=\"minWidth\" />\n            </p>\n            <p>\n              <span>minHeigth：</span>\n              <input type=\"text\" v-model=\"minHeight\" />\n            </p>\n            <p>\n              <span>png转jpeg阈值：</span>\n              <select v-model=\"convertSize\">\n                <option :value=\"1024000\">1mb</option>\n                <option :value=\"2048000\">2mb</option>\n                <option :value=\"5120000\">5mb</option>\n                <option :value=\"10240000\">10mb</option>\n                <option :value=\"Infinity\">Infinity</option>\n              </select>\n            </p>\n            <p>\n              <span>宽松模式：</span>\n              <select v-model=\"loose\">\n                <option :value=\"true\">是</option>\n                <option :value=\"false\">否</option>\n              </select>\n            </p>\n            <p>\n              <span>矫正jpeg方向：</span>\n              <select v-model=\"redressOrientation\">\n                <option :value=\"true\">是</option>\n                <option :value=\"false\">否</option>\n              </select>\n            </p>\n            <p>\n              <span>压缩率：</span>\n              <input\n                type=\"number\"\n                max=\"1\"\n                min=\"0.05\"\n                step=\"0.05\"\n                v-model=\"quality\"\n              />\n            </p>\n            <p>\n              <button @click=\"submit\">{{btnText}}</button>\n            </p>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"column origin\">\n          <img :src=\"originImgUrl\" />\n        </div>\n        <div class=\"column\">\n          <h3>源图片信息</h3>\n          <div>\n            <span>mime类型：{{originMimeType}}</span>\n          </div>\n          <div>\n            <span>尺寸：{{originImgWidth}} x {{originImgHeight}}</span>\n          </div>\n          <div>\n            <span>大小：{{originSize}}</span>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"column output\">\n          <img :src=\"outputImgUrl\" />\n        </div>\n        <div class=\"column\">\n          <h3>压缩图片信息</h3>\n          <div>\n            <span>mime类型：{{outputMimeType}}</span>\n          </div>\n          <div>\n            <span>尺寸：{{outputImgWidth}} x {{outputImgHeight}}</span>\n          </div>\n          <div>\n            <span>大小：{{outputSize}}</span>\n          </div>\n          <div>\n            <span>实际压缩率：{{compressRatio}}</span>\n          </div>\n          <div>\n            <a :href=\"outputImgUrl\" :download=\"imgName\">下载</a>\n          </div>\n        </div>\n      </div>\n    </div>\n    <script src=\"../vue.min.js\"></script>\n    <script src=\"../../dist/image-compressor.js\"></script>\n    <script src=\"./main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/default/main.js",
    "content": "var BTN_OK = '确定'\n\nnew Vue({\n  el: '#app',\n  data: function () {\n    return {\n      file: null,\n      btnText: BTN_OK,\n      imgName: '',\n      mimeType: 'auto',\n      originImgUrl: '',\n      originMimeType: 'auto',\n      originSize: 0,\n      originImgWidth: 'auto',\n      originImgHeight: 'auto',\n      outputImgUrl: '',\n      outputMimeType: 'auto',\n      outputImgWidth: 'auto',\n      outputImgHeight: 'auto',\n      outputSize: 0,\n      compressRatio: 0,\n      quality: 0.6,\n      maxWidth: 0,\n      maxHeight: 0,\n      width: 0,\n      height: 0,\n      minWidth: 0,\n      minHeight: 0,\n      convertSize: Infinity,\n      loose: true,\n      redressOrientation: true,\n    }\n  },\n\n  methods: {\n    /**\n     * 拖拽文件进入元素\n     * @param {Event} ev 事件\n     */\n    dragFileEnter: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n    },\n\n    /**\n     * 拖拽文件在元素上\n     * @param {Event} ev 事件\n     */\n    drageFileOver: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n    },\n\n    /**\n     * 放开文件拖拽\n     * @param {Event} ev 事件\n     */\n    dropFile: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n\n      var dt = ev.dataTransfer\n      var file = dt.files[0]\n      this.file = file\n      this.compressImage(file)\n    },\n\n    /**\n     * 上传文件改变事件\n     * @param {Event} ev 事件\n     */\n    inputChange: function (ev) {\n      var file = ev.target.files[0]\n      this.file = file\n      this.compressImage(file)\n    },\n\n    /**\n     * 确定提交\n     */\n    submit: function () {\n      this.compressImage(this.file)\n    },\n\n    /**\n     * 压缩图片\n     * @param {File} file `File` 对象\n     */\n    compressImage: function (file) {\n      var vm = this\n      vm.btnText = '读取中...'\n      var options = {\n        file: file,\n        quality: this.quality,\n        mimeType: this.mimeType,\n        maxWidth: this.maxWidth,\n        maxHeight: this.maxHeight,\n        width: this.width,\n        height: this.height,\n        minWidth: this.minWidth,\n        minHeight: this.minHeight,\n        convertSize: this.convertSize,\n        loose: this.loose,\n        redressOrientation: this.redressOrientation,\n\n        // 压缩前回调\n        beforeCompress: function (result) {\n          vm.btnText = '处理中...'\n          vm.originImgWidth = result.width\n          vm.originImgHeight = result.height\n          vm.originSize = result.size\n          vm.originMimeType = result.type\n          console.log('压缩之前图片尺寸大小: ', result.size)\n          console.log('mime 类型: ', result.type)\n          // 将上传图片在页面预览\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.originImgUrl = url\n          })\n        },\n\n        // 压缩成功回调\n        success: function (result) {\n          vm.btnText = BTN_OK\n          vm.imgName = result.name\n          console.log('result: ', result)\n          console.log('压缩之后图片尺寸大小: ', result.size)\n          console.log('mime 类型: ', result.type)\n          console.log(\n            '实际压缩率： ',\n            (((file.size - result.size) / file.size) * 100).toFixed(2) + '%'\n          )\n\n          vm.outputImgWidth = result.width\n          vm.outputImgHeight = result.height\n          vm.outputSize = result.size\n          vm.outputMimeType = result.type\n          vm.compressRatio =\n            (((file.size - result.size) / file.size) * 100).toFixed(2) + '%'\n\n          // 生成压缩后图片在页面展示\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.outputImgUrl = url\n          })\n          // 上传到远程服务器\n          // util.upload('/upload.png', result);\n        },\n\n        // 发生错误\n        error: function (msg) {\n          vm.btnText = BTN_OK\n          console.error(msg)\n        },\n      }\n\n      new ImageCompressor(options)\n    },\n  },\n})\n"
  },
  {
    "path": "examples/grayscale/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>grayscale demo</title>\n    <link rel=\"stylesheet\" href=\"../index.css\" />\n  </head>\n\n  <body>\n    <div id=\"app\">\n      <div class=\"row\">\n        <div class=\"column upload\">\n          <div\n            class=\"drop-content\"\n            @drop=\"dropFile\"\n            @dragenter=\"dragFileEnter\"\n            @dragover=\"drageFileOver\"\n          >\n            <input\n              id=\"file-elem\"\n              type=\"file\"\n              accept=\"image/*\"\n              @change=\"inputChange\"\n            />\n            <span>拖入文件或</span><label for=\"file-elem\">点击选择文件</label>\n          </div>\n        </div>\n        <div class=\"column\">\n          <div>\n            <h3>设置参数</h3>\n            <p>\n              <span>压缩率：</span>\n              <input\n                type=\"number\"\n                max=\"1\"\n                min=\"0.05\"\n                step=\"0.05\"\n                v-model=\"quality\"\n              />\n            </p>\n            <p>\n              <button @click=\"submit\">{{btnText}}</button>\n            </p>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"column origin\">\n          <img :src=\"originImgUrl\" />\n        </div>\n        <div class=\"column\">\n          <h3>源图片信息</h3>\n          <div>\n            <span>mime类型：{{originMimeType}}</span>\n          </div>\n          <div>\n            <span>尺寸：{{originImgWidth}} x {{originImgHeight}}</span>\n          </div>\n          <div>\n            <span>大小：{{originSize}}</span>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"column output\">\n          <img :src=\"outputImgUrl\" />\n        </div>\n        <div class=\"column\">\n          <h3>压缩图片信息</h3>\n          <div>\n            <span>mime类型：{{outputMimeType}}</span>\n          </div>\n          <div>\n            <span>尺寸：{{outputImgWidth}} x {{outputImgHeight}}</span>\n          </div>\n          <div>\n            <span>大小：{{outputSize}}</span>\n          </div>\n          <div>\n            <span>实际压缩率：{{compressRatio}}</span>\n          </div>\n          <div>\n            <a :href=\"outputImgUrl\" :download=\"imgName\">下载</a>\n          </div>\n        </div>\n      </div>\n    </div>\n    <script src=\"../vue.min.js\"></script>\n    <script src=\"../../dist/image-compressor.js\"></script>\n    <script src=\"./main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/grayscale/main.js",
    "content": "var BTN_OK = '确定'\n\nnew Vue({\n  el: '#app',\n  data: function () {\n    return {\n      file: null,\n      btnText: BTN_OK,\n      imgName: '',\n      originImgUrl: '',\n      originMimeType: 'auto',\n      originSize: 0,\n      originImgWidth: 'auto',\n      originImgHeight: 'auto',\n      outputImgUrl: '',\n      outputMimeType: 'auto',\n      outputImgWidth: 'auto',\n      outputImgHeight: 'auto',\n      outputSize: 0,\n      compressRatio: 0,\n      quality: 0.6,\n    }\n  },\n\n  methods: {\n    /**\n     * 拖拽文件进入元素\n     * @param {Event} ev 事件\n     */\n    dragFileEnter: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n    },\n\n    /**\n     * 拖拽文件在元素上\n     * @param {Event} ev 事件\n     */\n    drageFileOver: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n    },\n\n    /**\n     * 放开文件拖拽\n     * @param {Event} ev 事件\n     */\n    dropFile: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n\n      var dt = ev.dataTransfer\n      var file = dt.files[0]\n      this.file = file\n      this.compressImage(file)\n    },\n\n    /**\n     * 上传文件改变事件\n     * @param {Event} ev 事件\n     */\n    inputChange: function (ev) {\n      var file = ev.target.files[0]\n      this.file = file\n      this.compressImage(file)\n    },\n\n    /**\n     * 确定提交\n     */\n    submit: function () {\n      this.compressImage(this.file)\n    },\n\n    /**\n     * 压缩图片\n     * @param {File} file `File` 对象\n     */\n    compressImage: function (file) {\n      var vm = this\n      vm.btnText = '读取中...'\n      var options = {\n        file: file,\n        quality: this.quality,\n\n        // 压缩前回调\n        beforeCompress: function (result) {\n          vm.btnText = '处理中...'\n          vm.imgName = result.name\n          vm.originImgWidth = result.width\n          vm.originImgHeight = result.height\n          vm.originSize = result.size\n          vm.originMimeType = result.type\n          console.log('压缩之前图片尺寸大小: ', result.size)\n          console.log('mime 类型: ', result.type)\n          // 将上传图片在页面预览\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.originImgUrl = url\n          })\n        },\n\n        // 图片绘画前\n        beforeDraw: function (ctx) {\n          vm.btnText = '准备绘图...'\n          console.log('准备绘图...')\n          ctx.filter = 'grayscale(100%)'\n        },\n\n        // 压缩成功回调\n        success: function (result) {\n          vm.btnText = BTN_OK\n          console.log('result: ', result)\n          console.log('压缩之后图片尺寸大小: ', result.size)\n          console.log('mime 类型: ', result.type)\n          console.log(\n            '实际压缩率： ',\n            (((file.size - result.size) / file.size) * 100).toFixed(2) + '%'\n          )\n\n          vm.outputImgWidth = result.width\n          vm.outputImgHeight = result.height\n          vm.outputSize = result.size\n          vm.outputMimeType = result.type\n          vm.compressRatio =\n            (((file.size - result.size) / file.size) * 100).toFixed(2) + '%'\n\n          // 生成压缩后图片在页面展示\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.outputImgUrl = url\n          })\n          // 上传到远程服务器\n          // util.upload('/upload.png', result);\n        },\n      }\n\n      new ImageCompressor(options)\n    },\n  },\n})\n"
  },
  {
    "path": "examples/index.css",
    "content": ".row {\n  display: flex;\n  border-bottom: 1px dashed #999;\n}\n\n.column {\n  padding: 20px;\n}\n\n.upload,\n.origin,\n.output {\n  width: 300px;\n  max-height: 300px;\n  overflow: hidden;\n  text-align: center;\n}\n\n.origin img,\n.output img {\n  max-width: 100%;\n  max-height: 300px;\n  vertical-align: middle;\n}\n\n.drop-content {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border: 1px dotted #999;\n  border-radius: 4px;\n  min-height: 100px;\n  height: 100%;\n}\n\n.drop-content label {\n  color: #3db8fd;\n}\n\n.drop-content label:hover {\n  cursor: pointer;\n}\n\n#file-elem {\n  position: absolute !important;\n  height: 1px;\n  width: 1px;\n  overflow: hidden;\n  clip: rect(1px, 1px, 1px, 1px);\n}\n\n#file-elem:focus+label {\n  outline: thin dotted;\n}\n\n#file-elem:focus-within+label {\n  outline: thin dotted;\n}"
  },
  {
    "path": "examples/simple/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>simple demo</title>\n    <link rel=\"stylesheet\" href=\"../index.css\" />\n  </head>\n\n  <body>\n    <div id=\"app\">\n      <div class=\"row\">\n        <div class=\"column upload\">\n          <div\n            class=\"drop-content\"\n            @drop=\"dropFile\"\n            @dragenter=\"dragFileEnter\"\n            @dragover=\"drageFileOver\"\n          >\n            <input\n              id=\"file-elem\"\n              type=\"file\"\n              accept=\"image/*\"\n              @change=\"inputChange\"\n            />\n            <span>拖入文件或</span><label for=\"file-elem\">点击选择文件</label>\n          </div>\n        </div>\n        <div class=\"column\">\n          <div>\n            <h3>设置参数</h3>\n            <p>\n              <span>压缩率：</span>\n              <input\n                type=\"number\"\n                max=\"1\"\n                min=\"0.05\"\n                step=\"0.05\"\n                v-model=\"quality\"\n              />\n            </p>\n            <p>\n              <button @click=\"submit\">{{btnText}}</button>\n            </p>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"column origin\">\n          <img :src=\"originImgUrl\" />\n        </div>\n        <div class=\"column\">\n          <h3>源图片信息</h3>\n          <div>\n            <span>mime类型：{{originMimeType}}</span>\n          </div>\n          <div>\n            <span>尺寸：{{originImgWidth}} x {{originImgHeight}}</span>\n          </div>\n          <div>\n            <span>大小：{{originSize}}</span>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"column output\">\n          <img :src=\"outputImgUrl\" />\n        </div>\n        <div class=\"column\">\n          <h3>压缩图片信息</h3>\n          <div>\n            <span>mime类型：{{outputMimeType}}</span>\n          </div>\n          <div>\n            <span>尺寸：{{outputImgWidth}} x {{outputImgHeight}}</span>\n          </div>\n          <div>\n            <span>大小：{{outputSize}}</span>\n          </div>\n          <div>\n            <span>实际压缩率：{{compressRatio}}</span>\n          </div>\n          <div>\n            <a :href=\"outputImgUrl\" :download=\"imgName\">下载</a>\n          </div>\n        </div>\n      </div>\n    </div>\n    <script src=\"../vue.min.js\"></script>\n    <script src=\"../../dist/image-compressor.js\"></script>\n    <script src=\"./main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/simple/main.js",
    "content": "var BTN_OK = '确定'\n\nnew Vue({\n  el: '#app',\n  data: function () {\n    return {\n      file: null,\n      btnText: BTN_OK,\n      imgName: '',\n      originImgUrl: '',\n      originMimeType: 'auto',\n      originSize: 0,\n      originImgWidth: 'auto',\n      originImgHeight: 'auto',\n      outputImgUrl: '',\n      outputMimeType: 'auto',\n      outputImgWidth: 'auto',\n      outputImgHeight: 'auto',\n      outputSize: 0,\n      compressRatio: 0,\n      quality: 0.6,\n    }\n  },\n\n  methods: {\n    /**\n     * 拖拽文件进入元素\n     * @param {Event} ev 事件\n     */\n    dragFileEnter: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n    },\n\n    /**\n     * 拖拽文件在元素上\n     * @param {Event} ev 事件\n     */\n    drageFileOver: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n    },\n\n    /**\n     * 放开文件拖拽\n     * @param {Event} ev 事件\n     */\n    dropFile: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n\n      var dt = ev.dataTransfer\n      var file = dt.files[0]\n      this.file = file\n      this.compressImage(file)\n    },\n\n    /**\n     * 上传文件改变事件\n     * @param {Event} ev 事件\n     */\n    inputChange: function (ev) {\n      var file = ev.target.files[0]\n      this.file = file\n      this.compressImage(file)\n    },\n\n    /**\n     * 确定提交\n     */\n    submit: function () {\n      this.compressImage(this.file)\n    },\n\n    /**\n     * 压缩图片\n     * @param {File} file `File` 对象\n     */\n    compressImage: function (file) {\n      var vm = this\n      vm.btnText = '读取中...'\n      var options = {\n        file: file,\n        quality: this.quality,\n\n        // 压缩前回调\n        beforeCompress: function (result) {\n          vm.btnText = '处理中...'\n          vm.imgName = result.name\n          vm.originImgWidth = result.width\n          vm.originImgHeight = result.height\n          vm.originSize = result.size\n          vm.originMimeType = result.type\n          console.log('压缩之前图片尺寸大小: ', result.size)\n          console.log('mime 类型: ', result.type)\n          // 将上传图片在页面预览\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.originImgUrl = url\n          })\n        },\n\n        // 压缩成功回调\n        success: function (result) {\n          vm.btnText = BTN_OK\n          console.log('result: ', result)\n          console.log('压缩之后图片尺寸大小: ', result.size)\n          console.log('mime 类型: ', result.type)\n          console.log(\n            '实际压缩率： ',\n            (((file.size - result.size) / file.size) * 100).toFixed(2) + '%'\n          )\n\n          vm.outputImgWidth = result.width\n          vm.outputImgHeight = result.height\n          vm.outputSize = result.size\n          vm.outputMimeType = result.type\n          vm.compressRatio =\n            (((file.size - result.size) / file.size) * 100).toFixed(2) + '%'\n\n          // 生成压缩后图片在页面展示\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.outputImgUrl = url\n          })\n          // 上传到远程服务器\n          // util.upload('/upload.png', result);\n        },\n      }\n\n      new ImageCompressor(options)\n    },\n  },\n})\n"
  },
  {
    "path": "examples/watermark/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>watermark demo</title>\n    <link rel=\"stylesheet\" href=\"../index.css\" />\n  </head>\n\n  <body>\n    <div id=\"app\">\n      <div class=\"row\">\n        <div class=\"column upload\">\n          <div\n            class=\"drop-content\"\n            @drop=\"dropFile\"\n            @dragenter=\"dragFileEnter\"\n            @dragover=\"drageFileOver\"\n          >\n            <input\n              id=\"file-elem\"\n              type=\"file\"\n              accept=\"image/*\"\n              @change=\"inputChange\"\n            />\n            <span>拖入文件或</span><label for=\"file-elem\">点击选择文件</label>\n          </div>\n        </div>\n        <div class=\"column\">\n          <div>\n            <h3>设置参数</h3>\n            <p>\n              <span>压缩率：</span>\n              <input\n                type=\"number\"\n                max=\"1\"\n                min=\"0.05\"\n                step=\"0.05\"\n                v-model=\"quality\"\n              />\n            </p>\n            <p>\n              <span>水印：</span>\n              <input type=\"text\" v-model=\"watermarkText\" />\n            </p>\n            <p>\n              <button @click=\"submit\">{{btnText}}</button>\n            </p>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"column origin\">\n          <img :src=\"originImgUrl\" />\n        </div>\n        <div class=\"column\">\n          <h3>源图片信息</h3>\n          <div>\n            <span>mime类型：{{originMimeType}}</span>\n          </div>\n          <div>\n            <span>尺寸：{{originImgWidth}} x {{originImgHeight}}</span>\n          </div>\n          <div>\n            <span>大小：{{originSize}}</span>\n          </div>\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"column output\">\n          <img :src=\"outputImgUrl\" />\n        </div>\n        <div class=\"column\">\n          <h3>压缩图片信息</h3>\n          <div>\n            <span>mime类型：{{outputMimeType}}</span>\n          </div>\n          <div>\n            <span>尺寸：{{outputImgWidth}} x {{outputImgHeight}}</span>\n          </div>\n          <div>\n            <span>大小：{{outputSize}}</span>\n          </div>\n          <div>\n            <span>实际压缩率：{{compressRatio}}</span>\n          </div>\n          <div>\n            <a :href=\"outputImgUrl\" :download=\"imgName\">下载</a>\n          </div>\n        </div>\n      </div>\n    </div>\n    <script src=\"../vue.min.js\"></script>\n    <script src=\"../../dist/image-compressor.js\"></script>\n    <script src=\"./main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/watermark/main.js",
    "content": "var BTN_OK = '确定'\n\nnew Vue({\n  el: '#app',\n  data: function () {\n    return {\n      file: null,\n      btnText: BTN_OK,\n      imgName: '',\n      originImgUrl: '',\n      originMimeType: 'auto',\n      originSize: 0,\n      originImgWidth: 'auto',\n      originImgHeight: 'auto',\n      outputImgUrl: '',\n      outputMimeType: 'auto',\n      outputImgWidth: 'auto',\n      outputImgHeight: 'auto',\n      outputSize: 0,\n      compressRatio: 0,\n      quality: 0.6,\n      watermarkText: 'watermark',\n    }\n  },\n\n  methods: {\n    /**\n     * 拖拽文件进入元素\n     * @param {Event} ev 事件\n     */\n    dragFileEnter: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n    },\n\n    /**\n     * 拖拽文件在元素上\n     * @param {Event} ev 事件\n     */\n    drageFileOver: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n    },\n\n    /**\n     * 放开文件拖拽\n     * @param {Event} ev 事件\n     */\n    dropFile: function (ev) {\n      ev.preventDefault()\n      ev.stopPropagation()\n\n      var dt = ev.dataTransfer\n      var file = dt.files[0]\n      this.file = file\n      this.compressImage(file)\n    },\n\n    /**\n     * 上传文件改变事件\n     * @param {Event} ev 事件\n     */\n    inputChange: function (ev) {\n      var file = ev.target.files[0]\n      this.file = file\n      this.compressImage(file)\n    },\n\n    /**\n     * 确定提交\n     */\n    submit: function () {\n      this.compressImage(this.file)\n    },\n\n    /**\n     * 压缩图片\n     * @param {File} file `File` 对象\n     */\n    compressImage: function (file) {\n      var vm = this\n      vm.btnText = '读取中...'\n      var options = {\n        file: file,\n        quality: this.quality,\n\n        // 压缩前回调\n        beforeCompress: function (result) {\n          vm.btnText = '处理中...'\n          vm.imgName = result.name\n          vm.originImgWidth = result.width\n          vm.originImgHeight = result.height\n          vm.originSize = result.size\n          vm.originMimeType = result.type\n          console.log('压缩之前图片尺寸大小: ', result.size)\n          console.log('mime 类型: ', result.type)\n          // 将上传图片在页面预览\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.originImgUrl = url\n          })\n        },\n\n        // 图片绘画后\n        afterDraw: function (ctx, canvas) {\n          ctx.restore()\n          vm.btnText = '绘图完成...'\n          console.log('绘图完成...')\n          ctx.fillStyle = '#fff'\n          ctx.font = canvas.width * 0.1 + 'px microsoft yahei'\n          ctx.fillText(vm.watermarkText, 10, canvas.height - 20)\n        },\n\n        // 压缩成功回调\n        success: function (result) {\n          vm.btnText = BTN_OK\n          console.log('result: ', result)\n          console.log('压缩之后图片尺寸大小: ', result.size)\n          console.log('mime 类型: ', result.type)\n          console.log(\n            '实际压缩率： ',\n            (((file.size - result.size) / file.size) * 100).toFixed(2) + '%'\n          )\n\n          vm.outputImgWidth = result.width\n          vm.outputImgHeight = result.height\n          vm.outputSize = result.size\n          vm.outputMimeType = result.type\n          vm.compressRatio =\n            (((file.size - result.size) / file.size) * 100).toFixed(2) + '%'\n\n          // 生成压缩后图片在页面展示\n          ImageCompressor.file2DataUrl(result, function (url) {\n            vm.outputImgUrl = url\n          })\n          // 上传到远程服务器\n          // util.upload('/upload.png', result);\n        },\n      }\n\n      new ImageCompressor(options)\n    },\n  },\n})\n"
  },
  {
    "path": "index.js",
    "content": "if (process.env.NODE_ENV === 'production') {\n  module.exports = require('./dist/image-compressor.min.js')\n} else {\n  module.exports = require('./dist/image-compressor.js')\n}"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"js-image-compressor\",\n  \"version\": \"2.0.0\",\n  \"description\": \"A simple image compressor of javascript\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"build\": \"webpack\",\n    \"prepublish\": \"webpack\",\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [\n    \"jsImageCompressor\",\n    \"imageToCanvas\",\n    \"toBlob\"\n  ],\n  \"author\": \"wuwhs\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"terser-webpack-plugin\": \"^3.0.8\",\n    \"webpack\": \"^4.44.0\",\n    \"webpack-cli\": \"^3.3.12\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/wuwhs/js-image-compressor\"\n  }\n}"
  },
  {
    "path": "src/index.js",
    "content": "var WIN = window\nvar REGEXP_IMAGE_TYPE = /^image\\//\nvar REGEXP_EXTENSION = /\\.\\w+$/\nvar util = {}\nvar defaultOptions = {\n  file: null,\n  quality: 0.8,\n  convertSize: 2048000,\n  loose: true,\n  redressOrientation: true,\n}\n\n/**\n * 判断是否为函数\n * @param {Any} value 任意值\n * @returns {Boolean} 判断结果\n */\nvar isFunc = function (value) {\n  return typeof value === 'function'\n}\n\n/**\n * 判断是否为图片类型\n * @param {String} value 类型字符串\n * @returns {Boolean} 判断结果\n */\nvar isImageType = function (value) {\n  return REGEXP_IMAGE_TYPE.test(value)\n}\n\n/**\n * 图片类型转化为文件拓展名\n * @param {String} value\n */\nvar imageTypeToExtension = function (value) {\n  var extension = isImageType(value) ? value.substr(6) : ''\n  if (extension === 'jpeg') {\n    extension = 'jpg'\n  }\n  return '.' + extension\n}\n\n/**\n * 图片压缩构造函数\n * @param {Object} options 相关参数\n */\nfunction ImageCompressor(options) {\n  options = Object.assign({}, defaultOptions, options)\n  this.options = options\n  this.file = options.file\n  this.image = null\n  this.ParsedOrientationInfo = null\n  this.init()\n}\n\nvar _proto = ImageCompressor.prototype\n// WIN.ImageCompressor = ImageCompressor;\nexport default ImageCompressor\n\n/**\n * 初始化\n */\n_proto.init = function () {\n  var _this = this\n  var file = this.file\n  var options = this.options\n\n  if (!file || !isImageType(file.type)) {\n    _this.error('请上传图片文件!')\n    return\n  }\n\n  if (!isImageType(options.mimeType)) {\n    options.mimeType = file.type\n  }\n\n  util.file2Image(\n    file,\n    function (img) {\n      if (isFunc(_this.beforeCompress)) {\n        _this.image = img\n        file.width = img.naturalWidth\n        file.height = img.naturalHeight\n        _this.beforeCompress(file)\n      }\n\n      if (file.type === 'image/jpeg' && options.redressOrientation) {\n        _this.getParsedOrientationInfo(function (info) {\n          _this.parsedOrientationInfo = info\n          _this.rendCanvas()\n        })\n      } else {\n        _this.parsedOrientationInfo = {\n          rotate: 0,\n          scaleX: 1,\n          scaleY: 1,\n        }\n        _this.rendCanvas()\n      }\n    },\n    _this.error\n  )\n}\n\n/**\n * `Canvas` 渲染模块\n */\n_proto.rendCanvas = function () {\n  var _this = this\n  var options = this.options\n  var image = this.image\n  var edge = this.getExpectedEdge()\n  var dWidth = edge.dWidth\n  var dHeight = edge.dHeight\n  var width = edge.width\n  var height = edge.height\n\n  var canvas = util.image2Canvas(\n    image,\n    dWidth,\n    dHeight,\n    _this.beforeDraw.bind(_this),\n    _this.afterDraw.bind(_this),\n    width,\n    height\n  )\n\n  util.canvas2Blob(\n    canvas,\n    function (blob) {\n      if (blob) {\n        blob.width = canvas.width\n        blob.height = canvas.height\n      }\n      _this.success(blob)\n    },\n    options.quality,\n    options.mimeType\n  )\n}\n\n/**\n * 压缩之前，读取图片之后钩子函数\n */\n_proto.beforeCompress = function () {\n  if (isFunc(this.options.beforeCompress)) {\n    this.options.beforeCompress(this.file)\n  }\n}\n\n/**\n * 获取用户想要输出的边（宽高）\n */\n_proto.getExpectedEdge = function () {\n  var image = this.image\n  var parsedOrientationInfo = this.parsedOrientationInfo\n  var rotate = parsedOrientationInfo.rotate\n  var options = this.options\n  var naturalWidth = image.naturalWidth\n  var naturalHeight = image.naturalHeight\n\n  var is90DegreesRotated = Math.abs(rotate) % 180 === 90\n  var temp\n\n  if (is90DegreesRotated) {\n    temp = naturalHeight\n    naturalHeight = naturalWidth\n    naturalWidth = temp\n  }\n\n  var aspectRatio = naturalWidth / naturalHeight\n  var maxWidth = Math.max(options.maxWidth, 0) || Infinity\n  var maxHeight = Math.max(options.maxHeight, 0) || Infinity\n  var minWidth = Math.max(options.minWidth, 0) || 0\n  var minHeight = Math.max(options.minHeight, 0) || 0\n  var width = Math.max(options.width, 0) || naturalWidth\n  var height = Math.max(options.height, 0) || naturalHeight\n\n  if (maxWidth < Infinity && maxHeight < Infinity) {\n    if (maxHeight * aspectRatio > maxWidth) {\n      maxHeight = maxWidth / aspectRatio\n    } else {\n      maxWidth = maxHeight * aspectRatio\n    }\n  } else if (maxWidth < Infinity) {\n    maxHeight = maxWidth / aspectRatio\n  } else if (maxHeight < Infinity) {\n    maxWidth = maxHeight * aspectRatio\n  }\n\n  if (minWidth > 0 && minHeight > 0) {\n    if (minHeight * aspectRatio > minWidth) {\n      minHeight = minWidth / aspectRatio\n    } else {\n      minWidth = minHeight * aspectRatio\n    }\n  } else if (minWidth > 0) {\n    minHeight = minWidth / aspectRatio\n  } else if (minHeight > 0) {\n    minWidth = minHeight * aspectRatio\n  }\n\n  if (height * aspectRatio > width) {\n    height = width / aspectRatio\n  } else {\n    width = height * aspectRatio\n  }\n\n  width = Math.floor(Math.min(Math.max(width, minWidth), maxWidth))\n  height = Math.floor(Math.min(Math.max(height, minHeight), maxHeight))\n\n  var dWidth = width\n  var dHeight = height\n\n  if (is90DegreesRotated) {\n    temp = dHeight\n    dHeight = dWidth\n    dWidth = temp\n  }\n\n  return {\n    dWidth: dWidth,\n    dHeight: dHeight,\n    width: width,\n    height: height,\n  }\n}\n\n/**\n * 获取转化后的方向信息\n * @param {Function} callback 回调函数\n */\n_proto.getParsedOrientationInfo = function (callback) {\n  var _this = this\n  this.getOrientation(function (orientation) {\n    if (isFunc(callback)) {\n      callback(_this.parseOrientation(orientation))\n    }\n  })\n}\n\n/**\n * 获取方向\n * @param {Function} callback 回调函数\n */\n_proto.getOrientation = function (callback) {\n  var _this = this\n  util.file2ArrayBuffer(this.file, function (result) {\n    if (isFunc(callback)) {\n      callback(_this.resetAndGetOrientation(result))\n    }\n  })\n}\n\n/**\n * 从 `array buffer` 提取 `orientation` 值\n * @param {ArrayBuffer} arrayBuffer - array buffer\n * @returns {number} `orientation` 值\n */\n_proto.resetAndGetOrientation = function (arrayBuffer) {\n  var dataView = new DataView(arrayBuffer)\n  var orientation\n\n  // 当图像没有正确的 Exif 信息时忽略 range error\n  try {\n    var littleEndian\n    var app1Start\n    var ifdStart\n\n    // JPEG 图片 (以 0xFFD8 开头)\n    if (dataView.getUint8(0) === 0xff && dataView.getUint8(1) === 0xd8) {\n      var length = dataView.byteLength\n      var offset = 2\n\n      while (offset + 1 < length) {\n        if (\n          dataView.getUint8(offset) === 0xff &&\n          dataView.getUint8(offset + 1) === 0xe1\n        ) {\n          app1Start = offset\n          break\n        }\n\n        offset += 1\n      }\n    }\n\n    if (app1Start) {\n      var exifIDCode = app1Start + 4\n      var tiffOffset = app1Start + 10\n\n      if (util.getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n        var endianness = dataView.getUint16(tiffOffset)\n\n        littleEndian = endianness === 0x4949\n\n        if (littleEndian || endianness === 0x4d4d) {\n          if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002a) {\n            var firstIFDOffset = dataView.getUint32(\n              tiffOffset + 4,\n              littleEndian\n            )\n\n            if (firstIFDOffset >= 0x00000008) {\n              ifdStart = tiffOffset + firstIFDOffset\n            }\n          }\n        }\n      }\n    }\n\n    if (ifdStart) {\n      var length = dataView.getUint16(ifdStart, littleEndian)\n      var offset\n      var i\n\n      for (i = 0; i < length; i += 1) {\n        offset = ifdStart + i * 12 + 2\n\n        if (\n          dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */\n        ) {\n          // 8 是当前标签值的偏移\n          offset += 8\n\n          // 获取原始方向值\n          orientation = dataView.getUint16(offset, littleEndian)\n\n          // 以其默认值覆盖方向\n          dataView.setUint16(offset, 1, littleEndian)\n          break\n        }\n      }\n    }\n  } catch (e) {\n    console.error(e)\n    orientation = 1\n  }\n  return orientation\n}\n\n/**\n * 逆向转化Exif获取到图片的方向信息\n * @param {Number} orientation 方向标识\n * @returns {Object} 转化结果\n */\n_proto.parseOrientation = function (orientation) {\n  var rotate = 0\n  var scaleX = 1\n  var scaleY = 1\n\n  switch (orientation) {\n    // 水平翻转\n    case 2:\n      scaleX = -1\n      break\n    // 向左旋转180°\n    case 3:\n      rotate = -180\n      break\n    // 垂直翻转\n    case 4:\n      scaleY = -1\n      break\n    // 垂直翻转并且向右旋转90°\n    case 5:\n      rotate = 90\n      scaleY = -1\n      break\n    // 向右旋转90°\n    case 6:\n      rotate = 90\n      break\n    // 水平翻转并且向右旋转90°\n    case 7:\n      rotate = 90\n      scaleX = -1\n      break\n    // 向左旋转90°\n    case 8:\n      rotate = -90\n      break\n    default:\n      break\n  }\n\n  return {\n    rotate: rotate,\n    scaleX: scaleX,\n    scaleY: scaleY,\n  }\n}\n\n/**\n * 画布上绘制图片前的一些操作：设置画布一些样式，支持用户自定义\n * @param {CanvasRenderingContext2D} ctx Canvas 对象的上下文\n * @param {HTMLCanvasElement} canvas Canvas 对象\n */\n_proto.beforeDraw = function (ctx, canvas) {\n  var parsedOrientationInfo = this.parsedOrientationInfo\n  var rotate = parsedOrientationInfo.rotate\n  var scaleX = parsedOrientationInfo.scaleX\n  var scaleY = parsedOrientationInfo.scaleY\n  var file = this.file\n  var options = this.options\n  var fillStyle = 'transparent'\n  var width = canvas.width\n  var height = canvas.height\n\n  // `png` 格式图片大小超过 `convertSize`, 转化成 `jpeg` 格式\n  if (file.size > options.convertSize && options.mimeType === 'image/png') {\n    fillStyle = '#fff'\n    options.mimeType = 'image/jpeg'\n  }\n\n  // 覆盖默认的黑色填充色\n  ctx.fillStyle = fillStyle\n  ctx.fillRect(0, 0, width, height)\n\n  // 用户自定义画布样式\n  if (isFunc(options.beforeDraw)) {\n    options.beforeDraw.call(this, ctx, canvas)\n  }\n\n  ctx.save()\n\n  switch (rotate) {\n    case 90:\n      ctx.translate(width, 0)\n      break\n    case -90:\n      ctx.translate(0, height)\n      break\n    case -180:\n      ctx.translate(width, height)\n      break\n  }\n\n  ctx.rotate((rotate * Math.PI) / 180)\n  ctx.scale(scaleX, scaleY)\n}\n\n/**\n * 画布上绘制图片后的一些操作：支持用户自定义\n * @param {CanvasRenderingContext2D} ctx Canvas 对象的上下文\n * @param {HTMLCanvasElement} canvas Canvas 对象\n */\n_proto.afterDraw = function (ctx, canvas) {\n  var options = this.options\n  // 用户自定义画布样式\n  if (isFunc(options.afterDraw)) {\n    options.afterDraw.call(this, ctx, canvas)\n  }\n}\n\n/**\n * 错误触发函数\n * @param {String} msg 错误消息\n */\n_proto.error = function (msg) {\n  var options = this.options\n  if (isFunc(options.error)) {\n    options.error.call(this, msg)\n  } else {\n    throw new Error(msg)\n  }\n}\n\n/**\n * 成功触发函数\n * @param {File|Blob} result `Blob` 对象\n */\n_proto.success = function (result) {\n  var options = this.options\n  var file = this.file\n  var image = this.image\n  var edge = this.getExpectedEdge()\n  var naturalHeight = image.naturalHeight\n  var naturalWidth = image.naturalWidth\n\n  if (result && result.size) {\n    // 在非宽松模式下，用户期待的输出宽高没有大于源图片的宽高情况下，输出文件大小大于源文件，返回源文件\n    if (\n      !options.loose &&\n      result.size > file.size &&\n      !(edge.width > naturalWidth || edge.height > naturalHeight)\n    ) {\n      console.warn('当前设置的是非宽松模式，压缩结果大于源图片，输出源图片')\n      result = file\n    } else {\n      var date = new Date()\n\n      result.lastModified = date.getTime()\n      result.lastModifiedDate = date\n      result.name = file.name\n\n      // 文件 `name` 属性中的后缀转化成实际后缀\n      if (result.name && result.type !== file.type) {\n        result.name = result.name.replace(\n          REGEXP_EXTENSION,\n          imageTypeToExtension(result.type)\n        )\n      }\n    }\n  } else {\n    // 在某些情况下压缩后文件为 `null`，返回源文件\n    console.warn('图片压缩出了点意外，输出源图片')\n    result = file\n  }\n\n  if (isFunc(options.success)) {\n    options.success.call(this, result)\n  }\n}\n\n/**\n * 文件转化成 `data URL` 字符串\n * @param {File} file 文件对象\n * @param {Function} callback 成功回调函数\n * @param {Function} error 取消回调函数\n */\nutil.file2DataUrl = function (file, callback, error) {\n  var reader = new FileReader()\n  reader.onload = function () {\n    callback(reader.result)\n  }\n  reader.onerror = function () {\n    if (isFunc(error)) {\n      error('读取文件失败！')\n    }\n  }\n  reader.readAsDataURL(file)\n}\n\n/**\n * 文件转化成 `ArrayBuffer`\n * @param {File} file 文件对象\n * @param {Function} callback 成功回调函数\n * @param {Function} error 取消回调函数\n */\nutil.file2ArrayBuffer = function (file, callback, error) {\n  var reader = new FileReader()\n  reader.onload = function (ev) {\n    callback(ev.target.result)\n  }\n  reader.onerror = function () {\n    if (isFunc(error)) {\n      error('读取文件失败！')\n    }\n  }\n  reader.readAsArrayBuffer(file)\n}\n\n/**\n * 从 data view 中的字符代码获取字符字符串\n * @param {DataView} dataView - The data view for read.\n * @param {number} start - 起始位置\n * @param {number} length - 读取长度\n * @returns {string} 读取结果\n */\nutil.getStringFromCharCode = function (dataView, start, length) {\n  var str = ''\n  var i\n\n  length += start\n\n  for (i = start; i < length; i += 1) {\n    str += String.fromCharCode(dataView.getUint8(i))\n  }\n\n  return str\n}\n\n/**\n * 文件转化成 `Image` 对象\n * @param {File} file 文件对象\n * @param {Function} callback 成功回调函数\n * @param {Function} error 错误回调函数\n */\nutil.file2Image = function (file, callback, error) {\n  var image = new Image()\n  var URL = WIN.URL || WIN.webkitURL\n\n  if (\n    WIN.navigator &&\n    /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WIN.navigator.userAgent)\n  ) {\n    // 修复IOS上webkit内核浏览器抛出错误 `The operation is insecure` 问题\n    image.crossOrigin = 'anonymous'\n  }\n\n  image.alt = file.name\n  image.onerror = function () {\n    if (isFunc(error)) {\n      error('图片加载错误！')\n    }\n  }\n\n  if (URL) {\n    var url = URL.createObjectURL(file)\n    image.onload = function () {\n      callback(image)\n      URL.revokeObjectURL(url)\n    }\n    image.src = url\n  } else {\n    this.file2DataUrl(\n      file,\n      function (dataUrl) {\n        image.onload = function () {\n          callback(image)\n        }\n        image.src = dataUrl\n      },\n      error\n    )\n  }\n}\n\n/**\n * `url` 转化成 `Image` 对象\n * @param {File} url `url`\n * @param {Function} callback 成功回调函数\n * @param {Function} error 失败回调函数\n */\nutil.url2Image = function (url, callback, error) {\n  var image = new Image()\n  image.src = url\n  image.onload = function () {\n    callback(image)\n  }\n  image.onerror = function () {\n    if (isFunc(error)) {\n      error('图片加载错误！')\n    }\n  }\n}\n\n/**\n * `Image` 转化成 `Canvas` 对象\n * @param {File} image `Image` 对象\n * @param {Number} dWidth 目标宽度\n * @param {Number} dHeight 目标高度\n * @param {Function} beforeDraw 在图片绘画之前的回调函数\n * @param {Function} afterDraw 在图片绘画之后的回调函数\n * @param {Number} width 宽\n * @param {Number} height 高\n * @return {HTMLCanvasElement} `Canvas` 对象\n */\nutil.image2Canvas = function (\n  image,\n  dWidth,\n  dHeight,\n  beforeDraw,\n  afterDraw,\n  width,\n  height\n) {\n  var canvas = document.createElement('canvas')\n  var ctx = canvas.getContext('2d')\n  canvas.width = width || image.naturalWidth\n  canvas.height = height || image.naturalHeight\n  if (isFunc(beforeDraw)) {\n    beforeDraw(ctx, canvas)\n  }\n  ctx.save()\n  ctx.drawImage(image, 0, 0, dWidth, dHeight)\n  ctx.restore()\n  if (isFunc(afterDraw)) {\n    afterDraw(ctx, canvas)\n  }\n  return canvas\n}\n\n/**\n * `Canvas` 转化成 `data URL` 对象\n * @param {File} file  `Canvas` 对象\n * @param {Float} quality 输出质量比例\n * @return {String} `data URL` 字符串\n */\nutil.canvas2DataUrl = function (canvas, quality, type) {\n  return canvas.toDataURL(type || 'image/jpeg', quality)\n}\n\n/**\n * `data URL` 转化成 `Image` 对象\n * @param {File} dataUrl `data URL` 字符串\n * @param {Function} callback 成功回调函数\n * @param {Function} error 失败回调函数\n */\nutil.dataUrl2Image = function (dataUrl, callback, error) {\n  var image = new Image()\n  image.onload = function () {\n    callback(image)\n  }\n  image.error = function () {\n    if (isFunc(error)) {\n      error('图片加载错误！')\n    }\n  }\n  image.src = dataUrl\n}\n\n/**\n * `data URL` 转化成 `Blob` 对象\n * @param {File} dataUrl `data URL` 字符串\n * @param {String} type `mime`\n * @return {Blob} `Blob` 对象\n */\nutil.dataUrl2Blob = function (dataUrl, type) {\n  var data = dataUrl.split(',')[1]\n  var mimePattern = /^data:(.*?)(;base64)?,/\n  var mime = dataUrl.match(mimePattern)[1]\n  var binStr = atob(data)\n  var len = data.length\n  var arr = new Uint8Array(len)\n\n  for (var i = 0; i < len; i++) {\n    arr[i] = binStr.charCodeAt(i)\n  }\n  return new Blob([arr], { type: type || mime })\n}\n\n/**\n * `Blob` 对象转化成 `data URL`\n * @param {Blob} blob `Blob` 对象\n * @param {Function} callback 成功回调函数\n * @param {Function} error 失败回调函数\n */\nutil.blob2DataUrl = function (blob, callback, error) {\n  this.file2DataUrl(blob, callback, error)\n}\n\n/**\n * `Blob`对象 转化成 `Image` 对象\n * @param {Blob} blob `Blob` 对象\n * @param {Function} callback 成功回调函数\n * @param {Function} callback 失败回调函数\n */\nutil.blob2Image = function (blob, callback, error) {\n  this.file2Image(blob, callback, error)\n}\n\n/**\n * `Canvas` 对象转化成 `Blob` 对象\n * @param {HTMLCanvasElement} canvas `Canvas` 对象\n * @param {Function} callback 回调函数\n * @param {Float} quality 输出质量比例\n * @param {String} type `mime`\n */\nutil.canvas2Blob = function (canvas, callback, quality, type) {\n  var _this = this\n  if (!HTMLCanvasElement.prototype.toBlob) {\n    Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {\n      value: function (callback, type, quality) {\n        var dataUrl = this.toDataURL(type, quality)\n        callback(_this.dataUrl2Blob(dataUrl))\n      },\n    })\n  }\n  canvas.toBlob(\n    function (blob) {\n      callback(blob)\n    },\n    type || 'image/jpeg',\n    quality || 0.8\n  )\n}\n\n/**\n * 文件上传\n * @param {String} url 上传路径\n * @param {File} file 文件对象\n * @param {Function} callback 回调函数\n */\nutil.upload = function (url, file, callback) {\n  var xhr = new XMLHttpRequest()\n  var fd = new FormData()\n  fd.append('file', file)\n  xhr.onreadystatechange = function () {\n    if (xhr.readyState === 4 && xhr.status === 200) {\n      // 上传成功\n      callback && callback(xhr.responseText)\n    } else {\n      throw new Error(xhr)\n    }\n  }\n  xhr.open('POST', url, true)\n  xhr.send(fd)\n}\n\nfor (var key in util) {\n  if (util.hasOwnProperty(key)) {\n    ImageCompressor[key] = util[key]\n  }\n}\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const TerserWebpackPlugin = require('terser-webpack-plugin')\nconst path = require('path')\n\nmodule.exports = {\n  mode: 'none',\n  entry: {\n    'image-compressor': './src/index.js',\n    'image-compressor.min': './src/index.js',\n  },\n  output: {\n    path: path.resolve(__dirname, './dist'),\n    filename: '[name].js',\n    library: 'ImageCompressor',\n    libraryExport: 'default',\n    libraryTarget: 'umd',\n  },\n  optimization: {\n    minimize: true,\n    minimizer: [\n      new TerserWebpackPlugin({\n        include: /\\.min\\./,\n      }),\n    ],\n  },\n}\n"
  }
]