前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端:Canvas进行简单图片压缩算法

前端:Canvas进行简单图片压缩算法

作者头像
19组清风
发布2021-11-15 15:28:28
1.7K0
发布2021-11-15 15:28:28
举报
文章被收录于专栏:Web Front End

图片压缩算法

通过canvasAPI进行在Web端上传的时候进行图片压缩。

通过宽高压缩(第一次压缩 - Canvas宽高) 通过API压缩(第二次压缩 - HTMLCanvasDom.toDataURL())

简述步骤

  1. 通过input输入框用来坐上传,通过chang事件获得上传的文件。
  2. 对上传的文件进行一些简单的类型,大小的判断然后开始压缩。
  3. 压缩图片第一步将用户上传的图片(file)转为base64格式-new FileReader() -> ReaderAsDataUrl()异步读取 -> load事件读取完成获取base64
  4. 计算压缩图片宽高(等比例),创建Canvas画布赋值新的宽高。(第一次压缩尺寸)然后将canvasDom进行隐藏,挂载到dom节点上。
  5. getContext('2d')获得canvas对象,调用drawImage方法进行绘制。
  6. 然后通过CanvasHTMLDOM调用toDataURL方法将整个canvas画布输出成base64格式。(第二次压缩)
  7. 删除CanvasDOM,然后将压缩后的base64通过callback传入到服务端中就可以了。

Tips

  • 通过change事件进行异步完成监听
  • new FileReader() -> reader.readAsDataURL(file) 读取上传文件 => reader.result获得base64格式的上传图片
  • 压缩对宽度和高度进行压缩 naturalWidth/naturalHeihgt HTML5属性获得源生图片高度宽度
  • 创建Canvas标签赋值压缩后的宽高
  • 隐藏使用visibility-hidden 仅仅是样式隐藏。因为visibility属性隐藏可以获取DOM
  • ctx.clearReact() -> 每次重新上传文件要清除Canvas画布,防止图片覆盖。
  • ctx.drawImage()通过canvas绘制对应文件。
  • HTMLCanvasDOM.toDataUrl() 将canvas画布转化成base64格式同时进行压缩,得到返回值。
  • 压缩成功,使用callback传入新的Data URL进行向后台发送逻辑请求。

代码演示

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>canvas图片压缩算法</title>
</head>

