前端图片压缩算法

368 阅读1分钟

实现图片上传

<input type="file" id="upload">

    <script>
        const upload = document.getElementById('upload');
        upload.addEventListener('change', function (e) {
            console.log(e.target.files);
        })
    </script>

image.png

对图片格式与大小进行限制

<input type="file" id="upload">
    <script>
        //定义图片类型与最大尺寸
        const Accept = ['image/jpg', 'image/png', 'image/jpeg'];
        const MaxSize = 1024 * 1024

        const upload = document.getElementById('upload');
        upload.addEventListener('change', function (e) {
            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(`文件大小不能超过${MaxSize}`)
                upload.value = ''
                return
            }
        })

image.png

image.png

这样,图片大小与图片类型就已经有了限制.

核心图片压缩算法

图片转base64

            function ToBase64(file, callback) {
                let reader = new FileReader()
                reader.readAsDataURL(file)
                reader.addEventListener('load', function (e) {
                    console.log(e.target.result);
                })

            }
            ToBase64(file)

readAsDataURL 方法会读取指定的 Blob 或 File 对象。读取操作完成的时候,readyState 会变成已完成DONE,并触发 loadend (en-US) 事件,同时 result 属性将包含一个data:URL 格式的字符串(base64 编码)以表示所读取文件的内容。

image.png

图片压缩

//压缩图片
            function compress(base64Image) {
                let maxW = 500
                let maxH = 500
                // console.log(base64Image)
                const image = new Image()
                image.addEventListener('load', function (e) {
                    let ratio
                    let needCompress = false
                    console.log(image.naturalWidth, image.naturalHeight);
                    //naturalWidth和naturalHeight是图片原始尺寸永远不会改变
                    //当限定宽 > 图片宽
                    if (maxW < image.naturalWidth) {
                        needCompress = true
                        ratio = image.naturalWidth / maxW
                        console.log(ratio, 'ratio');
                        maxH = image.naturalHeight / ratio
                        console.log(maxH, 'maxH');
                    }
                    //当限定高 > 图片高
                    if (maxH < image.naturalHeight) {
                        needCompress = true
                        ratio = image.naturalHeight / maxH
                        console.log(ratio, 'ratio');
                        maxW = image.naturalWidth / ratio
                        console.log(maxW, 'maxW');
                    }
                    //当图片 < 最大值
                    if (!needCompress) {
                        maxW = image.naturalWidth
                        maxH = image.naturalHeight
                    }
                    
                    const canvas = document.createElement('canvas')
                    canvas.width = maxW
                    canvas.height = maxH
                    canvas.style.visibility = 'hidden'
                    document.body.appendChild(canvas)

                    const ctx = canvas.getContext('2d')
                    ctx.clearRect(0, 0, maxW, maxH) //清空画布
                    ctx.drawImage(image, 0, 0, maxW, maxH) //将图片绘制到canvas上
                    const compressImage = canvas.toDataURL('image/jpeg', 0.9)//转换为base64编码

                    canvas.remove()
                })
                image.src = base64Image
                // document.body.appendChild(image)
            }

改造图片转base64方法,在其中进行图片压缩

    //转换为base64
            function ToBase64(file, callback) {
                let reader = new FileReader()
                reader.readAsDataURL(file)
                reader.addEventListener('load', function (e) {
                    const base64 = e.target.result
                    callback && callback(base64)
                    reader = null
                })
            }
            ToBase64(file,compress)

整体代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图片压缩算法</title>
</head>

<body>
    <input type="file" id="upload">
    <script>
        //上传图片到服务器
        function uploadToServer(compressImage) {
            console.log('上传图片到服务器');
        }
        //基本数据
        const Accept = ['image/jpg', 'image/png', 'image/jpeg'];
        const MaxSize = 1024 * 1024

        const upload = document.getElementById('upload');
        upload.addEventListener('change', function (e) {
            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(`文件大小不能超过${MaxSize}`)
                upload.value = ''
                return
            }
            //压缩图片
            //转换为base64
            function ToBase64(file, callback) {
                let reader = new FileReader()
                reader.addEventListener('load', function (e) {
                    const base64 = e.target.result
                    callback && callback(base64)
                    reader = null
                })
                reader.readAsDataURL(file)
            }
            //压缩图片
            function compress(base64Image, callback) {
                let maxW = 500
                let maxH = 500
                // console.log(base64Image)
                const image = new Image()
                image.addEventListener('load', function (e) {
                    let ratio
                    let needCompress = false
                    // console.log(image.width, image.height);
                    console.log(image.naturalWidth, image.naturalHeight);
                    //naturalWidth和naturalHeight是图片原始尺寸永远不会改变,width和height是图片在浏览器中的尺寸
                    if (maxW < image.naturalWidth) {
                        needCompress = true
                        ratio = image.naturalWidth / maxW
                        console.log(ratio, 'ratio');
                        maxH = image.naturalHeight / ratio
                        console.log(maxH, 'maxH');
                    }
                    if (maxH < image.naturalHeight) {
                        needCompress = true
                        ratio = image.naturalHeight / maxH
                        console.log(ratio, 'ratio');
                        maxW = image.naturalWidth / ratio
                        console.log(maxW, 'maxW');
                    }
                    if (!needCompress) {
                        maxW = image.naturalWidth
                        maxH = image.naturalHeight
                    }
                    const canvas = document.createElement('canvas')
                    canvas.width = maxW
                    canvas.height = maxH
                    canvas.style.visibility = 'hidden'
                    document.body.appendChild(canvas)

                    const ctx = canvas.getContext('2d')
                    ctx.clearRect(0, 0, maxW, maxH) //清空画布
                    ctx.drawImage(image, 0, 0, maxW, maxH) //将图片绘制到canvas上

                    const compressImage = canvas.toDataURL('image/jpeg', 0.9)

                    callback && callback(compressImage)  //模拟发请求
                    console.log(compressImage);
                    canvas.remove()
                })
                image.src = base64Image
                // document.body.appendChild(image)
            }
            ToBase64(file, (base64Image) => compress(base64Image, uploadToServer))
        })
    </script>
</body>

</html>

核心流程

  1. 监听 input type = filechange 方法 , 获取到 e.target.files
  2. 使用 fileREader 读取文件 , 监听 该对象的 load 方法 , 拿到 e.target.result , 这是 base64 数据
  3. 压缩图片 新建 image 对象 , 监听 load 方法 根据自定义的宽高拿到纵横比对图片进行限制
  4. 使用 canvas 画布 进行图片的绘制 , 使用 drawImage 方法 , 把图片绘制到画布上 , 再调用 toDataURL 方法(方法返回一个包含图片展示的 data URI )