前端图片最优化压缩方案 |
您所在的位置:网站首页 › norizc照片 › 前端图片最优化压缩方案 |
前言
上传图片/视频/文件是我们经常会遇到的问题,但是一旦图片过大就会导致不好的操作体验。 图片上传是前端中常见的的业务场景。无论是前台还是后台,适当的对图片进行压缩处理, 可以显著的提升用户体验。而在后台管理系统中,图片压缩不仅仅能够提升后台管理员操作体验,更是可以防止后台设置过大的图片导致前台图片加载过久,从而影响用户体验。 关于压缩图片 思考想想压缩图片基本流程 input 读取到 文件 ,使用 FileReader 将其转换为 base64 编码 新建 img ,使其 src 指向刚刚的 base64 新建 canvas ,将 img 画到 canvas 上 利用 canvas.toDataURL/toBlob 将 canvas 导出为 base64 或 Blob 将 base64 或 Blob 转化为 File 将这些步骤逐个拆解,我们会发现似乎在canvas.toDataURL时涉及到图片质量,那咱们就从这里下手。 准备 HTMLCanvasElement.toDataURL()HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。 如果画布的高度或宽度是0,那么会返回字符串“data:,”。 如果传入的类型非“image/png”,但是返回的值以“data:image/png”开头,那么该传入的类型是不支持的。 语法canvas.toDataURL(type, encoderOptions); 参数 type 可选 图片格式,默认为 image/png encoderOptions 可选 在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。 Chrome支持“image/webp”类型。 猜想可能toDataURL(type,quality)的第二个参数(quality)越小,文件体积越小 实践先看下原图信息大小是7.31kb toDataURL(type,quality)quality默认0.92看看压缩结果如何 复制代码 let fileId = document.getElementById('fileInput') let img = document.getElementById('img') fileId.onchange = function (e) { let file = e.target.files[0] compressImg(file, 0.92).then(res => {//compressImg方法见附录 console.log(res) img.src = window.URL.createObjectURL(res.file); }) } 复制代码compressImg方法见附录
看来起初的猜想不完全正确,咱们继续看看。 fileId.onchange = function (e) { let file = e.target.files[0] compressImg(file, 0.1).then(res => {//compressImg方法见附录 console.log(res) img.src = window.URL.createObjectURL(res.file); }) } 复制代码
继续......A long time later 咱们用折线图来看吧更直观
所以说默认值得到的图片往往比原图大 下面看看当quality为多少时对图片的压缩效率可以最大化 压缩效率最大化,即:在不影响图片质量的情况下最大化压缩 尝试了一系列的图片之后我发现 当quality在0.2~0.5之间,图片质量变化并不大,quality的值越小,压缩效率越可观(也就是在0.2左右时,压缩图片可以最大化,同时并不对图片质量造成太大影响) 结论经过实践,可以得出结论这个默认值得到的图片往往比原图的图片质量要高。 当quality在0.2~0.5之间,图片质量变化并不大,quality的值越小,压缩效率越可观(也就是在0.2左右时,压缩图片可以最大化,同时并不对图片质量造成太大影响) 附录 /** * 压缩方法 * @param {string} file 文件 * @param {Number} quality 0~1之间 */ function compressImg(file, quality) { if (file[0]) { return Promise.all(Array.from(file).map(e => compressImg(e, quality))) // 如果是 file 数组返回 Promise 数组 } else { return new Promise((resolve) => { const reader = new FileReader() // 创建 FileReader reader.onload = ({ target: { result: src } }) => { const image = new Image() // 创建 img 元素 image.onload = async () => { const canvas = document.createElement('canvas') // 创建 canvas 元素 canvas.width = image.width canvas.height = image.height canvas.getContext('2d').drawImage(image, 0, 0, image.width, image.height) // 绘制 canvas const canvasURL = canvas.toDataURL('image/jpeg', quality) const buffer = atob(canvasURL.split(',')[1]) let length = buffer.length const bufferArray = new Uint8Array(new ArrayBuffer(length)) while (length--) { bufferArray[length] = buffer.charCodeAt(length) } const miniFile = new File([bufferArray], file.name, { type: 'image/jpeg' }) resolve({ file: miniFile, origin: file, beforeSrc: src, afterSrc: canvasURL, beforeKB: Number((file.size / 1024).toFixed(2)), afterKB: Number((miniFile.size / 1024).toFixed(2)) }) } image.src = src } reader.readAsDataURL(file) }) } } 复制代码 优化升级使用过程中发现边界问题,即图片尺寸过大、 IOS 尺寸限制,png 透明图变黑等问题 所以优化了大尺寸图片等比例缩小,这极大的提高了压缩效率 举个栗子,以一张大小14M,尺寸6016X4016的图片为例 一个14M的原图(6016X4016)不改变质量只改变尺寸的前提下压缩后(1400X935)就剩139.62KB 可想而知,尺寸的限制能最大化提高压缩效率 最近很多小伙伴在问我有没有TS版本的,今天刚好弄了下这一块,稍微调整了下,相较于之前更加简洁,性能也快了不少;如果需要对尺寸压缩,请自行添加尺寸处理方法,也很简单,自行添加吧,方法写的很简洁明了了,就不多加赘述了,大家自取吧。 const fileToDataURL = (file: Blob): Promise => { return new Promise((resolve) => { const reader = new FileReader() reader.onloadend = (e) => resolve((e.target as FileReader).result) reader.readAsDataURL(file) }) } const dataURLToImage = (dataURL: string): Promise => { return new Promise((resolve) => { const img = new Image() img.onload = () => resolve(img) img.src = dataURL }) } const canvastoFile = (canvas: HTMLCanvasElement, type: string, quality: number): Promise => { return new Promise((resolve) => canvas.toBlob((blob) => resolve(blob), type, quality)) } /** * 图片压缩方法 * @param {Object} file 图片文件 * @param {String} type 想压缩成的文件类型 * @param {Nubmber} quality 压缩质量参数 * @returns 压缩后的新图片 */ export const compressionFile = async(file, type = 'image/jpeg', quality = 0.5) => { const fileName = file.name const canvas = document.createElement('canvas') const context = canvas.getContext('2d') as CanvasRenderingContext2D const base64 = await fileToDataURL(file) const img = await dataURLToImage(base64) canvas.width = img.width canvas.height = img.height context.clearRect(0, 0, img.width, img.height) context.drawImage(img, 0, 0, img.width, img.height) const blob = (await canvastoFile(canvas, type, quality)) as Blob // quality:0.5可根据实际情况计算 const newFile = await new File([blob], fileName, { type: type }) return newFile } 复制代码 写在最后我是凉城a,一个前端,热爱技术也热爱生活。 与你相逢,我很开心。 如果你想了解更多,请点这里,期待你的小⭐⭐ 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊 本文首发于掘金,未经许可禁止转载💌 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |