前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Canvas 进阶(六)实现图片压缩功能

Canvas 进阶(六)实现图片压缩功能

作者头像
小皮咖
发布2020-02-25 15:56:25
1.3K0
发布2020-02-25 15:56:25
举报
文章被收录于专栏:小皮咖

功能体验

先看demo:图片压缩

项目源码:github

效果如下:

实现功能

  1. 能够获取压缩后的 base64 图片
  2. 能够获取压缩后的图片节点,并能替换文档中的图片节点
  3. 能够获取压缩后的 canvas 进行二次绘制,并能替换文档中的 canvas
  4. 能过获取压缩后的 blob 文件
  5. 能够下载压缩后的图片

具体实现

前端实现压缩功能,从图片大小和质量两方面着手:缩小图片和降低图片质量

因此我们设计一个 imageCompress 类,传入一个 option, 其参数有:

代码语言:javascript
复制
file: url 或 file
width: 输出图片的宽度
height: 输出图片的高度
mineType: 输出图片的格式,默认为image/png
quality: 输出图片的画质。值为0~1,默认为1 

因为图片的加载是一个异步的过程,因此我们需要借助 promise, 以new ImageCompress(option).then(instance => {})创建并在 then 中调用实例的方法。因为 file 可以是 url 也可以是 file 对象,因此在构建函数中需对这两种情况分别判断,并在结束时返回 promise

1. 创建此类

代码语言:javascript
复制
class imageCompress {
    constructor(options) {
        this._img = null; // 原始图片
        this._compressedImg = null; // 压缩后的图片
        this._canvas = null; // 创建的canvas
        this._blob = null; // 创建的blob
        
        // 默认设置
        this.options = {
            mimeType: 'image/png',
            quality: 1,
        };
        // 合并默认参数与用户参数
        extend(this.options, options, true);

        // 文件为传入抛出错误
        if (!options.file) {
            throw new Error('图片未传入');
        }

        let that = this;
        if (typeof options.file === 'string') {
            let img = (this._img = new Image());
            // 解决跨域报错问题
            img.setAttribute('crossOrigin', 'anonymous');
            img.src = options.file;

            return new Promise((resolve, reject) => {
                img.onload = function() {
                    resolve(that);
                };
                img.onerror = function(...args) {
                    reject(new Error('图片加载失败'));
                };
            });
        } else if (typeof options.file === 'object' && options.file.name) {
            // 判断文件的后缀是否为图片
            if (!isAssetTypeAnImage(options.file.name)) {
                throw new Error('该文件不是图片');
            }
            let image = (this._img = new Image());
            return new Promise((resolve, reject) => {
                if (window.URL) {
                    image.src = window.URL.createObjectURL(options.file);
                } else {
                    const reader = new FileReader();
                    reader.onload = e => {
                        image.src = e.target.result;
                    };

                    reader.onabort = () => {
                        reject(
                            new Error(
                                'Aborted to load the image with FileReader.'
                            )
                        );
                    };
                    reader.onerror = () => {
                        reject(
                            new Error(
                                'Failed to load the image with FileReader.'
                            )
                        );
                    };

                    reader.readAsDataURL(options.file);
                }
                image.onload = function() {
                    resolve(that);
                };
                image.onerror = function(...args) {
                    reject(new Error('图片加载失败'));
                };
            });
        }
    }
}
复制代码

2. 获取 canvas 和与页面 canvas 进行替换功能

我们在构建函数中已经将传入的图片加载并赋值给了 this._img, 接下来创建一个 canvas, 并将此图片按设置的大小画出来,便得到目标 canvas; 替换一个节点,先找出其父节点,并用一个新节点替换 oldNode.parentNode.replaceChild(newNode, oldNode);

代码语言:javascript
复制
// 获取canvas,可用于二次加工绘制
getCanvas() {
    if (!this._canvas) this._imagetoCanvas();
    return this._canvas;
}
// 私有方法,图片转canvas
_imagetoCanvas() {
    let image = this._img;
    var cvs = (this._canvas = document.createElement('canvas'));
    var ctx = cvs.getContext('2d');
    cvs.width = this.options.width || image.width;
    // 高度默认等比例压缩
    cvs.height = this.options.width
        ? (this.options.width * image.height) / image.width
        : image.height;
    ctx.drawImage(image, 0, 0, cvs.width, cvs.height);
}
// 替换文档canvas节点
replaceCanvasNode(oldNode) {
    let newNode = this.getCanvas();
    // 使新节点具有旧节点的id,类名,样式
    newNode.style.cssText = oldNode.style.cssText;
    newNode.id = oldNode.id;
    newNode.className = oldNode.className;
    // 用新节点替换旧节点
    oldNode.parentNode.replaceChild(this.getCanvas(), oldNode);
}

3. 获取压缩后的图片base64

前一步我们已经能够获取 canvas,将 canvas 调用 canvas.toDataURL(this.options.mimeType, this.options.quality)即可获取 base64

