zoukankan      html  css  js  c++  java
  • canvas压缩、裁切图片和格式转换的方法

    按照大小压缩图片,或者按照特定分辨率裁切图片,转为blob数据。自动处理ios中可能存在的照片偏差90°问题。

    例如,获取300*300大小的头像,实现以下效果:

     使用方式:

    <!-- 引入js文件 -->
        <script type="text/javascript" src="./compressImage.js"></script>
    <!-- input标签 -->
        <input type="file" id="avatar" name="avatar" accept="image/png, image/jpeg">
    

    如果想通过npn引入,请参考 git说明 。

    裁取特定分辨率的图片(如300*300):

        compressImage({
            input: document.getElementById('avatar'),
             300,
            height: 300,
            callback: function(blob, fileName) {
                // blob是处理之后的图片二进制数据
                // fileName是文件名,如"avatar.png"
                // ...
            }
        })
    

      

    将图片压缩到指定大小,如500kb以下:

        compressImage({
            input: document.getElementById('avatar'),
            size: 500,
            callback: function(blob, fileName) {
                // ...
            }
        })
    

      

    指定图片名字和格式(允许jpg和png格式互转):

        compressImage({
            input: document.getElementById('avatar'),
            size: 500,
            name: "user_avatar_1024687956"
            type: 'png',
            callback: function(blob, fileName) {
                // 此处得到的fileName就是 user_avatar_1024687956.png
                // ...
            }
        })
    

      

    callback是图片处理之后的回调函数,图片会转为blob数据数据,blob的用法参考:

        callback: function(blob, fileName) {
            var url = URL.createObjectURL(blob);
    
            /**** 通过<img>显示 ****/
            var img = document.createElement("img");
            img.src = url;
            document.body.append(img);
    
            /**** formData上传图片 ****/
            var formData = new FormData();
            formData.append("file", blob, fileName);
            $.ajax({
                url: 'api/upload/img',
                type: 'POST',
                data: formData,
                success: function(returndata) {
                    console.log("上传成功")
                    formData = null;
                }
            })
    
            /**** 下载图片 ****/
            var a = document.createElement('a');
            a.setAttribute('download', fileName);
            a.href = url;
            a.click();
        }
    

      

    compressImage方法:

    /**
     * compressImage.js
     * 参数config:{ input, callback, name, type, quality, size, width, height}
     * input: 必填,input[type=file]的表单元素,支持multiple多张图片
     * callback: 必填,处理之后的回调函数,参数(blob,fileName)
     * name: 非必填,自定义文件名,不包含后缀(如.jpg),默认原文件名
     * type: 非必填,图片格式,可选png/jpg,默认原图片格式
     * quality: 非必填,图片质量系数,默认0.92
     * 只传size: 压缩图片至size(单位kb)大小
     * 传 根据宽度压缩图片,高度自适应
     * 传height: 根据高度压缩图片,宽度自适应
     * 传width和height: 压缩图片,从中心位置裁取
     * 不传size/width/height: 只进行格式转换,不压缩图片
     * 同时传size和width/height: 会忽略size,根据width/height处理
     **/
    
    function compressImage(config) {
        if (!config.input || !config.input.files || config.input.files.length == 0) {
            console.log("compressImage: 无图片文件")
            return;
        }
        if (!config.callback) {
            console.log("compressImage: 缺少回调函数")
            return;
        }
        if (config.type && config.type != "png" && config.type != "jpg") {
            console.log("compressImage: 图片格式指定错误,请选择png或jpg")
            return;
        }
        config.quality = (config.quality && config.quality > 0 && config.quality <= 1) ? config.quality : 0.92;
        for (var i = 0; i < config.input.files.length; i++) {
            HANDLE_SINGLE_IMAGE(config.input.files[i], config)
        }
    }
    
    function HANDLE_SINGLE_IMAGE(file, config) {
        var idx = file.name.lastIndexOf(".");
        var imageName = file.name.substring(0, idx);
        var imageType = file.name.substring(idx + 1, file.name.length).toLowerCase();
        if (imageType != "png" && imageType != "jpg" && imageType != "jpeg") {
            console.log("compressImage: 不支持的图片格式 - " + imageType)
        } else {
    
            // fileType: canvas.toBlob方法的参数
            config.fileType = (!!config.type ? ("image/" + config.type.replace("jpg", "jpeg")) : file.type);
    
            // type: 文件名中的格式后缀
            config.type = config.type || imageType.replace("jpeg", "jpg");
    
            // fileName: 完整的文件名,将在callback中返回
            config.fileName = (config.name ? (config.name + "." + config.type) : (imageName + "." + config.type));
    
            // ios下的jpg文件需要修正照片方向
            var isIOS = (/iphone|ipad|mac/).test(window.navigator.userAgent.toLowerCase());
            if (isIOS && file.type == "image/jpeg") {
                var reader = new FileReader();
                reader.readAsArrayBuffer(file);
                reader.onload = function() {
                    var orientation = GET_ORIENTATION(this.result);
                    alert(orientation)
                    IMAGE_READER(file, config, orientation)
                }
            } else {
                IMAGE_READER(file, config)
            }
        }
    }
    
    function IMAGE_READER(file, config, orientation) {
        var reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function() {
            var img = document.createElement("img");
            img.src = this.result;
            img.onload = function() {
                if(orientation==6 || orientation==8){
                    var origin_width = parseInt(this.width);
                    this.width = parseInt(this.height);
                    this.height = origin_width;
                }else{
                    this.width = parseInt(this.width);
                    this.height = parseInt(this.height);
                }
    
                // 标记是否只按照size要求去压缩
                var bySize = false;
    
                // 缩放后图片的尺寸,canvas将从中裁切
                var imgWidth = 0;
                var imgHeight = 0;
    
                // 目标尺寸,即最后生成的图片尺寸
                var targetWidth = 0;
                var targetHeight = 0;
    
                // config有width/height时
                if (config.width && config.height) {
                    targetWidth = config.width;
                    targetHeight = config.height;
                    var ratio_x = this.width / targetWidth;
                    var ratio_y = this.height / targetHeight;
                    if (ratio_x > ratio_y) {
                        imgWidth = this.width / ratio_y;
                        imgHeight = targetHeight;
                    } else {
                        imgWidth = targetWidth;
                        imgHeight = this.height / ratio_x;
                    }
                }
                if (config.width && !config.height) {
                    imgWidth = targetWidth = config.width;
                    imgHeight = targetHeight = targetWidth / (this.width / this.height);
                }
                if (!config.width && config.height) {
                    imgHeight = targetHeight = config.height;
                    imgWidth = targetWidth = (this.width / this.height) * targetHeight;
                }
                if (targetWidth == 0 && targetHeight == 0) {
                    // config有size时,根据大小进行压缩
                    if (config.size && config.size > 0 && file.size > config.size * 1024) {
                        bySize = true;
                        var ratio = Math.sqrt((config.size * 1024) / file.size).toFixed(2);
                        if (ratio < 0.5) {
                            ratio = 0.5;
                        }
                        imgWidth = targetWidth = parseInt(this.width * ratio);
                        imgHeight = targetHeight = parseInt(this.height * ratio);
                    } else {
                        // 不压缩或者裁切,只将图片转为blob数据
                        imgWidth = targetWidth = this.width;
                        imgHeight = targetHeight = this.height;
                    }
                } else {
                    targetWidth = parseInt(targetWidth);
                    targetHeight = parseInt(targetHeight);
                }
                var canvas = document.createElement('canvas');
                var ctx = canvas.getContext('2d');
                canvas.width = targetWidth;
                canvas.height = targetHeight;
                
                // 矫正旋转方向
                switch (orientation) {
                    case 3:
                        ctx.rotate(180 * Math.PI / 180);
                        ctx.drawImage(this, (imgWidth-targetWidth)/2-imgWidth, (imgHeight-targetHeight)/2-imgHeight, imgWidth, imgHeight );
                        break;
                    case 6:
                        ctx.rotate(90 * Math.PI / 180);
                        ctx.drawImage(this, (targetHeight-imgHeight)/2, (imgWidth-targetWidth)/2-imgWidth, imgHeight , imgWidth);
                        break;
                    case 8:
                        ctx.rotate(270 * Math.PI / 180);
                        ctx.drawImage(this, (imgHeight-targetHeight)/2-imgHeight, (targetWidth-imgWidth)/2, imgHeight , imgWidth);
                        break;
                    default:
                        ctx.drawImage(this, (targetWidth-imgWidth)/2, (targetHeight-imgHeight)/2, imgWidth, imgHeight);
                }
    
                canvas.toBlob(function(blob) {
                    if (bySize && blob.size >= config.size * 1024) {
                        COMPRESS_BY_SIZE(blob, config, canvas, ctx)
                        return;
                    }
                    config.callback(blob, config.fileName)
                }, config.fileType, config.quality);
            }
        }
    }
    
    //将图片按0.9倍缩小至目标size
    function COMPRESS_BY_SIZE(old_blob, config, canvas, ctx) {
        console.log("COMPRESS_BY_SIZE")
        config.quality = 0.98;
        var reader = new FileReader();
        reader.readAsDataURL(old_blob);
        reader.onload = function() {
            var img = document.createElement("img");
            img.src = this.result;
            img.onload = function() {
                width = parseInt(img.width * 0.9);
                height = parseInt(img.height * 0.9);
                canvas.width = width;
                canvas.height = height;
                ctx.drawImage(img, 0, 0, width, height);
                canvas.toBlob(function(blob) {
                    if (blob.size >= config.size * 1024) {
                        COMPRESS_BY_SIZE(blob, config, canvas, ctx);
                        return;
                    }
                    config.callback(blob, config.fileName)
                }, config.fileType, config.quality);
            }
        }
    }
    
    /**
     * 获取iOS照片的旋转角度
     * 1-0° 3-180° 6-90° 8-270°
     **/
    function GET_ORIENTATION(arrayBuffer) {
        var dataView = new DataView(arrayBuffer);
        var length = dataView.byteLength;
        var orientation = 0;
        var exifIDCode;
        var tiffOffset;
        var firstIFDOffset;
        var littleEndian;
        var endianness;
        var app1Start;
        var ifdStart;
        var offset;
        var i;
        if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
            offset = 2;
            while (offset < length) {
                if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
                    app1Start = offset;
                    break;
                }
                offset++;
            }
        }
        if (app1Start) {
            exifIDCode = app1Start + 4;
            tiffOffset = app1Start + 10;
            if (GET_CHARCODE_STRING(dataView, exifIDCode, 4) === 'Exif') {
                endianness = dataView.getUint16(tiffOffset);
                littleEndian = endianness === 0x4949;
                if (littleEndian || endianness === 0x4D4D) {
                    if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
                        firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
                        if (firstIFDOffset >= 0x00000008) {
                            ifdStart = tiffOffset + firstIFDOffset;
                        }
                    }
                }
            }
        }
        if (ifdStart) {
            length = dataView.getUint16(ifdStart, littleEndian);
            for (i = 0; i < length; i++) {
                offset = ifdStart + i * 12 + 2;
                if (dataView.getUint16(offset, littleEndian) === 0x0112) {
                    offset += 8;
                    orientation = dataView.getUint16(offset, littleEndian);
                    break;
                }
            }
        }
        return orientation;
    }
    
    function GET_CHARCODE_STRING(dataView, start, length) {
        var str = '';
        var i;
        for (i = start, length += start; i < length; i++) {
            str += String.fromCharCode(dataView.getUint8(i));
        }
        return str;
    }
  • 相关阅读:
    java基础梳理--朝花夕拾(三)
    java基础梳理--朝花夕拾(二)
    [C++] 分治法之棋盘覆盖、循环赛日程表
    [C++] 递归之全排列问题、半数集
    蓝桥杯 算法训练 ALGO-143 字符串变换
    蓝桥杯 算法训练 ALGO-129 特殊的数字四十
    蓝桥杯 算法训练 ALGO-126 水仙花
    蓝桥杯 算法训练 ALGO-122 未名湖边的烦恼
    蓝桥杯 算法训练 ALGO-121 猴子分苹果
    蓝桥杯 算法训练 ALGO-116 最大的算式
  • 原文地址:https://www.cnblogs.com/yangshifu/p/12690130.html
Copyright © 2011-2022 走看看