mx-compress.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <view class="mx-compress-container">
  3. <!-- #ifndef H5 -->
  4. <canvas :style="{width: W + 'px', height: H + 'px', visibility: 'hidden'}" style="left:-9000px;top:-9000px;" class="canvas" canvas-id="canvas"></canvas>
  5. <!-- #endif -->
  6. </view>
  7. </template>
  8. <script>
  9. export default {
  10. data() {
  11. return {
  12. W: '',
  13. H: '',
  14. canvas: null,
  15. ctx: null,
  16. maxLongSize: 1280, //长边最大长度
  17. quality: 0.8, //压缩质量,0.1-1.0
  18. base64: false, //是否base64
  19. waitDrawTime: 1500, //等待绘图完成时间,用于解决微信小程序真机调试时图片压缩不完整
  20. showLoading: '正在压缩..', //提示信息
  21. mask: true
  22. }
  23. },
  24. methods: {
  25. async compress(args, options = {}) {
  26. return new Promise(async (resolve, reject) => {
  27. let files;
  28. if (arguments[0].tempFiles || arguments[0].tempFilePaths) {
  29. files = arguments[0].tempFilePaths || arguments[0].tempFiles;
  30. };
  31. if (arguments[0].files) {
  32. files = arguments[0].files;
  33. }
  34. if (!files instanceof Array) {
  35. reject('数据格式错误');
  36. }
  37. if (!files.length) {
  38. reject('数据不能为空');
  39. }
  40. this.maxLongSize = options.maxLongSize || 1280;
  41. this.quality = options.quality || 0.8;
  42. this.base64 = options.base64 || false;
  43. this.waitDrawTime = options.waitDrawTime || 1500;
  44. this.showLoading = options.showLoading === true ? '正在压缩..' : typeof options.showLoading === 'string' ? options.showLoading : false;
  45. this.mask = options.mask || true;
  46. if (this.showLoading) {
  47. uni.showLoading({
  48. title: this.showLoading,
  49. mask: this.mask,
  50. })
  51. }
  52. try {
  53. const result = await this.doCompress(files);
  54. resolve(result);
  55. uni.hideLoading();
  56. } catch (error) {
  57. reject(error);
  58. uni.hideLoading();
  59. }
  60. })
  61. },
  62. async doCompress(files) {
  63. // #ifdef H5
  64. if (typeof files[0] === 'object') {
  65. let result = await this.toBase64H5(files);
  66. return await this.compressImageH5(result);
  67. }
  68. if (typeof files[0] === 'string') {
  69. let result = files.map(item => {
  70. return {
  71. base64: item
  72. }
  73. });
  74. return await this.compressImageH5(result);
  75. }
  76. return [];
  77. // #endif
  78. // #ifndef H5
  79. if (typeof files[0] === 'string') {
  80. const result = await this.getImgInfoWX(files);
  81. return await this.compressImageWX(result);
  82. }
  83. if (typeof files[0] === 'object') {
  84. files = files.map(item => {
  85. return item.path
  86. });
  87. const result = await this.getImgInfoWX(files);
  88. return await this.compressImageWX(result);
  89. }
  90. return [];
  91. // #endif
  92. return [];
  93. },
  94. compressImageH5(base64Result) {
  95. let result = [];
  96. return new Promise(async (resolve, reject) => {
  97. for (let i = 0; i < base64Result.length; i++) {
  98. let res = await this.compressResultH5(base64Result[i]);
  99. result.push(res);
  100. if (result.length === base64Result.length) {
  101. resolve(result);
  102. this.canvas = null;
  103. }
  104. }
  105. })
  106. },
  107. compressResultH5(base64Item) {
  108. return new Promise((resolve, reject) => {
  109. let image = new Image();
  110. image.src = base64Item.base64;
  111. image.addEventListener('load', () => {
  112. let oriWidth = image.naturalWidth
  113. let oriHeight = image.naturalHeight
  114. //不需要压缩尺寸
  115. if (oriWidth < this.maxLongSize && oriHeight < this.maxLongSize) {
  116. this.W = oriWidth
  117. this.H = oriHeight
  118. }
  119. //需要压缩尺寸
  120. else {
  121. if (oriWidth > oriHeight) {
  122. this.W = this.maxLongSize
  123. this.H = Math.trunc(this.maxLongSize * oriHeight / oriWidth)
  124. } else {
  125. this.H = this.maxLongSize
  126. this.W = Math.trunc(this.maxLongSize * oriWidth / oriHeight)
  127. }
  128. }
  129. if (!this.canvas) {
  130. this.canvas = document.createElement('canvas');
  131. }
  132. this.canvas.width = this.W;
  133. this.canvas.height = this.H;
  134. const ctx = this.canvas.getContext('2d');
  135. ctx.clearRect(0, 0, this.W, this.H);
  136. ctx.drawImage(image, 0, 0, this.W, this.H);
  137. const compressImg = this.canvas.toDataURL('image/jpeg', this.quality);
  138. let file = this._dataURLtoFile(compressImg, base64Item.filename);
  139. if (this.base64) {
  140. resolve({
  141. base64: compressImg,
  142. file,
  143. width: this.W,
  144. height: this.H
  145. });
  146. } else {
  147. resolve({
  148. file,
  149. width: this.W,
  150. height: htis.H
  151. });
  152. }
  153. image = null;
  154. })
  155. })
  156. },
  157. getImgInfoWX(tempFilePaths) {
  158. let result = [];
  159. return new Promise((resolve, reject) => {
  160. for (let i = 0; i < tempFilePaths.length; i++) {
  161. uni.getImageInfo({
  162. src: tempFilePaths[i],
  163. success: (info) => {
  164. result.push({
  165. filePath: tempFilePaths[i],
  166. fileInfo: info
  167. });
  168. if (result.length === tempFilePaths.length) {
  169. resolve(result);
  170. }
  171. }
  172. });
  173. }
  174. })
  175. },
  176. compressImageWX(tempFiles) {
  177. let result = [];
  178. return new Promise(async (resolve, reject) => {
  179. for (let i = 0; i < tempFiles.length; i++) {
  180. let res = await this.compressResultWX(tempFiles[i]);
  181. result.push(res);
  182. if (result.length === tempFiles.length) {
  183. resolve(result);
  184. this.ctx = null;
  185. }
  186. }
  187. })
  188. },
  189. compressResultWX(tempFile) {
  190. return new Promise((resolve, reject) => {
  191. let oriWidth = tempFile.fileInfo.width;
  192. let oriHeight = tempFile.fileInfo.height;
  193. let filePath = tempFile.filePath
  194. //不需要压缩
  195. if (oriWidth < this.maxLongSize && oriHeight < this.maxLongSize) {
  196. if (this.base64) {
  197. let base64 = uni.getFileSystemManager().readFileSync(filePath, 'base64');
  198. base64 = `data:image/jpeg;base64,${base64}`
  199. resolve({
  200. file: filePath,
  201. base64,
  202. width: oriWidth,
  203. height: oriHeight
  204. });
  205. } else {
  206. resolve({
  207. file: filePath,
  208. width: oriWidth,
  209. height: oriHeight
  210. });
  211. }
  212. return
  213. }
  214. if (oriWidth > oriHeight) {
  215. this.W = this.maxLongSize
  216. this.H = Math.trunc(this.maxLongSize * oriHeight / oriWidth)
  217. } else {
  218. this.H = this.maxLongSize
  219. this.W = Math.trunc(this.maxLongSize * oriWidth / oriHeight)
  220. }
  221. if (!this.ctx) {
  222. this.ctx = uni.createCanvasContext('canvas', this);
  223. }
  224. this.ctx.clearRect(0, 0, this.W, this.H);
  225. this.ctx.drawImage(filePath, 0, 0, this.W, this.H);
  226. this.ctx.draw(false, setTimeout(() => {
  227. uni.canvasToTempFilePath({
  228. x: 0,
  229. y: 0,
  230. width: this.W,
  231. height: this.H,
  232. destWidth: this.W,
  233. destHeight: this.H,
  234. canvasId: 'canvas',
  235. quality: this.quality,
  236. fileType: 'jpg',
  237. success: (res) => {
  238. let newFilePath = res.tempFilePath;
  239. if (this.base64) {
  240. let base64 = uni.getFileSystemManager().readFileSync(newFilePath, 'base64');
  241. base64 = `data:image/jpeg;base64,${base64}`
  242. resolve({
  243. file: newFilePath,
  244. base64,
  245. width: this.W,
  246. height: this.H
  247. });
  248. } else {
  249. resolve({
  250. file: newFilePath,
  251. width: this.W,
  252. height: this.H
  253. });
  254. }
  255. }
  256. }, this)
  257. }, this.waitDrawTime)); //等待几秒确保绘图完成(用于解决微信小程序真机调试压缩图片不完整的不完美方案)
  258. })
  259. },
  260. toBase64H5(file) {
  261. return new Promise((resolve, reject) => {
  262. let result = [];
  263. for (let i = 0; i < file.length; i++) {
  264. let reader = new FileReader();
  265. let base64Result;
  266. reader.addEventListener('load', (e) => {
  267. base64Result = reader.result || e.target.result;
  268. let filename = file[i].name.slice(0, file[i].name.lastIndexOf('.'));
  269. result.push({
  270. base64: base64Result,
  271. filename
  272. });
  273. reader = null;
  274. if (result.length === file.length) {
  275. resolve(result);
  276. }
  277. });
  278. reader.readAsDataURL(file[i]);
  279. }
  280. })
  281. },
  282. _dataURLtoFile(dataurl, filename) {
  283. let arr = dataurl.split(',');
  284. let mime = arr[0].match(/:(.*?);/)[1],
  285. bstr = atob(arr[1]),
  286. n = bstr.length,
  287. u8arr = new Uint8Array(n);
  288. while (n--) {
  289. u8arr[n] = bstr.charCodeAt(n);
  290. }
  291. return new File([u8arr], filename, {
  292. type: mime
  293. });
  294. }
  295. }
  296. }
  297. </script>
  298. <style scoped>
  299. .mx-compress-container {
  300. width: 0;
  301. height: 0;
  302. margin: 0;
  303. padding: 0;
  304. overflow: hidden;
  305. position: absolute;
  306. z-index: -100000;
  307. }
  308. </style>