JavaScript實(shí)現(xiàn)壓縮圖片至指定大小
完整代碼
yaSuoImg.js
/**
* 加載圖片
* @param {URL} url 圖片地址
* @returns {Promise.<Image>}
*/
const loadImageAsync = (url) => {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = url;
image.onload = function () {
resolve(image);
};
image.onerror = function (err) {
reject(new Error("出錯(cuò) " + err));
};
});
}
/**
* 圖片壓縮
* @param {File} file 拾取的圖片文件File
* @param {Number} [maxSizeKB=300] 超過maxSizeKB啟動(dòng)壓縮(KB)
* @param {String} [imgType="webp"] 壓縮后的圖片格式,推薦webp,既無(wú)損壓縮
* @param {Number} [opy=.9] 清晰度 0-1 推薦0.9,既不失真,又可壓縮圖片內(nèi)存
* @return {Promise.<Base64URLString>}
*/
export const compress = async (file, maxSizeKB = 300, imgType = 'webp', opy = .9) => {
const canvas = document.createElement("canvas"),
context = canvas.getContext("2d"),
image = await loadImageAsync(URL.createObjectURL(file)),
{ height, width } = image;
if (file.size > maxSizeKB * 1024) {
let rate = 0; // 壓縮率
const fileSizeKB = file.size / 1024; // 文件大小KB, file.size給的是字節(jié)Byte
if (fileSizeKB > maxSizeKB) { // 當(dāng)圖片大小超標(biāo),才進(jìn)行壓縮
rate = (fileSizeKB - maxSizeKB) / fileSizeKB; // 計(jì)算壓縮率
}
// 糾正因子,不加會(huì)導(dǎo)致壓縮出的文件太小
const factor = 0,
cvWidth = width * (1 - rate + factor),
cvHeight = height * (1 - rate + factor);
canvas.height = cvHeight;
canvas.width = cvWidth;
context.clearRect(0, 0, cvWidth, cvHeight);
context.drawImage(image, 0, 0, cvWidth, cvHeight);
} else {
canvas.height = height;
canvas.width = width;
opy = .7
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
}
URL.revokeObjectURL(file)//釋放內(nèi)存
return canvas.toDataURL(`image/${imgType}`, opy);
}
具體使用
import { compress } from "/yaSuoImg.js";
const imgBlobSrc = await compress(file);
方法補(bǔ)充
1.JS將base64圖片壓縮至指定大小
在開發(fā)中,通常在上傳圖片時(shí),由于各種限制,需要將上傳的圖片壓縮到某一大小范圍內(nèi)才能上傳。在此提供以下方法實(shí)現(xiàn)該需求,復(fù)制可用。
壓縮圖片原理
通過原生的input標(biāo)簽?zāi)玫揭蟼鞯膱D片文件
將圖片文件file對(duì)象轉(zhuǎn)化成圖片base64
根據(jù)圖片base64創(chuàng)建img圖片對(duì)象
在canvas上繪制該HTMLImageElement
通過不斷降低圖片質(zhì)量指數(shù)來(lái)達(dá)到壓縮圖片大小的目的
封裝
將以上代碼封裝為一個(gè)js文件,這里命名為compressImg.js,放置在utils文件夾下
/**
* 壓縮圖片到指定大小
* @param baseImg base64圖片
* @param maxSize 單位kb
*/
export function compressImgBySize (baseImg, maxSize = 200) {
return new Promise((resolve) => {
// 計(jì)算圖片大小
const strLength = baseImg.length
const fileLength = parseInt(strLength - (strLength / 8) * 2)
let size = parseInt((fileLength / 1024).toFixed(2))
// 判斷圖片是否符合指定大小要求
if (size <= maxSize) {
resolve(baseImg)
return
}
// 創(chuàng)建image對(duì)象
const img = new Image()
if (baseImg.indexOf('data:image/') !== -1) {
img.src = baseImg
} else {
img.src = 'data:image/jpeg;base64,' + baseImg
}
img.onload = () => {
// 把image對(duì)象 轉(zhuǎn)換為 canvas對(duì)象
const canvas = imgToCanvas(img)
let resUrl = ''
// 圖片質(zhì)量,范圍:0 ~ 1
let quality = 0.9
// 當(dāng)圖片大小大于指定maxSize,圖片質(zhì)量大于0時(shí)繼續(xù)通過降低圖片資料來(lái)壓縮
while (size > maxSize && quality > 0) {
// 在canvas上繪制該HTMLImageElement,得到圖片base64
resUrl = canvas.toDataURL('image/jpeg', quality).replace(/^data:image\/\w+;base64,/, '')
const resLength = resUrl.length
// 計(jì)算繪制出的base64圖片大小
const resFileLength = parseInt(resLength - (resLength / 8) * 2)
size = parseInt((resFileLength / 1024).toFixed(2))
// 降低圖片質(zhì)量
quality = (quality - 0.1).toFixed(1)
}
resolve(resUrl)
}
img.onerror = () => {
resolve(baseImg)
}
})
}
// 把image 轉(zhuǎn)換為 canvas對(duì)象
export function imgToCanvas (image) {
var canvas = document.createElement('canvas')
canvas.width = image.width
canvas.height = image.height
canvas.getContext('2d').drawImage(image, 0, 0)
return canvas
}
在需要使用圖片壓縮的.vue文件中引入并使用,如下
// 注意引入路徑是否正確
import { compressImgBySize } from '@/utils/compressImg'
// this.applyFile[0].content 為指定的base64格式照片
// 1200 指將圖片壓縮到1200kb大小以下
compressImgBySize('file文件對(duì)象', 1200).then(baseImg => {
// 輸出圖片base64
console.log(baseImg)
})
2.js壓縮圖片到指定大小
代碼如下:
import React from 'react';
import PropTypes from 'prop-types';
import styles from './upload.less';
import compress from './compress';
class Upload extends React.Component {
constructor(props) {
super(props);
this.fileInput = React.createRef();
this.state = {
fileObjs: [], // item { originFile, compressBase64, compressFile }
};
}
getFileUrl(file) {
let url;
const agent = navigator.userAgent;
if (agent.indexOf('MSIE') >= 1) {
url = file.value;
} else if (agent.indexOf('Firefox') > 0 || agent.indexOf('Chrome') > 0) {
url = window.URL.createObjectURL(file);
}
return url;
}
compressCallBack(file, fileObj, result) {
const { fileObjs } = this.state;
file.compressing = false; // 壓縮完成
fileObj.compressBase64 = result.compressBase64;
fileObj.compressFile = result.compressFile;
this.setState({ fileObjs: [...fileObjs] });
if (fileObjs.length && fileObjs.every(fileObjItem => fileObjItem.compressBase64)) {
console.log('全部壓縮完成', fileObjs);
}
}
onInputChange(e) {
const { fileObjs } = this.state;
Object.keys(e.target.files).forEach((key) => {
const file = e.target.files[key];
// 驗(yàn)證圖片格式
const type = file.name.split('.')[1];
if (type !== 'png' && type !== 'jpg' && type !== 'jpeg') {
console.warn('請(qǐng)上傳png,jpg,jpeg格式的圖片!');
e.target.value = '';
return;
}
file.url = this.getFileUrl(file);
file.compressing = true; // 壓縮狀態(tài),開始?jí)嚎s
const fileObj = { originFile: file, compressBase64: null, compressFile: null };
fileObjs.push(fileObj);
// 壓縮圖片的方法, maxSize單位為kb
compress(file, 200).then((res) => {
this.compressCallBack(file, fileObj, res);
}, (err) => {
// 壓縮失敗,則返回原圖片的信息
this.compressCallBack(file, fileObj, err);
});
});
this.setState({ fileObjs: [...fileObjs] });
e.target.value = '';
}
render() {
const { fileObjs } = this.state;
return (
<div
className={styles.uploadContainer}
>
<div className={styles.gridItem}>
<div
className={styles.inputContainer}
onClick={() => {
this.fileInput.current.click();
}}
>
<span className={styles.uploadIcon}>+</span>
<input
className={styles.fileInput}
ref={this.fileInput}
type="file"
name="file"
multiple="multiple"
accept="image/*"
onChange={e => this.onInputChange(e)}
/>
</div>
</div>
{
fileObjs.map(fileObj => (
<div className={styles.gridItem}>
<img
src={fileObj.compressBase64 ? fileObj.compressBase64 : fileObj.originFile.url}
className={fileObj.originFile.compressing && styles.filter}
/>
{
fileObj.originFile.compressing ?
<div className={styles.progressContainer}>
<div className={styles.progress}>
<div className={styles.progressHighlight} />
</div>
</div> : ''
}
</div>
))
}
</div>);
}
}
Upload.propTypes = {
dispatch: PropTypes.func.isRequired,
};
export default Upload;圖片壓縮主要代碼compress.js
// 將File(Blob)對(duì)象轉(zhuǎn)變?yōu)橐粋€(gè)dataURL字符串, 即base64格式
const fileToDataURL = file => new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = e => resolve(e.target.result);
reader.readAsDataURL(file);
});
// 將dataURL字符串轉(zhuǎn)變?yōu)閕mage對(duì)象,即base64轉(zhuǎn)img對(duì)象
const dataURLToImage = dataURL => new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(img);
img.src = dataURL;
});
// 將一個(gè)canvas對(duì)象轉(zhuǎn)變?yōu)橐粋€(gè)File(Blob)對(duì)象
const canvastoFile = (canvas, type, quality) => new Promise(resolve => canvas.toBlob(blob => resolve(blob), type, quality));
const compress = (originfile, maxSize) => new Promise(async (resolve, reject) => {
const originSize = originfile.size / 1024; // 單位為kb
console.log('圖片指定最大尺寸為', maxSize, '原始尺寸為:', originSize);
// 將原圖片轉(zhuǎn)換成base64
const base64 = await fileToDataURL(originfile);
// 縮放圖片需要的canvas
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
// 小于maxSize,則不需要壓縮,直接返回
if (originSize < maxSize) {
resolve({ compressBase64: base64, compressFile: originfile });
console.log(`圖片小于指定大小:${maxSize}KB,不用壓縮`);
return;
}
const img = await dataURLToImage(base64);
const scale = 1;
const originWidth = img.width;
const originHeight = img.height;
const targetWidth = originWidth * scale;
const targetHeight = originHeight * scale;
canvas.width = targetWidth;
canvas.height = targetHeight;
context.clearRect(0, 0, targetWidth, targetHeight);
context.drawImage(img, 0, 0, targetWidth, targetHeight);
// 將Canvas對(duì)象轉(zhuǎn)變?yōu)閐ataURL字符串,即壓縮后圖片的base64格式
// const compressedBase64 = canvas.toDataURL('image/jpeg', 0.1);
// 經(jīng)過我的對(duì)比,通過scale控制圖片的拉伸來(lái)壓縮圖片,能夠壓縮jpg,png等格式的圖片
// 通過canvastoFile方法傳遞quality來(lái)壓縮圖片,只能壓縮jpeg類型的圖片,png等格式不支持
// scale的壓縮效果沒有canvastoFile好
// 在壓縮到指定大小時(shí),通過scale壓縮的圖片比通過quality壓縮的圖片模糊的多
// 壓縮的思路,用二分法找最佳的壓縮點(diǎn)
// 這里為了規(guī)避浮點(diǎn)數(shù)計(jì)算的弊端,將quality轉(zhuǎn)為整數(shù)再計(jì)算;
// const preQuality = 100;
const maxQualitySize = { quality: 100, size: Number.MAX_SAFE_INTEGER };
const minQualitySize = { quality: 0, size: 0 };
let quality = 100;
let count = 0; // 壓縮次數(shù)
let compressFinish = false; // 壓縮完成
let invalidDesc = '';
let compressBlob = null;
// 二分法最多嘗試8次即可覆蓋全部可能
while (!compressFinish && count < 12) {
compressBlob = await canvastoFile(canvas, 'image/jpeg', quality / 100);
const compressSize = compressBlob.size / 1024;
count++;
if (compressSize === maxSize) {
console.log(`壓縮完成,總共壓縮了${count}次`);
compressFinish = true;
return;
}
if (compressSize > maxSize) {
maxQualitySize.quality = quality;
maxQualitySize.size = compressSize;
}
if (compressSize < maxSize) {
minQualitySize.quality = quality;
minQualitySize.size = compressSize;
}
console.log(`第${count}次壓縮,壓縮后大小${compressSize},quality參數(shù):${quality}`);
quality = Math.ceil((maxQualitySize.quality + minQualitySize.quality) / 2);
if (maxQualitySize.quality - minQualitySize.quality < 2) {
if (!minQualitySize.size && quality) {
quality = minQualitySize.quality;
} else if (!minQualitySize.size && !quality) {
compressFinish = true;
invalidDesc = '壓縮失敗,無(wú)法壓縮到指定大小';
console.log(`壓縮完成,總共壓縮了${count}次`);
} else if (minQualitySize.size > maxSize) {
compressFinish = true;
invalidDesc = '壓縮失敗,無(wú)法壓縮到指定大小';
console.log(`壓縮完成,總共壓縮了${count}次`);
} else {
console.log(`壓縮完成,總共壓縮了${count}次`);
compressFinish = true;
quality = minQualitySize.quality;
}
}
}
if (invalidDesc) {
// 壓縮失敗,則返回原始圖片的信息
console.log(`壓縮失敗,無(wú)法壓縮到指定大?。?{maxSize}KB`);
reject({ msg: invalidDesc, compressBase64: base64, compressFile: originfile });
return;
}
compressBlob = await canvastoFile(canvas, 'image/jpeg', quality / 100);
const compressSize = compressBlob.size / 1024;
console.log(`最后一次壓縮(即第${count + 1}次),quality為:${quality},大小:${compressSize}`);
const compressedBase64 = await fileToDataURL(compressBlob);
const compressedFile = new File([compressBlob], originfile.name, { type: 'image/jpeg' });
resolve({ compressFile: compressedFile, compressBase64: compressedBase64 });
});
export default compress;到此這篇關(guān)于JavaScript實(shí)現(xiàn)壓縮圖片至指定大小的文章就介紹到這了,更多相關(guān)JavaScript壓縮圖片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript實(shí)現(xiàn)數(shù)組扁平化六種技巧總結(jié)
這篇文章主要為大家詳細(xì)介紹了六種javascript中實(shí)現(xiàn)數(shù)組扁平化的技巧,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下2023-12-12
javaScript遍歷對(duì)象和數(shù)組的方法總結(jié)
這篇文章介紹了javaScript遍歷對(duì)象和數(shù)組的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
文本框只能輸入數(shù)字的實(shí)現(xiàn)方法(兼容IE火狐)
簡(jiǎn)單實(shí)現(xiàn)JS對(duì)dom操作封裝
非阻塞動(dòng)態(tài)加載javascript廣告實(shí)現(xiàn)代碼
JavaScript實(shí)現(xiàn)簡(jiǎn)單日期特效

