draw-poster.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import uni from "./utils/global";
  2. import { handleBuildOpts, extendMount } from "./utils/utils";
  3. import { getCanvas2dContext } from "./utils/wx-utils";
  4. // 扩展挂载储存
  5. let drawPosterExtend = {};
  6. let drawCtxPosterExtend = {};
  7. class DrawPoster {
  8. /** 构建器, 构建返回当前实例, 并挂载多个方法 */
  9. constructor(canvas, ctx, canvasId, loading, debugging, loadingText, createText, tips) {
  10. var _a;
  11. this.canvas = canvas;
  12. this.ctx = ctx;
  13. this.canvasId = canvasId;
  14. this.loading = loading;
  15. this.debugging = debugging;
  16. this.loadingText = loadingText;
  17. this.createText = createText;
  18. this.executeOnions = [];
  19. this.stopStatus = false;
  20. /** 提示器, 传入消息与数据 */
  21. this.debuggingLog = (message, data, color = "#3489fd") => {
  22. if (this.debugging) {
  23. if (data) {
  24. console.log(`%c${this.canvasId} -> ${message}`, `color: ${color}`, data);
  25. }
  26. else {
  27. console.log(`%c${this.canvasId} -> ${message}`, `color: ${color}`);
  28. }
  29. }
  30. };
  31. /** 绘制器, 接收执行器函数, 添加到绘制容器中 */
  32. this.draw = (execute) => {
  33. const length = this.executeOnions.length;
  34. this.executeOnions.push(async () => {
  35. var _a, _b;
  36. try {
  37. this.ctx.save();
  38. await execute(this.ctx);
  39. this.ctx.restore();
  40. return true;
  41. }
  42. catch (error) {
  43. const isOutError = ((_b = (_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.search) === null || _b === void 0 ? void 0 : _b.call(_a, `'nodeId' of undefined`)) >= 0;
  44. !isOutError && console.error(`${this.canvasId} -> 绘画栈(${length}),绘制错误:`, error);
  45. return false;
  46. }
  47. });
  48. };
  49. /** 等待创建绘画, 成功后清空绘制器容器 */
  50. this.awaitCreate = async () => {
  51. this.debuggingLog('绘制海报中...');
  52. this.loading && uni.showLoading({ title: this.loadingText });
  53. const tips = [];
  54. for (let i = 0; i < this.executeOnions.length; i++) {
  55. const execute = this.executeOnions[i];
  56. tips.push(await execute());
  57. }
  58. this.executeOnions = [];
  59. this.debuggingLog('绘制状况', tips);
  60. // 当前绘制为 type2 绘制
  61. if (this.drawType === 'type2d') {
  62. this.loading && uni.hideLoading();
  63. return tips;
  64. }
  65. // 当前绘制为 context 绘制
  66. return await new Promise((resolve) => {
  67. this.ctx.draw(true, () => {
  68. resolve(tips);
  69. this.loading && uni.hideLoading();
  70. });
  71. });
  72. };
  73. /** 创建canvas本地地址 @returns {string} 本地地址 */
  74. this.createImagePath = async (baseOptions = {}) => {
  75. const { canvas, canvasId, executeOnions, awaitCreate } = this;
  76. executeOnions.length && await awaitCreate();
  77. // 如果当前为停止状态
  78. if (this.stopStatus) {
  79. this.stopStatus = false;
  80. return '---stop createImagePath---';
  81. }
  82. this.loading && uni.showLoading({ title: this.createText });
  83. const options = Object.assign({}, baseOptions);
  84. if (this.drawType === 'context')
  85. options.canvasId = canvasId;
  86. if (this.drawType === 'type2d')
  87. options.canvas = canvas;
  88. return new Promise((resolve, reject) => {
  89. options.success = (res) => {
  90. resolve(res.tempFilePath);
  91. this.loading && uni.hideLoading();
  92. this.debuggingLog('绘制成功 🎉', res, '#19be6b');
  93. };
  94. options.fail = (err) => {
  95. reject(err);
  96. this.loading && uni.hideLoading();
  97. this.debuggingLog('绘制失败 🌟', err, '#fa3534');
  98. };
  99. uni.canvasToTempFilePath(options);
  100. });
  101. };
  102. /** 停止当前绘画, 调用则停止当前绘画堆栈的绘画 */
  103. this.stop = () => {
  104. this.executeOnions = [];
  105. this.stopStatus = true;
  106. };
  107. if (!canvas || !ctx || !canvasId) {
  108. throw new Error("DrawPoster Error: Use DrawPoster.build(string | ops) to build drawPoster instance objects");
  109. }
  110. // 判断当前绘制类型
  111. ctx.drawType = this.drawType = (ctx.draw) ? 'context' : 'type2d';
  112. // 挂载全局实例, 绘画扩展
  113. extendMount(this.ctx, drawCtxPosterExtend, (extend, target) => {
  114. var _a;
  115. (_a = target === null || target === void 0 ? void 0 : target.init) === null || _a === void 0 ? void 0 : _a.call(target, this.canvas, this.ctx);
  116. return (...args) => extend(this.canvas, this.ctx, ...args);
  117. });
  118. extendMount(this, drawPosterExtend, (extend, target) => {
  119. var _a;
  120. (_a = target === null || target === void 0 ? void 0 : target.init) === null || _a === void 0 ? void 0 : _a.call(target, this);
  121. return (...args) => extend(this, ...args);
  122. });
  123. // 当离开页面时, 自动调用停止绘画
  124. const _this = this;
  125. const pages = getCurrentPages();
  126. const page = pages[pages.length - 1];
  127. // 查询标识, 不存在, 在替换页面卸载回调, 避免产生死循环
  128. if (!((_a = page === null || page === void 0 ? void 0 : page.onUnload) === null || _a === void 0 ? void 0 : _a.identification)) {
  129. page.oldOnUnload = page.onUnload;
  130. page.onUnload = function () {
  131. _this === null || _this === void 0 ? void 0 : _this.stop();
  132. page.oldOnUnload();
  133. };
  134. page.onUnload.identification = true;
  135. }
  136. tips && this.debuggingLog('构建完成', { canvas, ctx, selector: canvasId }, '#19be6b');
  137. }
  138. }
  139. /** 传入挂载配置对象, 添加扩展方法 */
  140. DrawPoster.use = (opts) => {
  141. if (opts.name)
  142. drawPosterExtend[opts.name] = opts;
  143. };
  144. /** 传入挂载配置对象, 添加绘画扩展方法 */
  145. DrawPoster.useCtx = (opts) => {
  146. if (opts.name)
  147. drawCtxPosterExtend[opts.name] = opts;
  148. };
  149. /** 构建绘制海报矩形方法, 传入canvas选择器或配置对象, 返回绘制对象 */
  150. DrawPoster.build = async (options, tips = true) => {
  151. var _a, _b, _c, _d, _e;
  152. const config = handleBuildOpts(options);
  153. // 初始化监测当前页面绘制对象
  154. const pages = getCurrentPages();
  155. const page = pages[pages.length - 1];
  156. const gcanvas = DrawPoster.prototype['gcanvas'];
  157. if (page[config.selector + '__dp']) {
  158. return page[config.selector + '__dp'];
  159. }
  160. if (config.gcanvas) {
  161. if (!gcanvas)
  162. console.error('--- 当前未引入gcanvas扩展, 将自动切换为普通 canvas ---');
  163. else
  164. gcanvas.enable((_b = (_a = config.componentThis) === null || _a === void 0 ? void 0 : _a.$refs) === null || _b === void 0 ? void 0 : _b[config.selector], {
  165. bridge: gcanvas.WeexBridge
  166. });
  167. }
  168. // 获取canvas实例
  169. const canvas = config.gcanvas && gcanvas ?
  170. gcanvas.enable((_d = (_c = config.componentThis) === null || _c === void 0 ? void 0 : _c.$refs) === null || _d === void 0 ? void 0 : _d[config.selector], {
  171. bridge: gcanvas.WeexBridge
  172. }) :
  173. await getCanvas2dContext(config.selector, config.componentThis);
  174. const ctx = (((_e = canvas.getContext) === null || _e === void 0 ? void 0 : _e.call(canvas, "2d")) || uni.createCanvasContext(config.selector, config.componentThis));
  175. const dp = new DrawPoster(canvas, ctx, config.selector, config.loading, config.debugging, config.loadingText, config.createText, tips);
  176. // 储存当前绘制对象
  177. page[config.selector + '__dp'] = dp;
  178. return page[config.selector + '__dp'];
  179. };
  180. /** 构建多个绘制海报矩形方法, 传入选择器或配置对象的数组, 返回多个绘制对象 */
  181. DrawPoster.buildAll = async (optionsAll) => {
  182. const dpsArr = await Promise.all(optionsAll.map(async (options) => {
  183. return await DrawPoster.build(options, false);
  184. }));
  185. const dpsObj = {};
  186. dpsArr.forEach(dp => dpsObj[dp.canvasId] = dp);
  187. console.log("%cdraw-poster 构建完成:", "#E3712A", dpsObj);
  188. return dpsObj;
  189. };
  190. export default DrawPoster;