JS对图片尺寸和DPI进行编辑修改(1寸照修改为2寸照)

各种报名都对照片有大小限制,鉴于这种情况,网上搜了后拼凑出了如下代码,用于解决1寸照片修改为2寸照片,同时将DPI修改为300,当然也可以根据自己的情况修改代码:

HTML

html 复制代码
<input type="file" id="input" accept="image/*">
<div style="display: grid;grid-template-columns: 1fr 5px 1fr;">
    <div>
        <p>修改前:</p>
        <img id="before-image" src="" style="display: block; max-width:100%;" >
    </div>
    <div></div>
    <div>
        <p>修改后(点击图片下载):</p>
        <a href="javascript:;" download><img id="after-image" src="" style="display: block;"></a>
    </div>
</div>

JS

javascript 复制代码
<script type="text/javascript">
    const after_image = document.getElementById('before-image');
    document.getElementById('input').addEventListener('change', (e) => {
        const reader = new FileReader();
        reader.readAsDataURL(e.target.files[0]);
        reader.onload = (e) => {
            b64 = e.target.result;
            after_image.src = b64;
            //设置目标图片大小
            var target_width = 413;
            var target_height = 626;
            //计算目标图片宽高比例
            var target_wh_scale = target_width / target_height;

            //定义一个Image对象
            var bitmap = new Image();
            bitmap.src = b64;
            bitmap.onload = function () {
                //
                var cut_width = 0;
                var cut_height = 0;
                //
                var bitmap_wh_scale = bitmap.width / bitmap.height;
                if (bitmap_wh_scale > target_wh_scale) {
                    cut_width = bitmap.width - bitmap.width / (bitmap_wh_scale / target_wh_scale);
                }//裁剪宽度
                else if (bitmap_wh_scale < target_wh_scale) {
                    cut_height = bitmap.height - bitmap.height * (bitmap_wh_scale / target_wh_scale);

                } else {
                    Console.log("比例一致无需裁剪");
                }
                console.log("图片裁剪宽度:" + cut_width + " px");
                console.log("图片裁剪高度:" + cut_height + " px");



                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                canvas.width = target_width;
                canvas.height = target_height;
                ctx.drawImage(this, cut_width / 2, cut_height / 2, bitmap.width - cut_width, bitmap.height - cut_height, 0, 0, target_width, target_height);

                var after_img = document.getElementById('after-image');
                after_img.src = canvas.toDataURL('image/jpg');

                var dataUrl = canvas.toDataURL('image/jpeg', 0.9);
                //修改DPI为300
                downloadBase64Img(after_img.parentElement, changeDpiDataUrl(dataUrl, 300), "t.jpg");
            }

        };
    })

    function downloadBase64Img(a, base64URL, fileName) {
        // 将 a 标签的 download 属性设置为要下载的文件名
        a.download = fileName || 'image';
        // 创建 Blob 对象,并获取 base64 数据的 MIME 类型
        const mimeType = base64URL.match(/:(.*?);/)[1];
        // 将 base64 数据转换为字节数组
        const byteCharacters = atob(base64URL.split(',')[1]);
        const byteNumbers = new Array(byteCharacters.length);
        // 将字节数组填充到 Uint8Array 中
        for (let i = 0; i < byteCharacters.length; i++) {
            byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        // 创建 Blob 对象
        const blob = new Blob([byteArray], { type: mimeType });
        // 将 Blob 对象的 URL 赋值给 a 标签的 href 属性
        a.href = URL.createObjectURL(blob);
    }

    function changeDpiDataUrl(base64Image, dpi) {
        const PNG = 'image/png';
        const JPEG = 'image/jpeg';
        const dataSplitted = base64Image.split(',');
        const format = dataSplitted[0];
        const body = dataSplitted[1];
        let type;
        let headerLength;
        let overwritepHYs = false;
        if (format.indexOf(PNG) !== -1) {
            type = PNG;
            const b64Index = detectPhysChunkFromDataUrl(body);
            // 28 bytes in dataUrl are 21bytes, length of phys chunk with everything inside.
            if (b64Index >= 0) {
                headerLength = Math.ceil((b64Index + 28) / 3) * 4;
                overwritepHYs = true;
            } else {
                headerLength = 33 / 3 * 4;
            }
        }
        if (format.indexOf(JPEG) !== -1) {
            type = JPEG;
            headerLength = 18 / 3 * 4;
        }
        // 33 bytes are ok for pngs and jpegs
        // to contain the information.
        const stringHeader = body.substring(0, headerLength);
        const restOfData = body.substring(headerLength);
        const headerBytes = atob(stringHeader);
        const dataArray = new Uint8Array(headerBytes.length);
        for (let i = 0; i < dataArray.length; i++) {
            dataArray[i] = headerBytes.charCodeAt(i);
        }
        const finalArray = changeDpiOnArray(dataArray, dpi, type, overwritepHYs);
        const base64Header = btoa(String.fromCharCode(...finalArray));
        return [format, ',', base64Header, restOfData].join('');
    }

    function changeDpiOnArray(dataArray, dpi, format, overwritepHYs) {
        const PNG = 'image/png';
        const JPEG = 'image/jpeg';

        if (format === JPEG) {
            dataArray[13] = 1; // 1 pixel per inch or 2 pixel per cm
            dataArray[14] = dpi >> 8; // dpiX high byte
            dataArray[15] = dpi & 0xff; // dpiX low byte
            dataArray[16] = dpi >> 8; // dpiY high byte
            dataArray[17] = dpi & 0xff; // dpiY low byte
            return dataArray;
        }
        if (format === PNG) {
            const physChunk = new Uint8Array(13);
            // chunk header pHYs
            // 9 bytes of data
            // 4 bytes of crc
            // this multiplication is because the standard is dpi per meter.

            const _P = 'p'.charCodeAt(0);
            const _H = 'H'.charCodeAt(0);
            const _Y = 'Y'.charCodeAt(0);
            const _S = 's'.charCodeAt(0);
            dpi *= 39.3701;
            physChunk[0] = _P;
            physChunk[1] = _H;
            physChunk[2] = _Y;
            physChunk[3] = _S;
            physChunk[4] = dpi >>> 24; // dpiX highest byte
            physChunk[5] = dpi >>> 16; // dpiX veryhigh byte
            physChunk[6] = dpi >>> 8; // dpiX high byte
            physChunk[7] = dpi & 0xff; // dpiX low byte
            physChunk[8] = physChunk[4]; // dpiY highest byte
            physChunk[9] = physChunk[5]; // dpiY veryhigh byte
            physChunk[10] = physChunk[6]; // dpiY high byte
            physChunk[11] = physChunk[7]; // dpiY low byte
            physChunk[12] = 1; // dot per meter....

            const crc = calcCrc(physChunk);

            const crcChunk = new Uint8Array(4);
            crcChunk[0] = crc >>> 24;
            crcChunk[1] = crc >>> 16;
            crcChunk[2] = crc >>> 8;
            crcChunk[3] = crc & 0xff;

            if (overwritepHYs) {
                const startingIndex = searchStartOfPhys(dataArray);
                dataArray.set(physChunk, startingIndex);
                dataArray.set(crcChunk, startingIndex + 13);
                return dataArray;
            } else {
                // i need to give back an array of data that is divisible by 3 so that
                // dataurl encoding gives me integers, for luck this chunk is 17 + 4 = 21
                // if it was we could add a text chunk contaning some info, untill desired
                // length is met.

                // chunk structur 4 bytes for length is 9
                const chunkLength = new Uint8Array(4);
                chunkLength[0] = 0;
                chunkLength[1] = 0;
                chunkLength[2] = 0;
                chunkLength[3] = 9;

                const finalHeader = new Uint8Array(54);
                finalHeader.set(dataArray, 0);
                finalHeader.set(chunkLength, 33);
                finalHeader.set(physChunk, 37);
                finalHeader.set(crcChunk, 50);
                return finalHeader;
            }
        }
    }
</script>

以上代码中changeDpiDataUrl()changeDpiOnArray()两个函数来源于开源项目changeDPI

相关推荐
lv程序媛17 分钟前
el-table表头前几列固定,后面几列根据接口返回的值不同展示不同
javascript·vue.js·elementui
ZwaterZ17 分钟前
el-table-column自动生成序号&&在序号前插入图标
前端·javascript·c#·vue
数据小爬虫@38 分钟前
利用Python爬虫获取淘宝店铺详情
开发语言·爬虫·python
蒟蒻的贤1 小时前
vue学习11.21
javascript·vue.js·学习
高 朗1 小时前
【GO基础学习】基础语法(2)切片slice
开发语言·学习·golang·slice
寒笙LED1 小时前
C++详细笔记(六)string库
开发语言·c++·笔记
IT书架1 小时前
golang面试题
开发语言·后端·golang
初遇你时动了情1 小时前
uniapp 城市选择插件
开发语言·javascript·uni-app
zongzi_4942 小时前
二次封装的天气时间日历选择组件
开发语言·javascript·ecmascript
kikyo哎哟喂3 小时前
Java 代理模式详解
java·开发语言·代理模式