utils.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. function compressImageForH5(options) {
  2. const {
  3. src,
  4. quality = 0.8,
  5. width = 'auto',
  6. height = 'auto',
  7. compressedWidth,
  8. compressedHeight,
  9. success,
  10. fail
  11. } = options;
  12. const img = new Image();
  13. img.crossOrigin = 'anonymous';
  14. img.onload = function() {
  15. try {
  16. const canvas = document.createElement('canvas');
  17. const ctx = canvas.getContext('2d');
  18. // 计算压缩后的尺寸
  19. let targetWidth = img.width;
  20. let targetHeight = img.height;
  21. // 优先使用 compressedWidth 和 compressedHeight
  22. if (compressedWidth && compressedHeight) {
  23. targetWidth = compressedWidth;
  24. targetHeight = compressedHeight;
  25. } else if (compressedWidth) {
  26. targetWidth = compressedWidth;
  27. targetHeight = (img.height * compressedWidth) / img.width;
  28. } else if (compressedHeight) {
  29. targetHeight = compressedHeight;
  30. targetWidth = (img.width * compressedHeight) / img.height;
  31. } else {
  32. // 处理 width 和 height 参数
  33. if (width !== 'auto') {
  34. if (width.includes('px')) {
  35. targetWidth = parseInt(width);
  36. } else if (width.includes('%')) {
  37. targetWidth = img.width * (parseInt(width) / 100);
  38. }
  39. }
  40. if (height !== 'auto') {
  41. if (height.includes('px')) {
  42. targetHeight = parseInt(height);
  43. } else if (height.includes('%')) {
  44. targetHeight = img.height * (parseInt(height) / 100);
  45. }
  46. }
  47. // 如果只设置了一个维度,等比缩放
  48. if (width !== 'auto' && height === 'auto') {
  49. targetHeight = (img.height * targetWidth) / img.width;
  50. } else if (height !== 'auto' && width === 'auto') {
  51. targetWidth = (img.width * targetHeight) / img.height;
  52. }
  53. }
  54. canvas.width = targetWidth;
  55. canvas.height = targetHeight;
  56. // 绘制压缩后的图片
  57. ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
  58. // 转换为 blob
  59. canvas.toBlob((blob) => {
  60. if (blob) {
  61. success({
  62. tempFilePath: URL.createObjectURL(blob)
  63. })
  64. } else {
  65. fail(new Error('图片压缩失败'));
  66. }
  67. }, 'image/jpeg', quality);
  68. } catch (error) {
  69. fail(error);
  70. }
  71. };
  72. img.onerror = function() {
  73. fail(new Error('图片加载失败'));
  74. };
  75. img.src = src;
  76. }
  77. function pickExclude(obj, keys) {
  78. // 某些情况下,type可能会为
  79. if (!['[object Object]', '[object File]'].includes(Object.prototype.toString.call(obj))) {
  80. return {}
  81. }
  82. return Object.keys(obj).reduce((prev, key) => {
  83. if (!keys.includes(key)) {
  84. prev[key] = obj[key]
  85. }
  86. return prev
  87. }, {})
  88. }
  89. async function compressImages(config, files) {
  90. if( config === false){
  91. return files;
  92. }
  93. const tasks = [];
  94. for(let i = 0; i < files.length; i++) {
  95. tasks.push(new Promise((resolve) => {
  96. const options = {
  97. ...config,
  98. src: files[i].url,
  99. fail:()=>{},
  100. success: (result) => {
  101. resolve(result);
  102. },
  103. fail: () => {
  104. resolve('');
  105. }
  106. };
  107. // #ifndef H5
  108. uni.compressImage(options);
  109. // #endif
  110. // #ifdef H5
  111. compressImageForH5(options);
  112. // #endif
  113. }));
  114. }
  115. const results = await Promise.all(tasks);
  116. for(let i = 0; i < files.length; i++) {
  117. if(results[i]) {
  118. files[i].url = results[i].tempFilePath;
  119. files[i].thumb = results[i].tempFilePath;
  120. }
  121. }
  122. return files;
  123. }
  124. function formatImage(res) {
  125. return res.tempFiles.map((item) => ({
  126. ...pickExclude(item, ['path']),
  127. type: 'image',
  128. url: item.path,
  129. thumb: item.path,
  130. size: item.size,
  131. // #ifdef H5
  132. name: item.name
  133. // #endif
  134. }))
  135. }
  136. function formatVideo(res) {
  137. return [
  138. {
  139. ...pickExclude(res, ['tempFilePath', 'thumbTempFilePath', 'errMsg']),
  140. type: 'video',
  141. url: res.tempFilePath,
  142. thumb: res.thumbTempFilePath,
  143. size: res.size,
  144. // #ifdef H5
  145. name: res.name
  146. // #endif
  147. }
  148. ]
  149. }
  150. function formatMedia(res) {
  151. return res.tempFiles.map((item) => ({
  152. ...pickExclude(item, ['fileType', 'thumbTempFilePath', 'tempFilePath']),
  153. type: res.type,
  154. url: item.tempFilePath,
  155. thumb: res.type === 'video' ? item.thumbTempFilePath : item.tempFilePath,
  156. size: item.size
  157. }))
  158. }
  159. function formatFile(res) {
  160. return res.tempFiles.map((item) => ({
  161. ...pickExclude(item, ['path']),
  162. url: item.path,
  163. size:item.size,
  164. // #ifdef H5
  165. name: item.name,
  166. type: item.type
  167. // #endif
  168. }))
  169. }
  170. export function chooseFile({
  171. accept,
  172. multiple,
  173. capture,
  174. compressed,
  175. compressImage,
  176. maxDuration,
  177. sizeType,
  178. camera,
  179. maxCount
  180. }) {
  181. return new Promise((resolve, reject) => {
  182. switch (accept) {
  183. case 'image':
  184. uni.chooseImage({
  185. count: multiple ? Math.min(maxCount, 9) : 1,
  186. sourceType: capture,
  187. sizeType,
  188. success: (res) => resolve(compressImages(compressImage,formatImage(res))),
  189. fail: reject
  190. })
  191. break
  192. // #ifdef MP-WEIXIN
  193. // 只有微信小程序才支持chooseMedia接口
  194. case 'media':
  195. wx.chooseMedia({
  196. count: multiple ? Math.min(maxCount, 9) : 1,
  197. sourceType: capture,
  198. maxDuration,
  199. sizeType,
  200. camera,
  201. success: (res) => resolve(formatMedia(res)),
  202. fail: reject
  203. })
  204. break
  205. // #endif
  206. case 'video':
  207. uni.chooseVideo({
  208. sourceType: capture,
  209. compressed,
  210. maxDuration,
  211. camera,
  212. success: (res) => resolve(formatVideo(res)),
  213. fail: reject
  214. })
  215. break
  216. // #ifdef MP-WEIXIN || H5
  217. // 只有微信小程序才支持chooseMessageFile接口
  218. case 'file':
  219. // #ifdef MP-WEIXIN
  220. wx.chooseMessageFile({
  221. count: multiple ? maxCount : 1,
  222. type: accept,
  223. success: (res) => resolve(formatFile(res)),
  224. fail: reject
  225. })
  226. // #endif
  227. // #ifdef H5
  228. // 需要hx2.9.9以上才支持uni.chooseFile
  229. uni.chooseFile({
  230. count: multiple ? maxCount : 1,
  231. type: accept,
  232. success: (res) => resolve(formatFile(res)),
  233. fail: reject
  234. })
  235. // #endif
  236. break
  237. // #endif
  238. default:
  239. // 此为保底选项,在accept不为上面任意一项的时候选取全部文件
  240. // #ifdef MP-WEIXIN
  241. wx.chooseMessageFile({
  242. count: multiple ? maxCount : 1,
  243. type: 'all',
  244. success: (res) => resolve(formatFile(res)),
  245. fail: reject
  246. })
  247. // #endif
  248. // #ifdef H5
  249. // 需要hx2.9.9以上才支持uni.chooseFile
  250. uni.chooseFile({
  251. count: multiple ? maxCount : 1,
  252. type: 'all',
  253. success: (res) => resolve(formatFile(res)),
  254. fail: reject
  255. })
  256. // #endif
  257. }
  258. })
  259. }