canvas 高级应用:图片压缩算法实现

128 阅读1分钟

本文章将主要讲解利用canvas实现图片压缩,先将图片格式转为base64格式后利用convas实现等比例宽高的一次压缩后再调用convas.toDataURL('image/jpeg',0.9) 将convas画布输出成图片 二次压缩,代码粘贴如下

<!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>canvas 高级应用:图片压缩算法实现</title>
</head>
<body>
    <input type="file" id="upload">
    <script>
        const ACCEPT = ['image/jpg','image/png','image/jpeg']; //图片格式限制
        const MAX_SIZE = 3 * 1024 * 1024; // 图片大小限制
        const MAX_SIZE_STR = '3MB';
        const uplaod = document.getElementById('upload');
        // 转为base64格式
        function convertImageToBase64(file,callback) {
            let reader = new FileReader();
            reader.addEventListener('load', function(e) {
                const base64Image = e.target.result;
                callback && callback(base64Image);
                reader = null
4            })
            reader.readAsDataURL(file);
        }
        // 压缩图片回调函数
        function compress(base64Image,callback) {
            let maxW = 1024; // 图片最大宽
            let maxH = 1024; // 图片最大高
            const image = new Image();
            image.addEventListener('load',function(e) {
                let radio; //图片压缩比
                let needCompress = false; //是否需要压缩
                //console.log(image.naturalWidth,image.naturalWidth) // 图片原始宽高
                if(maxW < image.naturalWidth) {
                    needCompress = true;
                    radio = image.naturalWidth / maxW;
                    maxH = image.naturalHeight / radio; // 使压缩后的图片保持同样的压缩比
                } // 经过处理后的图片实际尺寸
                if(maxH < image.naturalHeight) {
                    needCompress = true;
                    radio = image.naturalHeight / maxH;
                    maxW = image.naturalWidth / radio;
                }
                if (!needCompress) {
                    maxW = image.naturalWidth;
                    maxH = image.naturalHeight;
                } // 如果不需要压缩,需要获取图片的实际尺寸
                const convas = document.createElement('canvas'); // 创建convas画布
                convas.setAttribute('id', '__compress__');
                convas.width = maxW;
                convas.height = maxH;
                convas.style.visibility = 'hidden';
                document.body.appendChild(convas);
                const ctx = convas.getContext('2d');
                ctx.clearRect(0,0,maxW,maxH); //清除像素再绘制图片 不然会出现两张图片叠加的效果
                ctx.drawImage(image,0,0,maxW,maxH); //绘制图片 一次压缩实现完成
                const compressImage = convas.toDataURL('image/jpeg',0.9) // 将convas画布输出成图片 二次压缩
                callback && callback(compressImage)
                const _image = new Image(); //实际项目移除 不做image展示 直接上传即可
                _image.src = compressImage;//实际项目移除 不做image展示 直接上传即可
                document.body.append(_image)//实际项目移除 不做image展示 直接上传即可
                convas.remove();
                console.log('压缩比:' + image.src.length / _image.src.length)
            })
            image.src = base64Image;
            document.body.appendChild(image) //渲染图片 //实际项目移除 不做image展示 直接上传即可
        }
        // 上传至服务端的逻辑
        function uploadtoServer(compressImage) {
            console.log('upload to server...',compressImage)
        }
        // 上传事件
        upload.addEventListener('change',function(e) {
            const [file] = e.target.files;
            if(!file) {
                return;
            }
            const {type:fileTye,size: fileSize} = file;
            // if(ACCEPT.indexOf(fileTye) < 0) {
            // 图片支持的类型检查
            if(!ACCEPT.includes(fileTye)) {
                alert(`不支持[ + ${fileTye} + ]文件类型`);
                return;
            }
            // 图片容量检查
            if(fileSize  > MAX_SIZE) {
                alert(`文件超出${MAX_SIZE_STR}!`);
                uplaod.value = '';
                return;
            }
            // 压缩图片
            convertImageToBase64(file,(base64Image) => compress(base64Image,uploadtoServer));
        })
    </script>
</body>
</html>