├── assets └── cut.png ├── README.md ├── pages └── test.vue ├── components ├── Doing.vue ├── Images.vue └── Uploader.vue └── utils └── imageUtil.js /assets/cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yywang95/h5ImageUploader/HEAD/assets/cut.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # h5ImageUploader 2 | 一个具有图片旋转、压缩、下载功能的h5上传组件 3 | 4 | # 功能说明 5 | - 项目基于vue 6 | - 可用于pc端、h5 7 | - 当上传图片的时候会根据limitsize属性对图片进行压缩 8 | - ios拍照会出现图片旋转的情况,该组件对这种旋转情况进行了修复: https://blog.csdn.net/xiaoermingn/article/details/94398621 9 | - 可对已上传的图片进行下载 10 | 11 | # 样式截图 12 |  13 | 14 | `注意`:仅上传部分代码,不可直接运行,请根据自己项目的接口进行参考~ 15 | -------------------------------------------------------------------------------- /pages/test.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /components/Doing.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{text}} 6 | 7 | 8 | 9 | 26 | 59 | -------------------------------------------------------------------------------- /utils/imageUtil.js: -------------------------------------------------------------------------------- 1 | // 该文件存放图片或canvas相关处理的常用方法 2 | import Exif from 'exif-js'; 3 | 4 | /** 5 | * @desc 获取图片信息,使用exif.js库,具体用法请在github中搜索 6 | * @param {Object} file 上传的图片文件 7 | * @param {String} tag 需要获取的信息 例如:'Orientation'旋转信息 8 | * @return {Promise} 读取是个异步操作,返回指定的图片信息 9 | */ 10 | export const getOrientation = (file, tag) => { 11 | if (!file) return 0; 12 | return new Promise((resolve, reject) => { 13 | /* eslint-disable func-names */ 14 | // 箭头函数会修改this,所以这里不能用箭头函数 15 | Exif.getData(file, function () { 16 | const o = Exif.getTag(this, tag); 17 | resolve(o); 18 | }); 19 | }); 20 | }; 21 | 22 | /** 23 | * @desc 将base64的图片转为文件流 24 | * @param {String} dataUrl base64数据 25 | * @return {Object} 文件流 26 | */ 27 | export const dataURLtoFile = (dataUrl) => { 28 | const filename = `img${Date.now()}`; 29 | const arr = dataUrl.split(','); 30 | const mime = arr[0].match(/:(.*?);/)[1]; 31 | const bstr = atob(arr[1]); 32 | let n = bstr.length; 33 | const u8arr = new Uint8Array(n); 34 | while (n--) { 35 | u8arr[n] = bstr.charCodeAt(n); 36 | } 37 | return new File([u8arr], filename, { type: mime }); 38 | }; 39 | 40 | /** 41 | * @desc 旋转canvas,会对源数据canvas进行修改 42 | * @param {Object} img 图片文件 43 | * @param {String} dir 方向 left逆时针|right顺时针 44 | * @param {Object} canvas 画布 45 | * @param {Number} s 向指定方向旋转几步,1步为90度 46 | */ 47 | export const rotateImg = (img, dir = 'right', canvas, s = 1) => { 48 | const MIN_STEP = 0; 49 | const MAX_STEP = 3; 50 | 51 | const width = canvas.width || img.width; 52 | const height = canvas.height || img.height; 53 | let step = 0; 54 | 55 | if (dir === 'right') { 56 | step += s; 57 | step > MAX_STEP && (step = MIN_STEP); 58 | } else { 59 | step -= s; 60 | step < MIN_STEP && (step = MAX_STEP); 61 | } 62 | 63 | const degree = step * 90 * Math.PI / 180; 64 | const ctx = canvas.getContext('2d'); 65 | 66 | switch (step) { 67 | case 1: 68 | canvas.width = height; 69 | canvas.height = width; 70 | ctx.rotate(degree); 71 | ctx.drawImage(img, 0, -height, width, height); 72 | break; 73 | case 2: 74 | canvas.width = width; 75 | canvas.height = height; 76 | ctx.rotate(degree); 77 | ctx.drawImage(img, -width, -height, width, height); 78 | break; 79 | case 3: 80 | canvas.width = height; 81 | canvas.height = width; 82 | ctx.rotate(degree); 83 | ctx.drawImage(img, -width, 0, width, height); 84 | break; 85 | default: 86 | canvas.width = width; 87 | canvas.height = height; 88 | ctx.drawImage(img, 0, 0, width, height); 89 | break; 90 | } 91 | }; 92 | 93 | /** 94 | * @desc 压缩canvas,会对源数据canvas进行修改 95 | * @param {Object} img 图片文件 96 | * @param {Object} canvas 画布上下文 97 | * @param {Number} limitsize 限制的大小 98 | * @return {Object} 新大小的宽高,因为如果后续还要处理canvas要使用新的宽高才行{width, height} 99 | */ 100 | export const compressImg = (img, canvas, limitsize) => { 101 | if (!limitsize || limitsize <= 0) return; 102 | const ctx = canvas.getContext('2d'); 103 | 104 | // 原始图片信息 105 | const imgSize = img.src.length; 106 | const width = img.width; 107 | const height = img.height; 108 | 109 | // 压缩比, 保留两位小数 110 | const ratio = parseFloat(limitsize / imgSize).toFixed(2); 111 | const ratioWidth = ~~(width * ratio); // 新图片宽度 112 | const ratioHeight = ~~(height * ratio); // 新图片高度 113 | 114 | canvas.width = ratioWidth; 115 | canvas.height = ratioHeight; 116 | ctx.drawImage(img, 0, 0, ratioWidth, ratioHeight); 117 | }; 118 | -------------------------------------------------------------------------------- /components/Images.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 点击添加图片 23 | 24 | 25 | 26 | {{items.length}}/{{limit}} 27 | 28 | 29 | 127 | 219 | -------------------------------------------------------------------------------- /components/Uploader.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 224 | 232 | --------------------------------------------------------------------------------