代码语言:javascript
复制
getImageBase64() {
    let canvas = this.getCanvas()
    return canvas.toDataURL(this.options.mimeType, this.options.quality);
}

4. 获取压缩后的文件

获取blob调用 canvas.toBlob(callback,mimeType,quality), 由于此过程也是异步,因此返回 promise

代码语言:javascript
复制
// 获取压缩后的文件,return promise.resolve(blob)
getCompressFile() {
    if (!this._canvas) this._imagetoCanvas();
    let that = this;
    return new Promise((resolve, reject) => {
        that._canvas.toBlob(
            blob => {
                that._blob = blob;
                resolve(blob);
            },
            that.options.mimeType, // 图片类型
            that.options.quality // 图片质量
        );
    });
}
复制代码

5. 获取压缩后的 img 节点与页面 img 进行替换功能

this._compressedImg 指向压缩后的图片,我们的目标是找到 image 的 src 属性,有两种方法 URL.createObjectURL(blob)new FileReader().readAsDataURL(blob), 因此我们需调用第 4 步实现的方法 getCompressFile 获取 blob

代码语言:javascript
复制
// 获取压缩后的图片节点
getCompressImageNode() {
    // 如果压缩后的图片已经创建,则不需要重复创建,返回即可
    if (this._compressedImg && this._compressedImg.src)
        return Promise.resolve(this._compressedImg);
    let image = (this._compressedImg = new Image());
    return this.getCompressFile().then(blob => {
        if (window.URL) {
            image.src = window.URL.createObjectURL(blob);
        } else {
            const reader = new FileReader();

            reader.onload = e => {
                image.src = e.target.result;
            };
            // 终止事件
            reader.onabort = () => {
                return Promise.reject(
                    new Error('Aborted to load the image with FileReader.')
                );
            };
            reader.onerror = () => {
                return Promise.reject(
                    new Error('Failed to load the image with FileReader.')
                );
            };

            reader.readAsDataURL(blob);
        }
        return Promise.resolve(image);
    });
}
// 替换页面图片
replaceImageNode(oldNode) {
    this.getCompressImageNode().then(image => {
        image.style.cssText = oldNode.style.cssText;
        image.id = oldNode.id;
        image.className = oldNode.className;
        oldNode.parentNode.replaceChild(image, oldNode);
    });
}
复制代码

6. 下载压缩后的文件

this._compressedImg 被赋值且其 src 属性存在时,可以直接创建 a 标签下载;若没有创建压缩后的 img, 则调用上一步创建的 getCompressImageNode() 方法获取压缩后的 img, 再进行下载

代码语言:javascript
复制
// 下载压缩后的文件
downloadCompressFile(name = 'compress-file') {
    if (this.blob && this._compressedImg) {
        const dataURL = this._compressedImg.src;
        const link = document.createElement('a');
        link.download = name;
        link.href = dataURL;
        link.dispatchEvent(new MouseEvent('click'));
    } else {
        this.getCompressImageNode().then(image => {
            const dataURL = image.src;
            const link = document.createElement('a');
            link.download = name;
            link.href = dataURL;
            link.dispatchEvent(new MouseEvent('click'));
        });
    }
}
复制代码

demo 测试

对上述7个功能进行测试,效果及代码如下:

代码语言:javascript
复制
new imageCompress({
    file:
        'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1477436123,3577936229&fm=26&gp=0.jpg',
    width: 500,
    quality: 1,
    mimeType: 'image/jpeg',
})
    .then(instance => {
        // 获取canvas,可用于自行加工绘制
        let canvas = instance.getCanvas();
        let context = canvas.getContext('2d');
        context.moveTo(100, 100);
        context.lineTo(50, 50);
        context.stroke();

        // 替换文档中存在图片节点
        instance.replaceImageNode(document.getElementById('img'));
        // 替换文档中存在的canvas节点
        instance.replaceCanvasNode(document.getElementById('canvas'));
        // 获取压缩后生成的image节点
        instance.getCompressImageNode().then(image => {
           console.log(image)
        });

        // // 获取压缩后的blob文件,可用于上传
        instance.getCompressFile().then(blob => {
           
        });
        // 获取图片base64
        let base64 = instance.getImageBase64();
        // 下载压缩后文件
        // instance.downloadCompressFile();
    })
    .catch(err => {
        console.log(err);
    });
复制代码

以上是我能想到的图片压缩的7个功能,如果你有想到其他的需求,欢迎在评论区留言。如果文中有错漏,也欢迎指出!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020年02月20日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 功能体验
  • 实现功能
  • 具体实现
    • 1. 创建此类
      • 2. 获取 canvas 和与页面 canvas 进行替换功能
        • 3. 获取压缩后的图片base64
          • 4. 获取压缩后的文件
            • 5. 获取压缩后的 img 节点与页面 img 进行替换功能
              • 6. 下载压缩后的文件
              • demo 测试
              相关产品与服务
              文件存储
              文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档