<body>
    <!-- 图片上传到服务端之前进行图片压缩 -->
    <input type="file" id="file" />
    <script>
        const ACCEPT = ['image/jpg', 'image/png', 'image/jpeg']
        const upload = document.getElementById("file")
        const MAXSIZE = 1024 * 1024;
        const MAXTIP = "1MB"

        function convertImageToBase64(file, callback) {
            // 创建一个FileReader对象,它允许Web应用程序异步读取存储在计算机上的文件
            // 也就是file对象
            let reader = new FileReader()
            // 添加一个load事件,load会在加载完毕之后进行触发(也就是readAsDataURL读取完毕后触发)
            reader.addEventListener("load", function (e) {
                const base64Image = e.target.result // 相当于reader.result 获取文件的Base64
                // 回收内存
                callback && callback(base64Image) // 调用callback压缩
                reader = null
            })

            // readAsDataURL方法读取指定的file或blob对象
            reader.readAsDataURL(file)
        }
        // 压缩算法函数
        /* 
        1.首先拿到了base64的图片字符串 
        2.创建一个image对象,获得原始图片的宽度和高度
        3.对原始图片的宽度和高度进行压缩达到符合条件(第一次压缩-从尺寸压缩)
        4.调用canvasAPI进行绘制新的图片
        5.绘制成功之后调用canvasAPI进行绘制(canvasAPI支持压缩-二次压缩-从质量压缩)
        6.得到压缩后的base64
         */
        function compress(base64Image, callback) {
            let maxW = 1024;
            let maxH = 1024;
            const image = new Image() // 创建image对象 相当于创建a标签
            image.addEventListener('load', function (e) {
                // image加载完成后就会触发 也就是src加载后
                let radio; // 压缩比例
                let needCompress = false; // 是否需要压缩
                // image.naturalWidth/naturalHeight H5新属性 获取源生图片的宽高
                if (image.naturalWidth > maxW) {
                    needCompress = true;
                    // 获得压缩宽高过后的大小(保证等比例缩放)
                    radio = image.naturalWidth / maxW
                    maxH = image.naturalHeight / radio
                }
                // 同样宽度压缩之后 还要看压缩后的高度是否满足 不满足则继续压缩宽高
                if (image.naturalHeight > maxH) {
                    needCompress = true;
                    radio = image.naturalHeight / maxH
                    maxW = image.naturalWidth / radio
                }
                // 不需要压缩
                if (!needCompress) {
                    maxW = image.naturalWidth;
                    maxH = image.naturalHeight;
                }
                // 第一次压缩完成
                // 接下来使用canvas进行质量压缩
                const canvas = document.createElement('canvas')
                canvas.height = maxH;
                canvas.width = maxW;
                canvas.setAttribute("id", "_compress_")
                // visibility hidden 需要创建的canvas隐藏 而不是不渲染DOM
                canvas.style.visibility = 'hidden'
                document.body.appendChild(canvas)

                const ctx = canvas.getContext('2d')
                // canvas.clearRect() 方法清空给定矩形内的指定像素。(x1,y1,width,height)
                // 防止重新上传覆盖 
                ctx.clearRect(0, 0, maxW, maxH)
                // canvas.drawImage() 方法在画布(canvas)上绘制图像、画布或视频。
                // 传入 视频/图片对象 起始点x 起始点y 绘制宽 绘制高 
                ctx.drawImage(image, 0, 0, maxW, maxH)
                // 接来下就是压缩canvas 通过API将canvas输出成base64格式

                /**
                 * @HTMLCanvasElement.toDataURL(type, encoderOptions);  注意调用这是 Canvas的Dom对象而非ctx
                 * @param {String} 可以使用 type 参数其类型, type 图片格式,默认为image/png,图片的分辨率为96dpi。
                 * @param {Number}  encoderOptions 可选 指定图片格式是image/jpeg或image/webp的情况下,可以从0到1区间内进行选择图片的质量(1原质量)。如果超出取值方位,使用默认0.92 
                 * @returns {dataURL}  方法返回一个包含图片展示的 data URI (Base64)
                 */
                console.log(base64Image, 'base64Image')
                /* 注意这里是Canvas DOM 节点 而非canvas对象*/
                const compressImage = canvas.toDataURL('image/jpeg', 0.8) // 通常压缩是0.8-0.9
                callback && callback(compressImage); // 压缩完成进行后台传输逻辑
                // 压缩完的图片就已经保存在内存(compressImage)中了 
                // 接下来移除canvas元素 调用DOM.remove()
                canvas.remove()

                // 需要的上传预览的话可以单独建一个new image进行预览
                const _image = new Image()
                _image.src = compressImage;
                document.body.appendChild(_image)
                //计算压缩比 使用src(base)的长度就可以对比了 
                console.log(`压缩比${image.src.length/_iamge.src.length}`)


            })
            image.src = base64Image;
            // document.body.appendChild(image) // 挂载

        }
        // 绑定上传图片的change事件
        // mock上传后台逻辑
        function uploadToServer(compressImage) {
            console.log("上传后台")
        }
        upload.addEventListener('change', function (e) {
            // 获取上传的文件 解构Arry 拿到第一个元素
            const [file] = e.target.files;
            if (!file) {
                return;
            }
            const {
                type: fileType,
                size: fileSize
            } = file;
            // 上传校验逻辑
            if (!ACCEPT.includes(fileType)) {
                alert(`不支持[${fileType}]类型`)
                upload.value = ""
                return
            }
            if (fileSize > MAXSIZE) {
                alert(`文件超${MAXTIP}`)
                upload.value = ""
                return
            }
            // 压缩图片 需要转成base64进行压缩
            convertImageToBase64(file, (base64Image) => {
                compress(base64Image, uploadToServer)
            })
        })
    </script>
</body>

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 图片压缩算法
  • 简述步骤
  • Tips
  • 代码演示
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档