封装一个图片压缩方法

前言

因公司项目数据涉及到的各类封面实景等图片,原始图片有的几兆甚至几十兆大小,故给后台管理系统上传图片组件增加了可设置质量、宽度、高度三个维度的参数,来校准合适的图片大小和效果。

先附上demo: KICompress

image.png

可指定宽高质量,如不传则默认原图指标,宽高设置其一,则按等比例放大缩小。

实现步骤

虽然不是啥深层次的东西,但感觉还是蛮实用的,就包装了一下 (~ ̄▽ ̄)~

创建方法

先封装一下压缩图片的方法compressImg(),接收一个参数对象,对象有五个属性分别为fileObj图片quality质量width宽度height高度callback回调函数
先实例化一个image对象,然后设置src属性,监听其加载成功或失败事件。

function compressImg (res = {}) {
    try {
        // 创建image对象
        const image = new Image()
        // 这里做了个判断,可以直接传入图片地址。
        if (typeof(res.fileObj) === "object") {
          image.src = window.URL.createObjectURL(res.fileObj)
        } else {
          image.src = res.fileObj
        }: res.fileObj
        image.onload = function() {
            // ...
        }
        image.onerror = function() {
          res.callback(false) // 失败
        }
    } catch () {
        res.callback(false) // 失败
    }
}
复制代码

宽高设置

当图片加载完成后,先保存图片的原始宽高,如果有分别指定宽和高,直接使用参数覆盖。
如果只设置了宽,那么则以指定的宽度为基准,乘原始宽高的比例,获取等比例的高度,反之亦然。

    image.onload = function() {
        let w = this.width // 当前图片宽
        let h = this.height // 当前图片高
        if (res.width && res.height) { // 如果宽高都指定
            w = res.width
            h = res.height
        } else if (res.width || res.height) { // 如果指定其一
            if (res.width > res.height) {
              h = res.width * (h / w)
              w = res.width
            } else {
              w = res.height * (w / h)
              h = res.height
            }
        }
        // ...
    }
复制代码

图片生成

图片宽高确定后,通过document.createElement('canvas')创建一个canvas画布,getContext()获取绘图环境,setAttributeNode()设置绘图区域宽高属性,然后使用drawImage()方法将图片插入到canvas画布中,最后通过toDataURL()方法将canvas转换为base64编码,到这里拿到base64基本已经完成功能了,可根据个人需求将其转换成不同的文件流格式。

    image.onload = function() {
        // ...
        const canvas = document.createElement('canvas') // 创建一个canvas
        const ctx = canvas.getContext('2d')  // 获取绘图环境
        // 创建属性节点
        const anw = document.createAttribute('width')
        anw.nodeValue = w
        const anh = document.createAttribute('height')
        anh.nodeValue = h
        canvas.setAttributeNode(anw)
        canvas.setAttributeNode(anh)
        // 设置图片
        ctx.drawImage(this, 0, 0, w, h)
        // canvas转换字节流并设置质量
        const data = canvas.toDataURL('image/jpeg', parseFloat(res.quality))
        // base64转换为Blob对象
        const newFile = convertBase64UrlToBlob(data)
        // 执行回调
        res.callback(newFile)
    }
复制代码

格式转换

我们要将图片转换成文件对象上传到服务器,封装了convertBase64UrlToBlob()方法将base64转换为Blob对象,Blob对象可以通过new window.File()转换为file文件对象,方便以fromData格式提交。

function convertBase64UrlToBlob(dataurl) {
    const bytes = window.atob(dataurl.split(',')[1]) // 去掉url的头,并转换为byte
    // 处理异常,将ascii码小于0的转换为大于0
    const ab = new ArrayBuffer(bytes.length)
    const ia = new Uint8Array(ab)
    for (let i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i)
    }
    return new Blob([ab], { type: 'image/png' })
}
复制代码

使用

方法封装完成后可以开始调用了,先准备几个要使用的HTML标签。
通过input.file标签选择图片,监听change事件在获取到图片后使用window.URL.createObjectURL()将图片对象转为浏览器临时路径,先把原图展示出来,并保存原图对象。

<html>
    <div class="compress-img">
        <img src="" id="img">
        <input type="file" accept="image/*">
    </div>
</html>
<script>
    let file = null // 文件对象
    let imgInput = document.querySelector(".compress-img input") // 获取input标签
    // 绑定change事件
    imgInput.addEventListener('change', function(e) {
        file = e.target.files[0] // 保存图片对象
        img.src = window.URL.createObjectURL(file); // 转换浏览器临时路径
    })
</script>
复制代码

添加输入框设置要指定的图片宽高及质量,不设置等于保持原有指标,然后添加执行按钮。

<html>
    <div class="compress-btn">
        <span>图片宽度:</span>
        <input type="number" placeholder="图片宽度" id="widthNode">
        <span>图片高度:</span>
        <input type="number" placeholder="图片高度" id="heightNode">
        <span>图片质量:</span>
        <input type="text" placeholder="图片质量 1 ~ 0" id="qualityNode"> 
        <br>
        <br>
        <span class="btn-item" onclick="qualityClick()">压缩图片</span>
        <span class="btn-item" onclick="empty()">清空</span>
    </div>
</html>
<script>
    // 压缩图片
    function qualityClick () {
        // ...
    }
    
    // 清除图片列表
    function empty(e) {
        // ...
    }
</script>
复制代码

执行调用compressImg()方法,并将生成新的图片插入到页面上进行效果展示。

<html>
    <div id="fileList">
    </div>
</html>
<script>
    // 压缩图片
    compressImg({
        fileObj: file, // 图片对象
        width: widthNode.value, // 图片宽度
        height: heightNode.value, // 图片高度
        quality: qualityNode.value, // 图片质量
        callback: function (val) {
            if (val) {
                // 裁剪成功,将Blob转换为file对象
                let newfile = new window.File([val], file.name, { type: file.type })
                // 插入到页面上
                fileList.innerHTML =  `
                    <div class="list-item">
                      <div class="list-img">
                        <img src="${window.URL.createObjectURL(newfile)}">
                      </div>
                      <div class="list-tips">
                        <div>图片名称:${newfile.name}</div>
                        <div>图片大小:${sizeInit(newfile.size) }</div>
                      </div>
                    </div>` + fileList.innerHTML
            } else {
              // 裁剪失败
              alert("裁剪失败!")
            }
        }
    })
</script>
复制代码

效果

未处理的图片

image.png

设置一半的质量

image.png

设置宽度,高度等比例变化(等比例情况下图片不会出现拉伸问题)

image.png

设置指定宽高(原始宽高是2481*3508)

image.png

最后

已经很久没用过DOM,BOM操作了

一个人竟然可以在没有钱没有事业的同时,还没有时间。

猜你喜欢

转载自juejin.im/post/7109389978982940709