JavaScript 图片处理
- 简介
简介
使用javascript封装了 文件读取、base64格式图片数据转换、图片压缩、图片截取等功能
/* 图片压缩处理 */
/**
* 文件读取
* FileReader 的实例拥有 4 个方法,其中 3 个用以读取文件,另一个用来中断读取。下面的表格列出了这些方法以及他们的参数和功能,需要注意的是 ,无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result属性中。
* 方法名 参数 描述
* abort none 中断读取
* readAsBinaryString file 将文件读取为二进制码
* readAsDataURL file 将文件读取为 DataURL, 若是图片的话,读取格式为 base64格式
* readAsText file, [encoding] 将文件读取为文本
*
* FileReader 包含了一套完整的事件模型,用于捕获读取文件时的状态。
* 事件 描述
* onabort 中断时触发
* onerror 出错时触发
* onload 文件读取成功完成时触发
* onloadend 读取完成触发,无论成功或失败
* onloadstart 读取开始时触发
* onprogress 读取中
*
* @param file 文件
* @param type binary、data、text
* @param encoding 字符编码方式, type = text 时可选
* @return {Promise<{size, data}>}
*/
function fileReader(file, type, encoding = 'UTF-8') {
return new Promise((resolve, reject) => {
if(!window.FileReader) {
return reject('FileReader is undefined')
}else if (Object.prototype.toString.call(file) !== '[object File]'){
return reject('not object File')
}
const reader = new FileReader()
reader.onloadend = (e) => {
resolve({
size: e.total,
data: e.target.result
})
}
reader.onerror = reject
if (type === 'binary') {
reader.readAsBinaryString(file)
} else if (type === 'data') {
reader.readAsDataURL(file)
} else if (type === 'text') {
reader.readAsText(file, encoding)
} else {
reject('not support type')
}
})
}
/**
* 读取文件,返回图片对象, 直接 append 到 document
* @param file 图片文件
* @return {Promise<Image>}
*/
function toImage(file) {
return new Promise((resolve, reject) => {
if (Object.prototype.toString.call(file) !== '[object File]'){
return reject('not object File')
}
const image = new Image()
const URL = window.webkitURL || window.URL
if (URL) {
const url = URL.createObjectURL(file)
image.onload = () => {
resolve(image)
URL.revokeObjectURL(url)
}
image.src = url;
image.onerror = reject
} else {
fileReader(file, 'data').then(res => {
image.src = res.data
resolve(image)
}).catch(reject)
}
})
}
/**
*
* 利用 drawImage() 方法将 Image 对象绘画在 Canvas 对象上。
*
* drawImage 有三种语法形式:
* void ctx.drawImage(image, dx, dy);
* void ctx.drawImage(image, dx, dy, dWidth, dHeight);
* void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
* 参数:
* image 绘制到上下文的元素;
* sx 绘制选择框左上角以 Image 为基准 X 轴坐标;
* sy 绘制选择框左上角以 Image 为基准 Y 轴坐标;
* sWidth 绘制选择框宽度;
* sHeight 绘制选择框宽度;
* dx Image 的左上角在目标 canvas 上 X 轴坐标;
* dy Image 的左上角在目标 canvas 上 Y 轴坐标;
* dWidth Image 在目标 canvas 上绘制的宽度;
* dHeight Image 在目标 canvas 上绘制的高度;
*
* @param image
* @return {Promise<unknown>}
*/
function toCanvas(image) {
return new Promise((resolve, reject) => {
if (Object.prototype.toString.call(image) !== '[object HTMLImageElement]'){
return reject('not Object HTMLImageElement')
}
try {
Object.prototype.toString.call(new Image())
const canvas = document.createElement('canvas');
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
resolve(canvas)
}catch (e){
reject(e)
}
})
}
/**
* 将画布转换成图片格式
*
* @param resolve 数据回调函数
* @param canvas 画布
* @param quality 精度 在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92,其他参数会被忽略。
* @param type 图片类型
* @return {string}
*/
function toDataUrl(resolve, canvas, quality = 1, type = 'image/jpeg') {
_checkCbCanvas(resolve, canvas)
resolve(canvas.toDataURL(type, quality));
}
function _checkCbCanvas(resolve, canvas){
if (Object.prototype.toString.call(resolve) !== '[object Function]') {
throw 'resolve not a function'
} else if(Object.prototype.toString.call(canvas) !== '[object HTMLCanvasElement]') {
throw 'canvas not object HTMLCanvasElement'
}
}
/**
* 转换成二进制对象
* @param resolve 数据回调函数
* @param canvas
* @param quality
* @param type
*/
function toBlob(resolve, canvas, quality = 1, type = 'image/jpeg') {
_checkCbCanvas(resolve, canvas)
canvas.toBlob(resolve, type, quality);
}
/**
* 压缩图片 - 压缩清晰度,不压缩图片尺寸
* @param image 压缩的图片
* @param quality 精度
* @return {Promise<Base64 image>}
*/
function compressImage(image, quality){
return compressImageRatio(image, 0, quality)
}
/**
* 压缩图片 - 指定宽度,等比压缩
* @param image 图片
* @param width 指定宽度 指定宽度, 等比压缩。 = 0 时表示不要宿
* @param quality 精度
* @param toCb 结果转换函数, 默认转换成 base64
* @return {Promise<unknown>}
*/
function compressImageRatio(image, width, quality = 0.9, toCb = toDataUrl){
return new Promise((resolve, reject) => {
if (Object.prototype.toString.call(image) !== '[object HTMLImageElement]'){
return reject('not Object HTMLImageElement')
}
/* 计算图片宽高 */
let { naturalWidth, naturalHeight } = image
if (width && width > 0 && width < naturalWidth){
naturalHeight *= (width / naturalWidth)
/* 等比计算 */
naturalWidth = width
}
/* 创建画布 */
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
/* 设置画布宽高*/
canvas.width = naturalWidth
canvas.height = naturalHeight
/* 图片绘制到画布 */
context.drawImage(image, 0, 0, canvas.width, canvas.height)
toCb(resolve, canvas, quality, image.type)
})
}
/**
* 图片裁剪
* @param image 图片
* @param imgX 裁剪开始 x 坐标
* @param imgY 裁剪开始 Y 坐标
* @param width 图标图片宽度
* @param height 图标图片高度
* @param toCb 数据格式转换函数
* @return {Promise<unknown>}
*/
function cutImg(image, imgX, imgY, width, height, toCb = toDataUrl){
return new Promise((resolve, reject) => {
if (Object.prototype.toString.call(image) !== '[object HTMLImageElement]'){
return reject('not Object HTMLImageElement')
}
let { naturalWidth, naturalHeight } = image
width = Math.min(naturalWidth - imgX, width)
height = Math.min(naturalHeight - imgY, height)
if (imgX > naturalWidth || imgY > naturalHeight){
return reject('x or y out of range')
}
/* 创建画布 */
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
/* 设置画布宽高*/
canvas.width = width
canvas.height = height
const zero = 0
/* 图片绘制到画布 */
context.drawImage(image
/* 图片的xy坐标, 宽高 */
, imgX, imgY, width, height
/* 画布的xy坐标, 宽高 */
, zero, zero, canvas.width, canvas.height)
/* void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); */
toCb(resolve, canvas, 1, image.type)
})
}
/**
* 将file格式的图片压缩,然后转换成file对象
* @param file 文件
* @param width 指定宽度, 等比压缩。 = 0 时表示不要宿
* @param quality 精度 默认 0.9
* @return {Promise<File>}
*/
function imgTo(file, width, quality){
return new Promise((resolve, reject) => {
toImage(file).catch(reject).then(img => {
compressImageRatio(img, width, quality, toBlob).catch(reject).then(data => {
let hiFile = new File([ data ], file.name, { type: data.type })
resolve(hiFile)
})
})
})
}
/**
* 图片压缩成 icon
* @param file
* @return {Promise<File>}
*/
function toIconSize(file){
return imgTo(file, 150, 0.8)
}
/**
* 图片压缩成 icon
* @param file
* @return {Promise<File>}
*/
function toHeadSize(file){
return imgTo(file, 400, 0.8)
}
export {
fileReader
, toImage
, toCanvas
, toDataUrl
, toBlob
, compressImage
, compressImageRatio
, cutImg
, imgTo
, toHeadSize
, toIconSize
}