uqrcode.vue 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. <template>
  2. <view class="uqrcode" :class="{ 'uqrcode-hide': hide }" :style="{ width: `${templateOptions.width}px`, height: `${templateOptions.height}px` }">
  3. <view class="uqrcode-canvas-wrapper">
  4. <!-- 画布 -->
  5. <!-- #ifndef APP-NVUE -->
  6. <canvas class="uqrcode-canvas" :id="canvasId" :canvas-id="canvasId" :type="canvasType" :style="{
  7. width: `${templateOptions.canvasWidth}px`,
  8. height: `${templateOptions.canvasHeight}px`,
  9. transform: templateOptions.canvasTransform
  10. }" v-if="templateOptions.canvasDisplay" @click="onClick"></canvas>
  11. <!-- #endif -->
  12. <!-- nvue用gcanvas -->
  13. <!-- #ifdef APP-NVUE -->
  14. <gcanvas class="uqrcode-canvas" ref="gcanvas" :style="{
  15. width: `${templateOptions.canvasWidth}px`,
  16. height: `${templateOptions.canvasHeight}px`
  17. }" v-if="templateOptions.canvasDisplay" @click="onClick"></gcanvas>
  18. <!-- #endif -->
  19. </view>
  20. <!-- 加载效果 -->
  21. <view class="uqrcode-makeing" v-if="loading === undefined ? makeing : loading">
  22. <slot name="loading">
  23. <image class="uqrcode-makeing-image" :style="{ width: `${templateOptions.size / 4}px`, height: `${templateOptions.size / 4}px` }"
  24. src="data:image/gif;base64,R0lGODlhAAEAAfIEAOHh4SSsWuDg4N3d3f///wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDYuMC1jMDAyIDc5LjE2NDQ4OCwgMjAyMC8wNy8xMC0yMjowNjo1MyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIyLjAgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODhGMzM4RDEwMTExRUM4MDhCRkVBQkE2QUZDQzkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjAyODhGMzM5RDEwMTExRUM4MDhCRkVBQkE2QUZDQzkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDI4OEYzMzZEMTAxMTFFQzgwOEJGRUFCQTZBRkNDOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4OEYzMzdEMTAxMTFFQzgwOEJGRUFCQTZBRkNDOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQ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">
  25. </image>
  26. </slot>
  27. </view>
  28. <!-- 错误处理 -->
  29. <view class="uqrcode-error" v-if="isError" @click="onClick">
  30. <slot name="error" :error="error">
  31. <text class="uqrcode-error-message">{{ error.errMsg }}</text>
  32. </slot>
  33. </view>
  34. <!-- H5保存提示 -->
  35. <!-- #ifdef H5 -->
  36. <view class="uqrcode-h5-save" v-if="isH5Save">
  37. <slot name="h5save" :tempFilePath="tempFilePath">
  38. <image class="uqrcode-h5-save-image" :src="tempFilePath"></image>
  39. <text class="uqrcode-h5-save-text">{{ h5SaveIsDownload ? '若保存失败,' : '' }}请长按二维码进行保存</text>
  40. </slot>
  41. <view class="uqrcode-h5-save-close" @click.stop="isH5Save = false">
  42. <view class="uqrcode-h5-save-close-before"></view>
  43. <view class="uqrcode-h5-save-close-after"></view>
  44. </view>
  45. </view>
  46. <!-- #endif -->
  47. </view>
  48. </template>
  49. <script>
  50. // #ifdef VUE3
  51. import {
  52. toRaw
  53. } from 'vue';
  54. // #endif
  55. /* 引入uQRCode核心js */
  56. import UQRCode from '../../js_sdk/uqrcode/uqrcode';
  57. /* 引入nvue所需模块 */
  58. // #ifdef APP-NVUE
  59. import {
  60. enable,
  61. WeexBridge
  62. } from '../../js_sdk/gcanvas';
  63. const modal = weex.requireModule('modal');
  64. // #endif
  65. /* 引入队列 */
  66. import {
  67. queueDraw,
  68. queueLoadImage
  69. } from '../../common/queue';
  70. /* 引入缓存图片 */
  71. import {
  72. cacheImageList
  73. } from '../../common/cache';
  74. let instance = null;
  75. export default {
  76. name: 'uqrcode',
  77. props: {
  78. /**
  79. * canvas组件id
  80. */
  81. canvasId: {
  82. type: String,
  83. required: true // canvasId在微信小程序初始值不能为空,created中赋值也不行,必须给一个值,否则挂载组件后无法绘制。不考虑用随机id,uuid
  84. },
  85. /**
  86. * 二维码内容
  87. */
  88. value: {
  89. type: [String, Number]
  90. },
  91. /**
  92. * 选项
  93. */
  94. options: {
  95. type: Object,
  96. default: () => {
  97. return {};
  98. }
  99. },
  100. /**
  101. * 二维码大小
  102. */
  103. size: {
  104. type: [String, Number],
  105. default: 200
  106. },
  107. /**
  108. * 二维码尺寸单位
  109. */
  110. sizeUnit: {
  111. type: String,
  112. default: 'px'
  113. },
  114. /**
  115. * 导出的文件类型
  116. */
  117. fileType: {
  118. type: String,
  119. default: 'png'
  120. },
  121. /**
  122. * 是否初始化组件后就开始生成
  123. */
  124. start: {
  125. type: Boolean,
  126. default: true
  127. },
  128. /**
  129. * 是否数据发生改变自动重绘
  130. */
  131. auto: {
  132. type: Boolean,
  133. default: true
  134. },
  135. /**
  136. * 隐藏组件
  137. */
  138. hide: {
  139. type: Boolean,
  140. default: false
  141. },
  142. /**
  143. * canvas 类型,微信小程序默认使用2d,非2d微信官方已放弃维护,问题比较多
  144. * 注意:微信小程序type2d手机上正常,PC上微信内打开小程序toDataURL报错,看后期微信官方团队会不会做兼容,不兼容的话只能在自行判断在PC使用非2d,或者直接提示用户请在手机上操作,微信团队的海报中心小程序就是这么做的
  145. */
  146. type: {
  147. type: String,
  148. default: () => {
  149. // #ifdef MP-WEIXIN
  150. return '2d';
  151. // #endif
  152. // #ifndef MP-WEIXIN
  153. return 'normal';
  154. // #endif
  155. }
  156. },
  157. /**
  158. * 队列绘制,主要针对NVue端
  159. */
  160. queue: {
  161. type: Boolean,
  162. default: false
  163. },
  164. /**
  165. * 是否队列加载图片,可减少canvas发起的网络资源请求,节省服务器资源
  166. */
  167. isQueueLoadImage: {
  168. type: Boolean,
  169. default: false
  170. },
  171. /**
  172. * loading态
  173. */
  174. loading: {
  175. type: Boolean,
  176. default: undefined
  177. },
  178. /**
  179. * H5保存即自动下载(在支持的环境下),默认false为仅弹层提示用户需要长按图片保存,不会自动下载
  180. */
  181. h5SaveIsDownload: {
  182. type: Boolean,
  183. default: false
  184. },
  185. /**
  186. * H5下载名称
  187. */
  188. h5DownloadName: {
  189. type: String,
  190. default: 'uQRCode'
  191. }
  192. },
  193. data() {
  194. return {
  195. canvas: undefined,
  196. canvasType: undefined,
  197. canvasContext: undefined,
  198. makeDelegate: undefined,
  199. drawDelegate: undefined,
  200. toTempFilePathDelegate: undefined,
  201. makeExecuted: false,
  202. makeing: false,
  203. drawing: false,
  204. isError: false,
  205. error: undefined,
  206. isH5Save: false,
  207. tempFilePath: '',
  208. templateOptions: {
  209. size: 0,
  210. width: 0, // 组件宽度
  211. height: 0,
  212. canvasWidth: 0, // canvas宽度
  213. canvasHeight: 0,
  214. canvasTransform: '',
  215. canvasDisplay: false
  216. },
  217. uqrcodeOptions: {
  218. data: ''
  219. },
  220. plugins: [],
  221. makeingPattern: [
  222. [
  223. [true, true, true, false, false, false, false, true, true, true],
  224. [true, true, true, false, false, false, false, true, true, true],
  225. [true, true, true, false, false, false, false, true, true, true],
  226. [true, true, true, false, false, false, false, true, true, true],
  227. [true, true, true, false, false, false, false, true, true, true],
  228. [true, true, true, false, false, false, false, true, true, true],
  229. [true, true, true, false, false, false, false, true, true, true],
  230. [true, true, true, true, true, true, true, true, true, true],
  231. [true, true, true, true, true, true, true, true, true, true],
  232. [true, true, true, true, true, true, true, true, true, true]
  233. ],
  234. [
  235. [true, true, true, true, true, true, true, true, true, true],
  236. [true, true, true, true, true, true, true, true, true, true],
  237. [true, true, true, true, true, true, true, true, true, true],
  238. [true, true, true, false, false, false, false, true, true, true],
  239. [true, true, true, false, false, false, false, true, true, true],
  240. [true, true, true, false, false, false, false, true, true, true],
  241. [true, true, true, false, false, false, false, false, false, false],
  242. [true, true, true, true, true, true, false, true, true, true],
  243. [true, true, true, true, true, true, false, true, true, true],
  244. [true, true, true, true, true, true, false, true, true, true]
  245. ],
  246. [
  247. [true, true, true, true, true, true, true, true, true, true],
  248. [true, true, true, true, true, true, true, true, true, true],
  249. [true, true, true, true, true, true, true, true, true, true],
  250. [true, true, true, false, false, false, false, true, true, true],
  251. [true, true, true, false, false, false, false, true, true, true],
  252. [true, true, true, true, true, true, true, false, false, false],
  253. [true, true, true, true, true, true, true, false, false, false],
  254. [true, true, true, true, true, true, true, false, false, false],
  255. [true, true, true, false, false, false, false, true, true, true],
  256. [true, true, true, false, false, false, false, true, true, true]
  257. ],
  258. [
  259. [true, true, true, true, true, true, true, true, true, true],
  260. [true, true, true, true, true, true, true, true, true, true],
  261. [true, true, true, true, true, true, true, true, true, true],
  262. [true, true, true, false, false, false, false, false, false, false],
  263. [true, true, true, false, false, false, false, false, false, false],
  264. [true, true, true, false, false, false, false, false, false, false],
  265. [true, true, true, false, false, false, false, false, false, false],
  266. [true, true, true, true, true, true, true, true, true, true],
  267. [true, true, true, true, true, true, true, true, true, true],
  268. [true, true, true, true, true, true, true, true, true, true]
  269. ]
  270. ]
  271. };
  272. },
  273. watch: {
  274. type: {
  275. handler(val) {
  276. const types = ['2d'];
  277. if (types.includes(val)) {
  278. this.canvasType = val;
  279. } else {
  280. this.canvasType = undefined;
  281. }
  282. },
  283. immediate: true
  284. },
  285. value: {
  286. handler() {
  287. if (this.auto) {
  288. this.remake();
  289. }
  290. }
  291. },
  292. size: {
  293. handler() {
  294. if (this.auto) {
  295. this.remake();
  296. }
  297. }
  298. },
  299. options: {
  300. handler() {
  301. if (this.auto) {
  302. this.remake();
  303. }
  304. },
  305. deep: true
  306. },
  307. makeing: {
  308. handler(val) {
  309. if (!val) {
  310. if (typeof this.toTempFilePathDelegate === 'function') {
  311. this.toTempFilePathDelegate();
  312. }
  313. }
  314. }
  315. }
  316. },
  317. mounted() {
  318. this.templateOptions.size = this.sizeUnit == 'rpx' ? uni.upx2px(this.size) : this.size;
  319. this.templateOptions.width = this.templateOptions.size;
  320. this.templateOptions.height = this.templateOptions.size;
  321. this.templateOptions.canvasWidth = this.templateOptions.size;
  322. this.templateOptions.canvasHeight = this.templateOptions.size;
  323. if (this.canvasType == '2d') {
  324. // #ifndef MP-WEIXIN
  325. this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
  326. this.templateOptions.canvasHeight})`;
  327. // #endif
  328. } else {
  329. this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
  330. this.templateOptions.canvasHeight})`;
  331. }
  332. if (this.start) {
  333. this.make();
  334. }
  335. },
  336. methods: {
  337. /**
  338. * 获取模板选项
  339. */
  340. getTemplateOptions() {
  341. var size = this.sizeUnit == 'rpx' ? uni.upx2px(this.size) : this.size;
  342. return deepReplace(this.templateOptions, {
  343. size,
  344. width: size,
  345. height: size
  346. });
  347. },
  348. /**
  349. * 获取插件选项
  350. */
  351. getUqrcodeOptions() {
  352. return deepReplace(this.options, {
  353. data: String(this.value),
  354. size: Number(this.templateOptions.size)
  355. });
  356. },
  357. /**
  358. * 重置画布
  359. */
  360. resetCanvas(callback) {
  361. this.templateOptions.canvasDisplay = false;
  362. this.$nextTick(() => {
  363. this.templateOptions.canvasDisplay = true;
  364. this.$nextTick(() => {
  365. callback && callback();
  366. });
  367. });
  368. },
  369. /**
  370. * 绘制二维码
  371. */
  372. async draw(callback = {}, isDrawDelegate = false) {
  373. if (typeof callback.success != 'function') {
  374. callback.success = () => {};
  375. }
  376. if (typeof callback.fail != 'function') {
  377. callback.fail = () => {};
  378. }
  379. if (typeof callback.complete != 'function') {
  380. callback.complete = () => {};
  381. }
  382. if (this.drawing) {
  383. if (!isDrawDelegate) {
  384. this.drawDelegate = () => {
  385. this.draw(callback, true);
  386. };
  387. return;
  388. }
  389. } else {
  390. this.drawing = true;
  391. }
  392. if (!this.canvasId) {
  393. console.error('[uQRCode]: canvasId must be set!');
  394. this.isError = true;
  395. this.drawing = false;
  396. callback.fail({
  397. errMsg: '[uQRCode]: canvasId must be set!'
  398. });
  399. callback.complete({
  400. errMsg: '[uQRCode]: canvasId must be set!'
  401. });
  402. return;
  403. }
  404. if (!this.value) {
  405. console.error('[uQRCode]: value must be set!');
  406. this.isError = true;
  407. this.drawing = false;
  408. callback.fail({
  409. errMsg: '[uQRCode]: value must be set!'
  410. });
  411. callback.complete({
  412. errMsg: '[uQRCode]: value must be set!'
  413. });
  414. return;
  415. }
  416. /* 组件数据 */
  417. this.templateOptions = this.getTemplateOptions();
  418. /* uQRCode选项 */
  419. this.uqrcodeOptions = this.getUqrcodeOptions();
  420. /* 纠错等级兼容字母写法 */
  421. if (typeof this.uqrcodeOptions.errorCorrectLevel === 'string') {
  422. this.uqrcodeOptions.errorCorrectLevel = UQRCode.errorCorrectLevel[this.uqrcodeOptions.errorCorrectLevel];
  423. }
  424. /* nvue不支持动态修改gcanvas尺寸,除nvue外,默认使用useDynamicSize */
  425. // #ifndef APP-NVUE
  426. if (typeof this.options.useDynamicSize === 'undefined') {
  427. this.uqrcodeOptions.useDynamicSize = true;
  428. }
  429. // #endif
  430. // #ifdef APP-NVUE
  431. if (typeof this.options.useDynamicSize === 'undefined') {
  432. this.uqrcodeOptions.useDynamicSize = false;
  433. }
  434. // if (typeof this.options.drawReserve === 'undefined') {
  435. // this.uqrcodeOptions.drawReserve = true;
  436. // }
  437. // #endif
  438. /* 获取uQRCode实例 */
  439. const qr = instance = new UQRCode();
  440. /* 注册扩展 */
  441. this.plugins.forEach(p => qr.register(p.plugin));
  442. /* 设置uQRCode选项 */
  443. qr.setOptions(this.uqrcodeOptions);
  444. /* 调用制作二维码方法 */
  445. qr.make();
  446. /* 获取canvas上下文 */
  447. let canvasContext = null;
  448. // #ifndef APP-NVUE
  449. if (this.canvasType === '2d') {
  450. // #ifdef MP-WEIXIN
  451. /* 微信小程序获取canvas2d上下文方式 */
  452. const canvas = (this.canvas = await new Promise(resolve => {
  453. uni
  454. .createSelectorQuery()
  455. .in(this) // 在组件内使用需要
  456. .select(`#${this.canvasId}`)
  457. .fields({
  458. node: true,
  459. size: true
  460. })
  461. .exec(res => {
  462. resolve(res[0].node);
  463. });
  464. }));
  465. canvasContext = this.canvasContext = canvas.getContext('2d');
  466. /* 2d的组件设置宽高与实际canvas绘制宽高不是一个,打个比方,组件size=200,canvas.width设置为100,那么绘制出来就是100=200,组件size=400,canvas.width设置为800,绘制大小还是800=400,所以无需理会下方返回的dynamicSize是多少,按dpr重新赋值给canvas即可 */
  467. this.templateOptions.canvasWidth = qr.size;
  468. this.templateOptions.canvasHeight = qr.size;
  469. this.templateOptions.canvasTransform = '';
  470. /* 使用dynamicSize+scale,可以解决小块间出现白线问题,dpr可以解决模糊问题 */
  471. const dpr = uni.getSystemInfoSync().pixelRatio;
  472. canvas.width = qr.dynamicSize * dpr;
  473. canvas.height = qr.dynamicSize * dpr;
  474. canvasContext.scale(dpr, dpr);
  475. /* 微信小程序获取图像方式 */
  476. qr.loadImage = this.getLoadImage(function(src) {
  477. /* 小程序下获取网络图片信息需先配置download域名白名单才能生效 */
  478. return new Promise((resolve, reject) => {
  479. const img = canvas.createImage();
  480. img.src = src;
  481. img.onload = () => {
  482. resolve(img);
  483. };
  484. img.onerror = err => {
  485. reject(err);
  486. };
  487. });
  488. });
  489. // #endif
  490. // #ifndef MP-WEIXIN
  491. /* 非微信小程序不支持2d,切换回uniapp获取canvas上下文方式 */
  492. canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this);
  493. /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */
  494. this.templateOptions.canvasWidth = qr.dynamicSize;
  495. this.templateOptions.canvasHeight = qr.dynamicSize;
  496. this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
  497. this.templateOptions.canvasHeight})`;
  498. /* uniapp获取图像方式 */
  499. qr.loadImage = this.getLoadImage(function(src) {
  500. return new Promise((resolve, reject) => {
  501. if (src.startsWith('http')) {
  502. uni.getImageInfo({
  503. src,
  504. success: res => {
  505. resolve(res.path);
  506. },
  507. fail: err => {
  508. reject(err);
  509. }
  510. });
  511. } else {
  512. if (src.startsWith('.')) {
  513. console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
  514. throw new Error('[uQRCode]: local image path only supports absolute path!');
  515. } else {
  516. resolve(src);
  517. }
  518. }
  519. });
  520. });
  521. // #endif
  522. } else {
  523. /* uniapp获取canvas上下文方式 */
  524. canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this);
  525. /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */
  526. this.templateOptions.canvasWidth = qr.dynamicSize;
  527. this.templateOptions.canvasHeight = qr.dynamicSize;
  528. this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
  529. this.templateOptions.canvasHeight})`;
  530. /* uniapp获取图像方式 */
  531. qr.loadImage = this.getLoadImage(function(src) {
  532. return new Promise((resolve, reject) => {
  533. /* getImageInfo在微信小程序的bug:本地路径返回路径会把开头的/或../移除,导致路径错误,解决方法:限制只能使用绝对路径 */
  534. if (src.startsWith('http')) {
  535. uni.getImageInfo({
  536. src,
  537. success: res => {
  538. resolve(res.path);
  539. },
  540. fail: err => {
  541. reject(err);
  542. }
  543. });
  544. } else {
  545. if (src.startsWith('.')) {
  546. console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
  547. throw new Error('[uQRCode]: local image path only supports absolute path!');
  548. } else {
  549. resolve(src);
  550. }
  551. }
  552. });
  553. });
  554. }
  555. // #endif
  556. // #ifdef APP-NVUE
  557. /* NVue获取canvas上下文方式 */
  558. const gcanvas = this.$refs['gcanvas'];
  559. const canvas = enable(gcanvas, {
  560. bridge: WeexBridge
  561. });
  562. canvasContext = this.canvasContext = canvas.getContext('2d');
  563. /* NVue获取图像方式 */
  564. qr.loadImage = this.getLoadImage(function(src) {
  565. return new Promise((resolve, reject) => {
  566. /* getImageInfo在nvue的bug:获取同一个路径的图片信息,同一时间第一次获取成功,后续失败,猜测是写入本地时产生文件写入冲突,所以没有返回,特别是对于网络资源 --- 已实现队列绘制,已解决此问题 */
  567. if (src.startsWith('.')) {
  568. console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
  569. throw new Error('[uQRCode]: local image path only supports absolute path!');
  570. } else {
  571. uni.getImageInfo({
  572. src,
  573. success: res => {
  574. resolve(res.path);
  575. },
  576. fail: err => {
  577. reject(err);
  578. }
  579. });
  580. }
  581. });
  582. });
  583. // #endif
  584. /* 设置uQRCode实例的canvas上下文 */
  585. qr.canvasContext = canvasContext;
  586. /* 延时等待页面重新绘制完毕 */
  587. setTimeout(() => {
  588. /* 从插件获取具体要调用哪一个扩展函数 */
  589. var plugin = this.plugins.find(p => p.name == qr.style);
  590. var drawCanvasName = plugin ? plugin.drawCanvas : 'drawCanvas';
  591. /* 虽然qr[drawCanvasName]是直接返回Promise的,但由于js内部this指向问题,故不能直接exec(qr[drawCanvasName])此方式执行,需要改成exec(() => qr[drawCanvasName]())才能正确获取this */
  592. var drawCanvas;
  593. if (this.queue) {
  594. drawCanvas = () => queueDraw.exec(() => qr[drawCanvasName]());
  595. // drawCanvas = () => queueDraw.exec(() => new Promise((resolve, reject) => {
  596. // setTimeout(() => {
  597. // qr[drawCanvasName]().then(resolve).catch(reject);
  598. // }, 1000);
  599. // }));
  600. } else {
  601. drawCanvas = () => qr[drawCanvasName]();
  602. }
  603. /* 调用绘制方法将二维码图案绘制到canvas上 */
  604. drawCanvas()
  605. .then(() => {
  606. if (this.drawDelegate) {
  607. /* 高频重绘纠正 */
  608. let delegate = this.drawDelegate;
  609. this.drawDelegate = undefined;
  610. delegate();
  611. } else {
  612. this.drawing = false;
  613. callback.success();
  614. }
  615. })
  616. .catch(err => {
  617. console.log(err);
  618. if (this.drawDelegate) {
  619. /* 高频重绘纠正 */
  620. let delegate = this.drawDelegate;
  621. this.drawDelegate = undefined;
  622. delegate();
  623. } else {
  624. this.drawing = false;
  625. this.isError = true;
  626. callback.fail(err);
  627. }
  628. })
  629. .finally(() => {
  630. callback.complete();
  631. });
  632. }, 300);
  633. },
  634. /**
  635. * 生成二维码
  636. */
  637. make(callback = {}) {
  638. this.makeExecuted = true;
  639. this.makeing = true;
  640. this.isError = false;
  641. if (typeof callback.success != 'function') {
  642. callback.success = () => {};
  643. }
  644. if (typeof callback.fail != 'function') {
  645. callback.fail = () => {};
  646. }
  647. if (typeof callback.complete != 'function') {
  648. callback.complete = () => {};
  649. }
  650. this.resetCanvas(() => {
  651. clearTimeout(this.makeDelegate);
  652. this.makeDelegate = setTimeout(() => {
  653. this.draw({
  654. success: () => {
  655. setTimeout(() => {
  656. callback.success();
  657. this.complete(true);
  658. }, 300);
  659. },
  660. fail: err => {
  661. callback.fail(err);
  662. this.error = err;
  663. this.complete(false, err.errMsg);
  664. },
  665. complete: () => {
  666. callback.complete();
  667. this.makeing = false;
  668. }
  669. });
  670. }, 300);
  671. });
  672. },
  673. /**
  674. * 重新生成
  675. */
  676. remake(callback) {
  677. this.$emit('change');
  678. this.make(callback);
  679. },
  680. /**
  681. * 生成完成
  682. */
  683. complete(success = true, errMsg = '') {
  684. if (success) {
  685. this.$emit('complete', {
  686. success
  687. });
  688. } else {
  689. this.$emit('complete', {
  690. success,
  691. errMsg
  692. });
  693. }
  694. },
  695. /**
  696. * 导出临时路径
  697. */
  698. toTempFilePath(callback = {}) {
  699. if (typeof callback.success != 'function') {
  700. callback.success = () => {};
  701. }
  702. if (typeof callback.fail != 'function') {
  703. callback.fail = () => {};
  704. }
  705. if (typeof callback.complete != 'function') {
  706. callback.complete = () => {};
  707. }
  708. if (!this.makeExecuted) {
  709. console.error('[uQRCode]: make() 方法从未调用!请先成功调用 make() 后再进行操作。');
  710. var err = {
  711. errMsg: '[uQRCode]: make() method has never been executed! please execute make() successfully before operating.'
  712. };
  713. callback.fail(err);
  714. callback.complete(err);
  715. return;
  716. }
  717. if (this.isError) {
  718. callback.fail(this.error);
  719. callback.complete(this.error);
  720. return;
  721. }
  722. if (this.makeing) {
  723. /* 如果还在生成状态,那当前操作将托管到委托,监听生成完成后再通过委托复调当前方法 */
  724. this.toTempFilePathDelegate = () => {
  725. this.toTempFilePath(callback);
  726. };
  727. return;
  728. } else {
  729. this.toTempFilePathDelegate = null;
  730. }
  731. // #ifndef APP-NVUE
  732. if (this.canvasType === '2d') {
  733. // #ifdef MP-WEIXIN
  734. try {
  735. let dataURL = null;
  736. // #ifdef VUE3
  737. dataURL = toRaw(this.canvas)
  738. .toDataURL();
  739. // #endif
  740. // #ifndef VUE3
  741. dataURL = this.canvas.toDataURL();
  742. // #endif
  743. callback.success({
  744. tempFilePath: dataURL
  745. });
  746. callback.complete({
  747. tempFilePath: dataURL
  748. });
  749. } catch (e) {
  750. callback.fail(e);
  751. callback.complete(e);
  752. }
  753. // #endif
  754. } else {
  755. uni.canvasToTempFilePath({
  756. canvasId: this.canvasId,
  757. fileType: this.fileType,
  758. width: Number(this.templateOptions.canvasWidth),
  759. height: Number(this.templateOptions.canvasHeight),
  760. destWidth: Number(this.templateOptions.size),
  761. destHeight: Number(this.templateOptions.size),
  762. success: res => {
  763. callback.success(res);
  764. },
  765. fail: err => {
  766. callback.fail(err);
  767. },
  768. complete: () => {
  769. callback.complete();
  770. }
  771. },
  772. this
  773. );
  774. }
  775. // #endif
  776. // #ifdef APP-NVUE
  777. const dpr = uni.getSystemInfoSync().pixelRatio;
  778. this.canvasContext.toTempFilePath(
  779. 0,
  780. 0,
  781. this.templateOptions.canvasWidth * dpr,
  782. this.templateOptions.canvasHeight * dpr,
  783. this.templateOptions.size * dpr,
  784. this.templateOptions.size * dpr,
  785. '',
  786. 1,
  787. res => {
  788. callback.success(res);
  789. callback.complete(res);
  790. }
  791. );
  792. // #endif
  793. },
  794. /**
  795. * 保存
  796. */
  797. save(callback = {}) {
  798. if (typeof callback.success != 'function') {
  799. callback.success = () => {};
  800. }
  801. if (typeof callback.fail != 'function') {
  802. callback.fail = () => {};
  803. }
  804. if (typeof callback.complete != 'function') {
  805. callback.complete = () => {};
  806. }
  807. this.toTempFilePath({
  808. success: res => {
  809. // #ifndef H5
  810. if (this.canvasType === '2d') {
  811. // #ifdef MP-WEIXIN
  812. /* 需要将 data:image/png;base64, 这段去除 writeFile 才能正常打开文件,否则是损坏文件,无法打开 */
  813. const reg = new RegExp('^data:image/png;base64,', 'g');
  814. const dataURL = res.tempFilePath.replace(reg, '');
  815. const fs = wx.getFileSystemManager();
  816. const tempFilePath = `${wx.env.USER_DATA_PATH}/${new Date().getTime()}${
  817. Math.random()
  818. .toString()
  819. .split('.')[1]
  820. }.png`;
  821. fs.writeFile({
  822. filePath: tempFilePath, // 要写入的文件路径 (本地路径)
  823. data: dataURL, // base64图片
  824. encoding: 'base64',
  825. success: res1 => {
  826. uni.saveImageToPhotosAlbum({
  827. filePath: tempFilePath,
  828. success: res2 => {
  829. callback.success(res2);
  830. },
  831. fail: err2 => {
  832. callback.fail(err2);
  833. },
  834. complete: () => {
  835. callback.complete();
  836. }
  837. });
  838. },
  839. fail: err => {
  840. callback.fail(err);
  841. },
  842. complete: () => {
  843. callback.complete();
  844. }
  845. });
  846. // #endif
  847. } else {
  848. uni.saveImageToPhotosAlbum({
  849. filePath: res.tempFilePath,
  850. success: res1 => {
  851. callback.success(res1);
  852. },
  853. fail: err1 => {
  854. callback.fail(err1);
  855. },
  856. complete: () => {
  857. callback.complete();
  858. }
  859. });
  860. }
  861. // #endif
  862. // #ifdef H5
  863. /* 可以在电脑浏览器下载,移动端iOS不行,安卓微信浏览器不行,安卓外部浏览器可以 */
  864. this.isH5Save = true;
  865. this.tempFilePath = res.tempFilePath;
  866. if (this.h5SaveIsDownload) {
  867. const aEle = document.createElement('a');
  868. aEle.download = this.h5DownloadName; // 设置下载的文件名,默认是'下载'
  869. aEle.href = res.tempFilePath;
  870. document.body.appendChild(aEle);
  871. aEle.click();
  872. aEle.remove(); // 下载之后把创建的元素删除
  873. }
  874. callback.success({
  875. errMsg: 'ok'
  876. });
  877. callback.complete({
  878. errMsg: 'ok'
  879. });
  880. // #endif
  881. },
  882. fail: err => {
  883. callback.fail(err);
  884. callback.complete(err);
  885. }
  886. });
  887. },
  888. /**
  889. * 注册click事件
  890. */
  891. onClick(e) {
  892. this.$emit('click', e);
  893. },
  894. /**
  895. * 获取实例
  896. */
  897. getInstance() {
  898. return instance;
  899. },
  900. /**
  901. * 注册扩展,组件仅支持注册type为style的drawCanvas扩展
  902. * @param {Object} plugin
  903. */
  904. registerStyle(plugin) {
  905. if (plugin.Type != 'style') {
  906. console.warn('[uQRCode]: registerStyle 仅支持注册 style 类型的扩展!');
  907. return {
  908. errMsg: 'registerStyle 仅支持注册 style 类型的扩展!'
  909. };
  910. }
  911. if (typeof plugin === 'function') {
  912. this.plugins.push({
  913. plugin,
  914. name: plugin.Name,
  915. drawCanvas: plugin.DrawCanvas
  916. });
  917. }
  918. },
  919. getLoadImage(loadImage) {
  920. var that = this;
  921. if (typeof loadImage == 'function') {
  922. return function(src) {
  923. /* 判断是否是队列加载图片的 */
  924. if (that.isQueueLoadImage) {
  925. /* 解决iOS APP||NVUE同时绘制多个二维码导致图片丢失需使用队列 */
  926. return queueLoadImage.exec(() => {
  927. return new Promise((resolve, reject) => {
  928. setTimeout(() => {
  929. const cache = cacheImageList.find(x => x.src == src);
  930. if (cache) {
  931. resolve(cache.img);
  932. } else {
  933. loadImage(src)
  934. .then(img => {
  935. cacheImageList.push({
  936. src,
  937. img
  938. });
  939. resolve(img);
  940. })
  941. .catch(err => {
  942. reject(err);
  943. });
  944. }
  945. }, 10);
  946. });
  947. });
  948. } else {
  949. return loadImage(src);
  950. }
  951. };
  952. } else {
  953. return function(src) {
  954. return Promise.resolve(src);
  955. };
  956. }
  957. }
  958. }
  959. };
  960. /**
  961. * 对象属性深度替换
  962. * @param {Object} o 原始对象/默认对象/被替换的对象
  963. * @param {Object} r 从这个对象里取值替换到o对象里
  964. * @return {Object} 替换后的新对象
  965. */
  966. function deepReplace(o = {}, r = {}, c = false) {
  967. let obj;
  968. if (c) {
  969. // 从源替换
  970. obj = o;
  971. } else {
  972. // 不替换源,copy一份备份来替换
  973. obj = {
  974. ...o
  975. };
  976. }
  977. for (let k in r) {
  978. var vr = r[k];
  979. if (vr != undefined) {
  980. if (vr.constructor == Object) {
  981. obj[k] = this.deepReplace(obj[k], vr);
  982. } else if (vr.constructor == String && !vr) {
  983. obj[k] = obj[k];
  984. } else {
  985. obj[k] = vr;
  986. }
  987. }
  988. }
  989. return obj;
  990. }
  991. </script>
  992. <style scoped>
  993. .uqrcode {
  994. position: relative;
  995. }
  996. .uqrcode-hide {
  997. position: fixed;
  998. left: 7500rpx;
  999. }
  1000. .uqrcode-canvas {
  1001. transform-origin: top left;
  1002. }
  1003. .uqrcode-makeing {
  1004. position: absolute;
  1005. top: 0;
  1006. right: 0;
  1007. bottom: 0;
  1008. left: 0;
  1009. z-index: 10;
  1010. /* #ifndef APP-NVUE */
  1011. display: flex;
  1012. /* #endif */
  1013. justify-content: center;
  1014. align-items: center;
  1015. }
  1016. .uqrcode-makeing-image {
  1017. /* #ifndef APP-NVUE */
  1018. display: block;
  1019. max-width: 120px;
  1020. max-height: 120px;
  1021. /* #endif */
  1022. }
  1023. .uqrcode-error {
  1024. position: absolute;
  1025. top: 0;
  1026. right: 0;
  1027. bottom: 0;
  1028. left: 0;
  1029. /* #ifndef APP-NVUE */
  1030. display: flex;
  1031. /* #endif */
  1032. justify-content: center;
  1033. align-items: center;
  1034. }
  1035. .uqrcode-error-message {
  1036. font-size: 12px;
  1037. color: #939291;
  1038. }
  1039. /* #ifdef H5 */
  1040. .uqrcode-h5-save {
  1041. position: fixed;
  1042. top: 0;
  1043. right: 0;
  1044. bottom: 0;
  1045. left: 0;
  1046. z-index: 100;
  1047. background-color: rgba(0, 0, 0, 0.68);
  1048. display: flex;
  1049. flex-direction: column;
  1050. justify-content: center;
  1051. align-items: center;
  1052. }
  1053. .uqrcode-h5-save-image {
  1054. width: 512rpx;
  1055. height: 512rpx;
  1056. padding: 32rpx;
  1057. }
  1058. .uqrcode-h5-save-text {
  1059. margin-top: 20rpx;
  1060. font-size: 32rpx;
  1061. font-weight: 700;
  1062. color: #ffffff;
  1063. }
  1064. .uqrcode-h5-save-close {
  1065. position: relative;
  1066. margin-top: 72rpx;
  1067. width: 60rpx;
  1068. height: 60rpx;
  1069. border: 2rpx solid #ffffff;
  1070. border-radius: 60rpx;
  1071. padding: 10rpx;
  1072. }
  1073. .uqrcode-h5-save-close-before {
  1074. position: absolute;
  1075. top: 50%;
  1076. left: 50%;
  1077. transform: translate(-50%, -50%) rotate(45deg);
  1078. width: 40rpx;
  1079. height: 4rpx;
  1080. background: #ffffff;
  1081. }
  1082. .uqrcode-h5-save-close-after {
  1083. position: absolute;
  1084. top: 50%;
  1085. left: 50%;
  1086. transform: translate(-50%, -50%) rotate(-45deg);
  1087. width: 40rpx;
  1088. height: 4rpx;
  1089. background: #ffffff;
  1090. }
  1091. /* #endif */
  1092. </style>