按照大小压缩图片,或者按照特定分辨率裁切图片,转为blob数据。自动处理ios中可能存在的照片偏差90°问题。

例如,获取300*300大小的头像,实现以下效果:

使用方式:

<!-- 引入js文件 -->
<script type="text/javascript" src="./compressImage.js"></script>
<!-- input标签 -->
<input type="file" id="avatar" name="avatar" accept="image/png, image/jpeg">

如果想通过npn引入,请参考 git说明 。

裁取特定分辨率的图片(如300*300):

    compressImage({
input: document.getElementById('avatar'),
width: 300,
height: 300,
callback: function(blob, fileName) {
// blob是处理之后的图片二进制数据
// fileName是文件名,如"avatar.png"
// ...
}
})

  

将图片压缩到指定大小,如500kb以下:

    compressImage({
input: document.getElementById('avatar'),
size: 500,
callback: function(blob, fileName) {
// ...
}
})

  

指定图片名字和格式(允许jpg和png格式互转):

    compressImage({
input: document.getElementById('avatar'),
size: 500,
name: "user_avatar_1024687956"
type: 'png',
callback: function(blob, fileName) {
// 此处得到的fileName就是 user_avatar_1024687956.png
// ...
}
})

  

callback是图片处理之后的回调函数,图片会转为blob数据数据,blob的用法参考:

    callback: function(blob, fileName) {
var url = URL.createObjectURL(blob); /**** 通过<img>显示 ****/
var img = document.createElement("img");
img.src = url;
document.body.append(img); /**** formData上传图片 ****/
var formData = new FormData();
formData.append("file", blob, fileName);
$.ajax({
url: 'api/upload/img',
type: 'POST',
data: formData,
success: function(returndata) {
console.log("上传成功")
formData = null;
}
}) /**** 下载图片 ****/
var a = document.createElement('a');
a.setAttribute('download', fileName);
a.href = url;
a.click();
}

  

compressImage方法:

