compressImage.js

import { toNumber } from 'ut2';
import loadImageWithBlob from './loadImageWithBlob';
import { nativeUndefined } from './utils/native';
/**
 * canvas 导出 blob 对象
 *
 * @private
 * @param canvas 画布元素
 * @param type 导出图片类型
 * @param quality 图像质量
 * @returns {Promise<Blob>}
 */
function canvasToBlob(canvas, type, quality) {
    return new Promise(function (resolve) {
        canvas.toBlob(function (blob) {
            resolve(blob);
        }, type, quality);
    });
}
/**
 * 压缩图片。
 *
 * <em style="font-weight: bold;">注意:该方法仅适用于浏览器端。</em>
 *
 * <em style="font-weight: bold;">如果是半透明图片并且导出 `image/png` 格式,建议将背景变成透明 `background=transparent`,避免出现白边。注意正常图片压缩导出 `image/png` 格式后文件可能会比原图大。</em>
 *
 * @function
 * @alias module:Other.compressImage
 * @since 4.20.0
 * @see {@link https://sytpwg.csb.app/ | 在线示例}
 * @param {string | Blob} img 图片地址或 blob 对象
 * @param {Object} [options] 配置项
 * @param {number} [options.width] 自定义图片宽度,默认图片自身宽度
 * @param {number} [options.height] 自定义图片高度,默认图片自身高度
 * @param {number} [options.rotate] 旋转
 * @param {Array | function} [options.offset=[0, 0]] x,y轴偏移值
 * @param {string} [options.background=#fff] 背景颜色
 * @param {number | function} [options.canvasWidth] 画布宽度,默认图片宽度
 * @param {number | function} [options.canvasHeight] 画布高度,默认图片高度
 * @param {'blob' | 'dataURL'} [options.format='blob'] 导出格式
 * @param {string} [options.type='image/jpeg'] 图片类型
 * @param {number} [options.quality=0.8] 图片质量
 * @param {function} [options.beforeCompress] 图片加载完成,画布创建之前调用
 * @param {function} [options.beforeDraw] 图片载入画布之前调用
 * @param {function} [options.afterDraw] 图片载入画布之后调用
 * @param {boolean | CacheOptions} [options.cacheImage=true] 是否使用 `loadImageWithBlob` 缓存。
 * @param {AjaxOptions} [options.ajaxOptions] ajax 请求配置项,当传入的图片为字符串时才会触发请求。
 * @returns {Promise<Blob | string>} blob 对象 或 data url 图片
 * @example
 *
 * // 默认返回压缩后的 blob 对象
 * compressImage(file).then(blob=>{
 *    // do something
 * });
 *
 * // 设置返回格式 data url
 * compressImage(file, { format: 'dataURL' }).then(url=>{
 *    // do something
 * });
 *
 * // 自定义配置
 * compressImage('https://dummyimage.com/200x300', {
 *  width: 100,
 *  height: 100,
 *  quality: 0.5,
 *  beforeCompress({ image, blob }){},
 *  beforeDraw({ image, blob, canvas, context }){}
 *  afterDraw({ image, blob, canvas, context }){}
 * }).then(blob=>{
 *   // do something
 * });
 *
 * // 支持不同形式的图片文件
 * compressImage('data:image/png;base64,PGEgaWQ9ImEiPjxiIGlkPSJiIj5oZXkhPC9iPjwvYT4=').then(blob=>{
 *   // do something
 * });
 *
 */
var compressImage = function (img, options) {
    if (options === void 0) { options = {}; }
    return new Promise(function (resolve, reject) {
        var width = options.width, height = options.height, rotate = options.rotate, _a = options.offset, offset = _a === void 0 ? [0, 0] : _a, _b = options.cacheImage, cacheImage = _b === void 0 ? true : _b, _c = options.background, background = _c === void 0 ? '#fff' : _c, canvasWidth = options.canvasWidth, canvasHeight = options.canvasHeight, _d = options.format, format = _d === void 0 ? 'blob' : _d, _e = options.type, type = _e === void 0 ? 'image/jpeg' : _e, _f = options.quality, quality = _f === void 0 ? 0.8 : _f, beforeCompress = options.beforeCompress, beforeDraw = options.beforeDraw, afterDraw = options.afterDraw, ajaxOptions = options.ajaxOptions;
        // 加载图片
        loadImageWithBlob(img, cacheImage, ajaxOptions)
            .then(function (_a) {
            var image = _a.image, blob = _a.blob;
            var numWidth = toNumber(width);
            var numHeight = toNumber(height);
            var numQuality = toNumber(quality);
            // 自定义图片宽高
            if (numWidth) {
                image.width = numWidth;
            }
            if (numHeight) {
                image.height = numHeight;
            }
            beforeCompress === null || beforeCompress === void 0 ? void 0 : beforeCompress({ image: image, blob: blob }, options);
            // 创建 canvas
            var canvas = document.createElement('canvas');
            var ctx = canvas.getContext('2d');
            var info = { image: image, blob: blob, canvas: canvas, context: ctx };
            // 设置 canvas 宽高
            var numCanvasWidth = toNumber(typeof canvasWidth === 'function' ? canvasWidth(info, options) : canvasWidth);
            var numCanvasHeight = toNumber(typeof canvasHeight === 'function' ? canvasHeight(info, options) : canvasHeight);
            canvas.width = numCanvasWidth || image.width;
            canvas.height = numCanvasHeight || image.height;
            var bgIsTransparent = background === 'none' || background === 'transparent';
            // 填充背景色
            if (bgIsTransparent) {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
            }
            else {
                ctx.fillStyle = background;
                ctx.fillRect(0, 0, canvas.width, canvas.height);
            }
            // 旋转/变换/偏移
            // 如果设置了旋转,自动将原点设置为图片中心,并调整偏移值。
            var internalOffset = [0, 0];
            if (rotate !== nativeUndefined) {
                ctx.translate(image.width / 2, image.height / 2);
                internalOffset = [-image.width / 2, -image.height / 2];
                ctx.rotate((rotate * Math.PI) / 180);
            }
            var outOffset = typeof offset === 'function' ? offset(info, options) : offset;
            beforeDraw === null || beforeDraw === void 0 ? void 0 : beforeDraw(info, options);
            // 将图像载入 canvas 中
            var dx = internalOffset[0] + toNumber(outOffset[0]);
            var dy = internalOffset[1] + toNumber(outOffset[1]);
            ctx.drawImage(image, dx, dy, image.width, image.height);
            // 处理png图片透明背景
            if (type === 'image/png' && bgIsTransparent) {
                ctx.globalCompositeOperation = 'destination-in';
                ctx.drawImage(image, dx, dy, image.width, image.height);
            }
            afterDraw === null || afterDraw === void 0 ? void 0 : afterDraw(info, options);
            // 导出压缩后的图片
            if (format === 'blob') {
                canvasToBlob(canvas, type, numQuality).then(resolve).catch(reject);
            }
            else {
                resolve(canvas.toDataURL(type, numQuality));
            }
        })
            .catch(reject);
    });
};
export default compressImage;