/**
* compressImage.js
* 参数config:{ input, callback, name, type, quality, size, width, height}
* input: 必填,input[type=file]的表单元素,支持multiple多张图片
* callback: 必填,处理之后的回调函数,参数(blob,fileName)
* name: 非必填,自定义文件名,不包含后缀(如.jpg),默认原文件名
* type: 非必填,图片格式,可选png/jpg,默认原图片格式
* quality: 非必填,图片质量系数,默认0.92
* 只传size: 压缩图片至size(单位kb)大小
* 传width: 根据宽度压缩图片,高度自适应
* 传height: 根据高度压缩图片,宽度自适应
* 传width和height: 压缩图片,从中心位置裁取
* 不传size/width/height: 只进行格式转换,不压缩图片
* 同时传size和width/height: 会忽略size,根据width/height处理
**/ function compressImage(config) {
if (!config.input || !config.input.files || config.input.files.length == 0) {
console.log("compressImage: 无图片文件")
return;
}
if (!config.callback) {
console.log("compressImage: 缺少回调函数")
return;
}
if (config.type && config.type != "png" && config.type != "jpg") {
console.log("compressImage: 图片格式指定错误,请选择png或jpg")
return;
}
config.quality = (config.quality && config.quality > 0 && config.quality <= 1) ? config.quality : 0.92;
for (var i = 0; i < config.input.files.length; i++) {
HANDLE_SINGLE_IMAGE(config.input.files[i], config)
}
} function HANDLE_SINGLE_IMAGE(file, config) {
var idx = file.name.lastIndexOf(".");
var imageName = file.name.substring(0, idx);
var imageType = file.name.substring(idx + 1, file.name.length).toLowerCase();
if (imageType != "png" && imageType != "jpg" && imageType != "jpeg") {
console.log("compressImage: 不支持的图片格式 - " + imageType)
} else { // fileType: canvas.toBlob方法的参数
config.fileType = (!!config.type ? ("image/" + config.type.replace("jpg", "jpeg")) : file.type); // type: 文件名中的格式后缀
config.type = config.type || imageType.replace("jpeg", "jpg"); // fileName: 完整的文件名,将在callback中返回
config.fileName = (config.name ? (config.name + "." + config.type) : (imageName + "." + config.type)); // ios下的jpg文件需要修正照片方向
var isIOS = (/iphone|ipad|mac/).test(window.navigator.userAgent.toLowerCase());
if (isIOS && file.type == "image/jpeg") {
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function() {
var orientation = GET_ORIENTATION(this.result);
alert(orientation)
IMAGE_READER(file, config, orientation)
}
} else {
IMAGE_READER(file, config)
}
}
} function IMAGE_READER(file, config, orientation) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {
var img = document.createElement("img");
img.src = this.result;
img.onload = function() {
if(orientation==6 || orientation==8){
var origin_width = parseInt(this.width);
this.width = parseInt(this.height);
this.height = origin_width;
}else{
this.width = parseInt(this.width);
this.height = parseInt(this.height);
} // 标记是否只按照size要求去压缩
var bySize = false; // 缩放后图片的尺寸,canvas将从中裁切
var imgWidth = 0;
var imgHeight = 0; // 目标尺寸,即最后生成的图片尺寸
var targetWidth = 0;
var targetHeight = 0; // config有width/height时
if (config.width && config.height) {
targetWidth = config.width;
targetHeight = config.height;
var ratio_x = this.width / targetWidth;
var ratio_y = this.height / targetHeight;
if (ratio_x > ratio_y) {
imgWidth = this.width / ratio_y;
imgHeight = targetHeight;
} else {
imgWidth = targetWidth;
imgHeight = this.height / ratio_x;
}
}
if (config.width && !config.height) {
imgWidth = targetWidth = config.width;
imgHeight = targetHeight = targetWidth / (this.width / this.height);
}
if (!config.width && config.height) {
imgHeight = targetHeight = config.height;
imgWidth = targetWidth = (this.width / this.height) * targetHeight;
}
if (targetWidth == 0 && targetHeight == 0) {
// config有size时,根据大小进行压缩
if (config.size && config.size > 0 && file.size > config.size * 1024) {
bySize = true;
var ratio = Math.sqrt((config.size * 1024) / file.size).toFixed(2);
if (ratio < 0.5) {
ratio = 0.5;
}
imgWidth = targetWidth = parseInt(this.width * ratio);
imgHeight = targetHeight = parseInt(this.height * ratio);
} else {
// 不压缩或者裁切,只将图片转为blob数据
imgWidth = targetWidth = this.width;
imgHeight = targetHeight = this.height;
}
} else {
targetWidth = parseInt(targetWidth);
targetHeight = parseInt(targetHeight);
}
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = targetWidth;
canvas.height = targetHeight; // 矫正旋转方向
switch (orientation) {
case 3:
ctx.rotate(180 * Math.PI / 180);
ctx.drawImage(this, (imgWidth-targetWidth)/2-imgWidth, (imgHeight-targetHeight)/2-imgHeight, imgWidth, imgHeight );
break;
case 6:
ctx.rotate(90 * Math.PI / 180);
ctx.drawImage(this, (targetHeight-imgHeight)/2, (imgWidth-targetWidth)/2-imgWidth, imgHeight , imgWidth);
break;
case 8:
ctx.rotate(270 * Math.PI / 180);
ctx.drawImage(this, (imgHeight-targetHeight)/2-imgHeight, (targetWidth-imgWidth)/2, imgHeight , imgWidth);
break;
default:
ctx.drawImage(this, (targetWidth-imgWidth)/2, (targetHeight-imgHeight)/2, imgWidth, imgHeight);
} canvas.toBlob(function(blob) {
if (bySize && blob.size >= config.size * 1024) {
COMPRESS_BY_SIZE(blob, config, canvas, ctx)
return;
}
config.callback(blob, config.fileName)
}, config.fileType, config.quality);
}
}
} //将图片按0.9倍缩小至目标size
function COMPRESS_BY_SIZE(old_blob, config, canvas, ctx) {
console.log("COMPRESS_BY_SIZE")
config.quality = 0.98;
var reader = new FileReader();
reader.readAsDataURL(old_blob);
reader.onload = function() {
var img = document.createElement("img");
img.src = this.result;
img.onload = function() {
width = parseInt(img.width * 0.9);
height = parseInt(img.height * 0.9);
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(function(blob) {
if (blob.size >= config.size * 1024) {
COMPRESS_BY_SIZE(blob, config, canvas, ctx);
return;
}
config.callback(blob, config.fileName)
}, config.fileType, config.quality);
}
}
} /**
* 获取iOS照片的旋转角度
* 1-0° 3-180° 6-90° 8-270°
**/
function GET_ORIENTATION(arrayBuffer) {
var dataView = new DataView(arrayBuffer);
var length = dataView.byteLength;
var orientation = 0;
var exifIDCode;
var tiffOffset;
var firstIFDOffset;
var littleEndian;
var endianness;
var app1Start;
var ifdStart;
var offset;
var i;
if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
offset = 2;
while (offset < length) {
if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
app1Start = offset;
break;
}
offset++;
}
}
if (app1Start) {
exifIDCode = app1Start + 4;
tiffOffset = app1Start + 10;
if (GET_CHARCODE_STRING(dataView, exifIDCode, 4) === 'Exif') {
endianness = dataView.getUint16(tiffOffset);
littleEndian = endianness === 0x4949;
if (littleEndian || endianness === 0x4D4D) {
if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
if (firstIFDOffset >= 0x00000008) {
ifdStart = tiffOffset + firstIFDOffset;
}
}
}
}
}
if (ifdStart) {
length = dataView.getUint16(ifdStart, littleEndian);
for (i = 0; i < length; i++) {
offset = ifdStart + i * 12 + 2;
if (dataView.getUint16(offset, littleEndian) === 0x0112) {
offset += 8;
orientation = dataView.getUint16(offset, littleEndian);
break;
}
}
}
return orientation;
} function GET_CHARCODE_STRING(dataView, start, length) {
var str = '';
var i;
for (i = start, length += start; i < length; i++) {
str += String.fromCharCode(dataView.getUint8(i));
}
return str;
}

canvas压缩、裁切图片和格式转换的方法的更多相关文章

  1. Vue directive自定义指令+canvas实现H5图片压缩上传-Base64格式

    前言 最近优化项目-手机拍照图片太大,回显速度比较慢,使用了vue的自定义指令实现H5压缩上传base64格式的图片 canvas自定义指令 Vue.directive("canvas&qu ...

  2. 前端图片canvas,file,blob,DataURL等格式转换

    将file转化成base64 方法一:利用URL.createObjectURL() <!DOCTYPE html> <html> <head> <title ...

  3. 利用canvas压缩图片

    现在手机拍的照片动不动就是几M,当用户上传手机里的照片时一个消耗流量大,一个上传时间长,为了解决这个问题,就需要压缩图片: 想法:利用canvas重绘图片,保持宽高比不变,具体宽高根本具体情况而定. ...

  4. js移动端/H5同时选择多张图片上传并使用canvas压缩图片

    最近在做一个H5的项目,里边涉及到拍照上传图片的功能以及识别图片的功能,这里对识别图片的功能不做赘述,不属本文范畴.我在做完并上线项目后,同事跟我提了一个要求是可不可以同时选择多张图片上传,我做的时候 ...

  5. vue实现PC端调用摄像头拍照人脸录入、移动端调用手机前置摄像头人脸录入、及图片旋转矫正、压缩上传base64格式/文件格式

    进入正题 1. PC端调用摄像头拍照上传base64格式到后台,这个没什么花里胡哨的骚操作,直接看代码 (canvas + video) <template> <div> &l ...

  6. 【VC++技术杂谈007】使用GDI+进行图片格式转换

    本文主要介绍如何使用GDI+对图片进行格式转换,可以转换的图片格式为bmp.jpg.png. 1.加载GDI+库 GDI+是GDI图形库的一个增强版本,提供了一系列Visual C++ API.为了使 ...

  7. CANVAS运用-对图片的压缩上传(仅针对移动浏览器)

    最近在移动端设计头像上传功能时,原本是以<input type="file">直接通过formData上传,然而实际使用情况是:对于过大的图片(高像素手机所拍摄的照片等 ...

  8. 前台图片Canvas压缩上传小结

    需求来源:之前有个提交审核表单的业务,表单中含有大量附件图片,大约有20多张吧,为了省事,采用的同步上传,一次需要上传很多照片,本来单张图片限制为200KB,这样子总图片大小约为5MB左右,想想也可以 ...

  9. vue上传图片 base64+canvas压缩图片

    这是先将图片 base64转码 在拿canvas压缩的

随机推荐

  1. postgresql自增字段初始值的设定

    在实际开发中会有这样的需求,想要自己设置表中自增字段的初始值. 比如:有一个your_table表中有一个自增字段id,我们知道,插入数据后,默认是从1开始自增的. 但是假如现在有一个需求,是要求id ...

  2. 机器学习 - LSTM应用之情感分析

    1. 概述 在情感分析的应用领域,例如判断某一句话是positive或者是negative的案例中,咱们可以通过传统的standard neuro network来作为解决方案,但是传统的神经网络在应 ...

  3. session、cookie和taken的区别

    http是无状态的协议,所以要维持应用的会话形式,就需要加入以下几种机制,来进行会话跟踪,识别用户身份(当同一用户进行多次操作,不用反复请求建立新的连接,从而节省服务器资源和处理速度)   生成位置 ...

  4. 基于.NetCore3.1搭建项目系列 —— 使用Swagger做Api文档 (上篇)

    前言 为什么在开发中,接口文档越来越成为前后端开发人员沟通的枢纽呢? 随着业务的发张,项目越来越多,而对于支撑整个项目架构体系而言,我们对系统业务的水平拆分,垂直分层,让业务系统更加清晰,从而产生一系 ...

  5. HTML每日学习笔记(3)

    7.17.2019 XHTML 1.XHTML与HTML的区别: XHTML 元素必须被正确地嵌套. XHTML 元素必须被关闭. 标签名必须用小写字母. XHTML 文档必须拥有根元素.(所有的 X ...

  6. Jenkins+Ant+JMeter报告自动化

    1.参考Jenkins+Ant+JMeter集成,安装Jenkins(不需要安装Performance Plugin插件),建立Slave节点,连接Slave节点,创建任务等. 2.将Jenkins+ ...

  7. 我的第一个html静态网页

    <!doctype html> <html>     <head>         <title>王兆国的个人博客</title>      ...

  8. Android ListView显示访问WebServices返回的JSON结果

    1.WebServices的返回结果 2.ListView内容布局代码 <?xml version="1.0" encoding="utf-8"?> ...

  9. 一文洞悉JVM内存管理机制

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习导图: 一.为什么要学习内存管理? Java与C++之间有一堵由内存动态分配和垃圾回收机制所围成的高墙,墙 ...

  10. css3实现旋转卡片

    基本思路:父div使用相对定位包裹着两个子元素,子元素使用绝对定位,定位在同一个位置,初始时一个div翻转到后面隐藏,另一个在前面显示,当鼠标悬停在父元素上时,前面的子元素旋转180度,到背面隐藏:背 ...