draw.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445
  1. import { base64ToPathFn, downloadFile, showLoading, hideLoading,
  2. countTextLength, getImageInfo, getModeImage, compressImage } from './util'
  3. import QRCodeAlg from './qrcode'
  4. /**
  5. * 绘制
  6. */
  7. export default class Draw{
  8. constructor(params = {}) {
  9. let { width, canvasId, height, background, drawDelayTime, delayTime, _this, fileType, quality, isCompressImage } = params
  10. this.width = width
  11. this.height = height
  12. this.canvasId = canvasId || null
  13. this.background = background || {
  14. type: 'color',
  15. w: this.width,
  16. h: this.height,
  17. color: '#ffffff'
  18. }
  19. this.drawDelayTime = drawDelayTime || 200
  20. this.delayTime = delayTime || 200
  21. this._this = _this || null
  22. this.Context = uni.createCanvasContext(this.canvasId, this._this)
  23. // 导出图片的类型
  24. this.fileType = fileType || 'png'
  25. // 导出图片的质量
  26. this.quality = quality || 1
  27. // 是否压缩图片,填写时绘制图片会进行压缩操作。绘制图片也能填写该参数。层级大于当前
  28. this.isCompressImage = isCompressImage || false
  29. this.callBack = {
  30. bgObj: {
  31. width: this.background.w,
  32. height: this.background.h
  33. },
  34. ctxObj: {
  35. width,
  36. height
  37. }
  38. }
  39. this.drawTipsText = params.drawTipsText || '绘制中...'
  40. this.allCallBack = []
  41. }
  42. /**
  43. * 绘制矩形
  44. * @param { Object } params 绘制内容
  45. */
  46. drawRect(params = {}) {
  47. let { width, Context } = this
  48. let { x, y, w, h, r, color, alpha, isFill, lineWidth, windowAlign, rotate, drawImage, borderColor, borderWidth, borderType } = {
  49. x: params.x || 0,
  50. y: params.y || 0,
  51. w: params.w || width,
  52. h: params.h || 0,
  53. r: params.r || 0,
  54. color: params.color || '#000000',
  55. borderWidth: params.borderWidth || 0,
  56. borderColor: params.borderColor || '#000000',
  57. borderType: params.borderType || 'default',
  58. alpha: params.alpha || 1,
  59. lineWidth: params.lineWidth || 1,
  60. isFill: params.isFill == undefined ? true : params.isFill,
  61. // 窗口对齐的方式 默认: none 可选 居中: center 右边: right
  62. windowAlign: params.windowAlign || 'none',
  63. // 旋转
  64. rotate: params.rotate || {},
  65. // 是否是在绘制图片,默认不是
  66. drawImage: params.drawImage == undefined ? false : params.drawImage,
  67. }
  68. if (r * 2 > h) {
  69. r = h / 2
  70. }
  71. if (!drawImage && rotate.deg) {
  72. Context.save()
  73. this.setRotate(x, y, w, h, rotate)
  74. }
  75. Context.beginPath()
  76. Context.setGlobalAlpha(alpha)
  77. if (windowAlign != 'none') {
  78. x = this.computedCenterX(width, w, windowAlign)
  79. }
  80. let tr = 0
  81. let tl = 0
  82. let br = 0
  83. let bl = 0
  84. if (typeof borderType == 'string') {
  85. switch(borderType) {
  86. case 'tr':
  87. tr = r
  88. break
  89. case 'tl':
  90. tl = r
  91. break
  92. case 'br':
  93. br = r
  94. break
  95. case 'bl':
  96. bl = r
  97. break
  98. default:
  99. tr = r
  100. tl = r
  101. br = r
  102. bl = r
  103. }
  104. }
  105. if (borderType instanceof Array) {
  106. if (borderType.includes('tr')) {
  107. tr = r
  108. }
  109. if (borderType.includes('tl')) {
  110. tl = r
  111. }
  112. if (borderType.includes('br')) {
  113. br = r
  114. }
  115. if (borderType.includes('bl')) {
  116. bl = r
  117. }
  118. if (borderType.includes('default')) {
  119. tr = r
  120. tl = r
  121. br = r
  122. bl = r
  123. }
  124. }
  125. // 上右 tr
  126. Context.lineTo(x + tl, y)
  127. Context.arc(x + w - tr, y + tr, tr, Math.PI * 1.5, 0, false)
  128. // 下右 br
  129. Context.lineTo(x + w, y + h - br)
  130. Context.arc(x + w - br,y + h - br, br, 0, Math.PI * .5, false)
  131. // 下左 bl
  132. Context.lineTo(x + bl, y + h)
  133. Context.arc(x + bl, y + h - bl, bl, Math.PI * .5, Math.PI, false)
  134. // 上左 tl
  135. Context.lineTo(x, y + tl)
  136. Context.arc(x + tl, y + tl, tl, Math.PI * 1, Math.PI * 1.5, false)
  137. Context.closePath()
  138. if (isFill) {
  139. if (borderWidth != 0) {
  140. Context.setLineWidth(borderWidth)
  141. Context.setStrokeStyle(borderColor)
  142. Context.stroke()
  143. }
  144. Context.setFillStyle(color)
  145. Context.fill()
  146. } else {
  147. Context.setLineWidth(lineWidth)
  148. Context.setStrokeStyle(color)
  149. Context.stroke()
  150. }
  151. Context.setGlobalAlpha(1)
  152. if (!drawImage && rotate.deg) {
  153. Context.restore()
  154. }
  155. }
  156. /**
  157. * 绘制圆
  158. * @param { Object } params 绘制内容
  159. */
  160. drawArc(params = {}) {
  161. let { width, Context } = this
  162. let { x, y, r, s, e, d, alpha, isFill, lineWidth, color, windowAlign, borderColor, borderWidth } = {
  163. x: params.x || 0,
  164. y: params.y || 0,
  165. r: params.r || 0,
  166. s: params.s || 0,
  167. e: params.e || Math.PI * 2,
  168. // 可选。指定弧度的方向是逆时针还是顺时针。默认是false,即顺时针。
  169. d: params.d == undefined ? false : params.d,
  170. alpha: params.alpha || 1,
  171. isFill: params.isFill == undefined ? true : params.isFill,
  172. lineWidth: params.lineWidth || 1,
  173. borderWidth: params.borderWidth || 0,
  174. borderColor: params.borderColor || '#000000',
  175. color: params.color || '#000000',
  176. // 窗口对齐的方式 默认: none 可选 居中: center 右边: right
  177. windowAlign: params.windowAlign || 'none'
  178. }
  179. Context.beginPath()
  180. Context.setGlobalAlpha(alpha)
  181. x = x + r
  182. y = y + r
  183. if (windowAlign != 'none') {
  184. x = this.computedCenterX(width, r * 2, windowAlign)
  185. x += r
  186. }
  187. Context.arc(x, y, r, s, e, d)
  188. if (isFill) {
  189. if (borderWidth != 0) {
  190. Context.setLineWidth(borderWidth)
  191. Context.setStrokeStyle(borderColor)
  192. Context.stroke()
  193. }
  194. Context.setFillStyle(color)
  195. Context.fill()
  196. } else {
  197. Context.setLineWidth(lineWidth)
  198. Context.setStrokeStyle(color)
  199. Context.stroke()
  200. }
  201. Context.setGlobalAlpha(1)
  202. }
  203. /**
  204. * 绘制三角形
  205. * @param @param { Object } params 绘制内容
  206. */
  207. drawTriangle(params = {}) {
  208. let { Context, width } = this
  209. let { x, y, w, h, color, alpha, isFill, lineWidth, drawType, coordinate, rotate, windowAlign, direction, borderWidth, borderColor } = {
  210. x: params.x || 0,
  211. y: params.y || 0,
  212. w: params.w || 0,
  213. h: params.h || 0,
  214. color: params.color || '#000000',
  215. borderWidth: params.borderWidth || 0,
  216. borderColor: params.borderColor || '#000000',
  217. alpha: params.alpha || 1,
  218. isFill: params.isFill == undefined ? true : params.isFill,
  219. lineWidth: params.lineWidth || 1,
  220. // 当绘制类别是自定义的时候需要传递的参数
  221. coordinate: params.coordinate || [],
  222. // 绘制三角形的类型
  223. // right: 直角三角形
  224. // isosceles: 等腰三角形
  225. // custom: 自定义时,x, y, 宽, 高都不需要传递。需要传递绘制点的坐标类型是数组(coordinate)
  226. // [[1, 3], [2, 3], [4, 5]]
  227. drawType: params.drawType || 'isosceles',
  228. // 三角形顶点朝向 top, left, right, bottom
  229. direction: params.direction || 'top',
  230. // 旋转
  231. rotate: params.rotate || {},
  232. // 窗口对齐的方式 默认: none 可选 居中: center 右边: right
  233. windowAlign: params.windowAlign || 'none'
  234. }
  235. if (windowAlign != 'none' && drawType != 'custom') {
  236. x = this.computedCenterX(width, w, windowAlign)
  237. }
  238. if (rotate.deg && drawType != 'custom') {
  239. Context.save()
  240. this.setTriangleRotate(x, y, w, h, rotate, drawType)
  241. }
  242. Context.beginPath()
  243. Context.setGlobalAlpha(alpha)
  244. if (drawType == 'isosceles') {
  245. switch (direction) {
  246. case 'top':
  247. Context.lineTo(x + w / 2, y)
  248. Context.lineTo(x, y + h)
  249. Context.lineTo(x + w, y + h)
  250. break
  251. case 'bottom':
  252. Context.lineTo(x, y)
  253. Context.lineTo(x + w, y)
  254. Context.lineTo(x + w / 2, y + h)
  255. break
  256. case 'right':
  257. Context.lineTo(x, y)
  258. Context.lineTo(x, y + h)
  259. Context.lineTo(x + w, y + h / 2)
  260. break
  261. case 'left':
  262. Context.lineTo(x + w, y)
  263. Context.lineTo(x + w, y + h)
  264. Context.lineTo(x, y + h / 2)
  265. break
  266. }
  267. } else if (drawType == 'right') {
  268. switch (direction) {
  269. case 'top':
  270. Context.lineTo(x, y)
  271. Context.lineTo(x, y + h)
  272. Context.lineTo(x + w, y + h)
  273. break
  274. case 'bottom':
  275. Context.lineTo(x, y)
  276. Context.lineTo(x + w, y)
  277. Context.lineTo(x, y + h)
  278. break
  279. case 'left':
  280. Context.lineTo(x, y)
  281. Context.lineTo(x, y + h)
  282. Context.lineTo(x + w, y)
  283. break
  284. case 'right':
  285. Context.lineTo(x, y + h)
  286. Context.lineTo(x + w, y + h)
  287. Context.lineTo(x + w, y)
  288. break
  289. }
  290. } else if (drawType == 'custom') {
  291. for (let i of coordinate) {
  292. Context.lineTo(i[0], i[1])
  293. }
  294. }
  295. Context.closePath()
  296. if (isFill) {
  297. if (borderWidth != 0) {
  298. Context.setLineWidth(borderWidth)
  299. Context.setStrokeStyle(borderColor)
  300. Context.stroke()
  301. }
  302. Context.setFillStyle(color)
  303. Context.fill()
  304. } else {
  305. Context.setLineWidth(lineWidth)
  306. Context.setStrokeStyle(color)
  307. Context.stroke()
  308. }
  309. Context.setGlobalAlpha(1)
  310. if (rotate.deg && drawType != 'custom') {
  311. Context.restore()
  312. }
  313. }
  314. /**
  315. * 绘制图片
  316. * @param { Object } params 绘制内容
  317. */
  318. drawImage(params = {}) {
  319. let { width, Context } = this
  320. return new Promise(async resolve => {
  321. try {
  322. let { x, y, w, h, r, src, alpha, drawType, borderWidth, windowAlign, color, mode, rotate, triangle, isCompressImage, quality, borderColor, borderType } = {
  323. x: params.x || 0,
  324. y: params.y || 0,
  325. w: params.w || width,
  326. h: params.h || 0,
  327. r: params.r || 0,
  328. src: params.src || '',
  329. alpha: params.alpha || 1,
  330. mode: params.mode || 'aspectFill',
  331. // default: 默认,rect: 圆角矩形, arc: 圆形 triangle: 三角形
  332. drawType: params.drawType || 'default',
  333. borderWidth: params.borderWidth || 0,
  334. borderColor: params.borderColor || '#000000',
  335. borderType: params.borderType || 'default',
  336. color: params.color || '#ffffff',
  337. // 窗口对齐的方式 默认: none 可选 居中: center 右边: right
  338. windowAlign: params.windowAlign || 'none',
  339. // 旋转
  340. rotate: params.rotate || {},
  341. // 绘制三角形图片时三角形的内容
  342. // triangle.type 三角形的类型 right: 直角三角形 isosceles: 等腰三角形 custom: 自定义三角形(不支持旋转)
  343. // triangle.coordinate 自定义三角形时传递的坐标
  344. // triangle.direction 三角形顶点朝向
  345. triangle: params.triangle || {},
  346. // 是否压缩图片
  347. isCompressImage: params.isCompressImage != undefined ? params.isCompressImage : this.isCompressImage,
  348. // 压缩图片时图片的质量只对jpg类型的图片生效
  349. quality: params.quality || 80
  350. }
  351. if (!/\S/.test(src)) {
  352. return resolve({
  353. success: false,
  354. message: '图片路径为空'
  355. })
  356. }
  357. src = await base64ToPathFn(src)
  358. // #ifndef MP-TOUTIAO
  359. if (src.includes('http')) {
  360. let downlaod = await downloadFile(src)
  361. if (downlaod.data.statusCode != 200) {
  362. hideLoading()
  363. return resolve({
  364. success: false,
  365. msg: `图片路径为:${src}的文件下载失败`
  366. })
  367. }
  368. if (!downlaod.success) {
  369. hideLoading()
  370. return resolve({
  371. success: false,
  372. msg: '下载图片失败'
  373. })
  374. }
  375. src = downlaod.data.tempFilePath
  376. }
  377. // #endif
  378. if (windowAlign != 'none') {
  379. x = this.computedCenterX(width, w, windowAlign)
  380. }
  381. // #ifndef H5
  382. // 压缩图片(不支持H5)
  383. // if (isCompressImage) {
  384. // let compressRes = await compressImage({
  385. // src,
  386. // quality
  387. // })
  388. // if (!compressRes.success) {
  389. // return resolve(compressRes)
  390. // }
  391. // src = compressRes.src
  392. // }
  393. // #endif
  394. // showLoading('获取图片信息中....')
  395. let imageInfo = await getImageInfo(src)
  396. if (!imageInfo.success) {
  397. hideLoading()
  398. return resolve(imageInfo)
  399. }
  400. // hideLoading()
  401. let modeImage = getModeImage(Number(imageInfo.width), Number(imageInfo.height), x, y, w, h, mode)
  402. let { dx, dy, dw, dh, sw, sh, sx, sy } = modeImage
  403. Context.beginPath()
  404. if (drawType == 'default') {
  405. Context.save()
  406. this.setRotate(x, y, w, h, rotate)
  407. this.drawRect({
  408. x,
  409. y,
  410. w,
  411. h,
  412. alpha,
  413. color,
  414. drawImage: true
  415. })
  416. Context.clip()
  417. Context.setGlobalAlpha(alpha)
  418. if (mode != 'default') {
  419. await Context.drawImage(src, dx, dy, dw, dh, sx, sy, sw, sh)
  420. } else {
  421. await Context.drawImage(src, dx, dy, dw, dh)
  422. }
  423. Context.restore()
  424. } else if (drawType == 'arc') {
  425. // 绘制圆形图片
  426. Context.save()
  427. this.setRotate(x, y, w, h, rotate)
  428. this.drawArc({
  429. x,
  430. y,
  431. r: w / 2,
  432. borderWidth,
  433. borderColor,
  434. color,
  435. }, true)
  436. Context.clip()
  437. Context.setGlobalAlpha(alpha)
  438. if (mode != 'default') {
  439. await Context.drawImage(src, dx, dy, dw, dh, sx, sy, sw, sh)
  440. } else {
  441. await Context.drawImage(src, dx, dy, dw, dh)
  442. }
  443. Context.restore()
  444. } else if (drawType == 'rect') {
  445. // 绘制矩形图片
  446. Context.save()
  447. this.setRotate(x, y, w, h, rotate)
  448. this.drawRect({
  449. x,
  450. y,
  451. w,
  452. h,
  453. alpha,
  454. borderWidth,
  455. borderColor,
  456. borderType,
  457. r,
  458. color,
  459. drawImage: true
  460. }, true)
  461. Context.clip()
  462. Context.setGlobalAlpha(alpha)
  463. if (mode != 'default') {
  464. await Context.drawImage(src, dx, dy, dw, dh, sx, sy, sw, sh)
  465. } else {
  466. await Context.drawImage(src, dx, dy, dw, dh)
  467. }
  468. Context.restore()
  469. } else if (drawType == 'triangle') {
  470. // 绘制三角形图片
  471. Context.save()
  472. let type = triangle.type || 'isosceles'
  473. let coordinate = triangle.coordinate || []
  474. let direction = triangle.direction || 'top'
  475. if (type != 'custom') {
  476. this.setTriangleRotate(x, y, w, h, rotate, type)
  477. }
  478. this.drawTriangle({
  479. x,
  480. y,
  481. w,
  482. h,
  483. alpha,
  484. borderWidth,
  485. borderColor,
  486. color,
  487. coordinate,
  488. direction,
  489. drawType: type
  490. }, true)
  491. Context.clip()
  492. Context.setGlobalAlpha(alpha)
  493. if (mode != 'default') {
  494. await Context.drawImage(src, dx, dy, dw, dh, sx, sy, sw, sh)
  495. } else {
  496. await Context.drawImage(src, dx, dy, dw, dh)
  497. }
  498. Context.restore()
  499. }
  500. Context.setGlobalAlpha(1)
  501. return resolve({
  502. success: true,
  503. data: src
  504. })
  505. } catch(e) {
  506. return resolve({
  507. success: false,
  508. msg: '绘制图片出错'
  509. })
  510. }
  511. })
  512. }
  513. /**
  514. * 绘制文字
  515. * @param { Object } params 绘制内容
  516. */
  517. drawText(params = {}) {
  518. let { width, Context } = this
  519. let { x, y, w, text, textIndent, lastWidth, font, color, alpha, isFill, line, windowAlign, textAlign, baseline } = {
  520. x: params.x || 0,
  521. y: params.y || 0,
  522. w: params.w || width - params.x,
  523. text: String(params.text) || '',
  524. textIndent: params.textIndent || 0,
  525. lastWidth: params.lastWidth || 0,
  526. font: this.getFont(params.font),
  527. color: params.color || '#000000',
  528. alpha: params.alpha || 1,
  529. isFill: params.isFill == undefined ? true : params.isFill,
  530. // 文字在窗口对齐的方式 默认: none 可选 居中: center 右边: right
  531. windowAlign: params.windowAlign || 'none',
  532. // 文字的对齐方式(在容器里面) none 默认 center: 居中 right: 右边
  533. textAlign: params.textAlign || 'none',
  534. // 水平对齐方式
  535. baseline: params.baseline || 'top',
  536. line: this.getTextLine(params.line)
  537. }
  538. Context.beginPath()
  539. Context.setGlobalAlpha(alpha)
  540. Context.font = font.style
  541. Context.setTextBaseline(baseline)
  542. if (typeof text != 'string') {
  543. text += ''
  544. }
  545. let textArr = params.textArr
  546. if (!textArr) {
  547. textArr = this.computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign)
  548. }
  549. if (isFill) {
  550. Context.setFillStyle(color)
  551. for (let i of textArr) {
  552. let { text, x, y, tx, ty, tw } = i
  553. Context.fillText(text, x, y)
  554. if (line.lineStyle != 'none') {
  555. this.drawLine({
  556. x: tx,
  557. y: ty,
  558. w: tw,
  559. color,
  560. lineType: line.lineType,
  561. lineWidth: line.lineWidth
  562. }, true)
  563. }
  564. }
  565. } else {
  566. Context.setStrokeStyle(color)
  567. for (let i of textArr) {
  568. let { text, x, y, tx, ty, tw } = i
  569. Context.strokeText(text, x, y)
  570. if (line.lineStyle != 'none') {
  571. this.drawLine({
  572. x: tx,
  573. y: ty,
  574. w: tw,
  575. color,
  576. lineType: line.lineType,
  577. lineWidth: line.lineWidth
  578. }, true)
  579. }
  580. }
  581. }
  582. Context.setGlobalAlpha(1)
  583. }
  584. /**
  585. * 获取字体样式
  586. * @param { Object } font 字体
  587. */
  588. getFont(font = {}) {
  589. let { fontSize, fontFamily, fontStyle, fontVariant, fontWeight } = {
  590. fontSize: font.size || 12,
  591. fontFamily: font.family || 'sans-serif',
  592. // 斜体: italic, 倾斜体:oblique
  593. fontStyle: font.style || 'normal',
  594. fontVariant: font.variant || 'normal',
  595. // 粗体
  596. fontWeight: font.weight || 'normal'
  597. }
  598. return {
  599. fontSize, fontFamily, fontStyle, fontVariant, fontWeight,
  600. style: `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}px ${fontFamily}`
  601. }
  602. }
  603. /**
  604. * 获取文字line样式
  605. * @param { Object } line 行高
  606. */
  607. getTextLine(line = {}) {
  608. return {
  609. // 显示的行数 -1 不限制
  610. lineNum: line.num || -1,
  611. // 行高
  612. lineHeight: line.height || 16,
  613. // none: 不需要 underline: 下划线, lineThrough 删除线
  614. lineStyle: line.style || 'none',
  615. // 线的类型 dashed 虚线 solid 实线
  616. lineType: line.type || 'solid',
  617. // 线宽度
  618. lineWidth: line.width || 1
  619. }
  620. }
  621. /**
  622. * 画线
  623. * @param { Object } params 绘制内容
  624. */
  625. drawLine(params = {}) {
  626. let { width, Context } = this
  627. let { x, y, color, w, algin, alpha, lineType, pattern, offset, lineCap, lineWidth, windowAlign } = {
  628. x: params.x || 0,
  629. y: params.y || 0,
  630. w: params.w || width - params.x,
  631. color: params.color || '#000000',
  632. algin: params.algin || 'right',
  633. alpha: params.alpha || 1,
  634. // dashed 虚线 solid 实线
  635. lineType: params.lineType || 'solid',
  636. // 详看CanvasContext.setLineDash文档
  637. // 一组描述交替绘制线段和间距(坐标空间单位)长度的数字
  638. pattern: params.pattern || [5, 5],
  639. // 虚线偏移量
  640. offset: params.offset || 5,
  641. lineWidth: params.lineWidth || 1,
  642. lineCap: params.lineCap || 'butt',
  643. // 窗口对齐的方式 默认: none 可选 居中: center 右边: right
  644. windowAlign: params.windowAlign || 'none'
  645. }
  646. Context.beginPath()
  647. Context.setGlobalAlpha(alpha)
  648. if (lineType == 'dashed') {
  649. Context.setLineDash(pattern, offset)
  650. }
  651. Context.setLineCap(lineCap)
  652. Context.setLineWidth(lineWidth)
  653. Context.setStrokeStyle(color)
  654. switch (algin) {
  655. case 'right':
  656. if (windowAlign != 'none') {
  657. x = this.computedCenterX(width, w, windowAlign)
  658. }
  659. Context.moveTo(x, y)
  660. Context.lineTo(w + x, y)
  661. break
  662. case 'left':
  663. if (windowAlign != 'none') {
  664. x = this.computedCenterX(width, w, windowAlign)
  665. }
  666. Context.moveTo(x, y)
  667. Context.lineTo(windowAlign == 'none' ? x - w : x + w, y)
  668. break
  669. case 'top':
  670. Context.moveTo(x, y)
  671. Context.lineTo(x, -(y + w))
  672. break
  673. case 'bottom':
  674. Context.moveTo(x, y)
  675. Context.lineTo(x, y + w)
  676. break
  677. }
  678. Context.stroke()
  679. Context.closePath()
  680. Context.setLineDash()
  681. Context.setGlobalAlpha(1)
  682. }
  683. /**
  684. * 绘制二维码
  685. * @param { Object } params 二维码参数
  686. */
  687. drawQrCode(params = {}) {
  688. let { Context, width } = this
  689. return new Promise(async resolve => {
  690. let { x, y, image, windowAlign, ...options } = {
  691. x: params.x || 0,
  692. y: params.y || 0,
  693. text: String(params.text) || '',
  694. size: params.size || 100,
  695. // 容错级别 默认3
  696. correctLevel: params.lv || 3,
  697. // 二维码背景色
  698. background: params.background || '#000000',
  699. // 二维码前景色
  700. foreground: params.foreground || '#ffffff',
  701. // 二维码角标色
  702. pdground: params.pdground || '#ffffff',
  703. image: params.image || {},
  704. // 窗口对齐的方式 默认: none 可选 居中: center 右边: right
  705. windowAlign: params.windowAlign || 'none'
  706. }
  707. if (windowAlign != 'none') {
  708. x = this.computedCenterX(width, options.size, windowAlign)
  709. }
  710. //使用QRCodeAlg创建二维码结构
  711. let qrcodeAlgObjCache = []
  712. let qrCodeAlg = null
  713. let l = qrcodeAlgObjCache.length
  714. let d = 0
  715. for (let i = 0;i < l; i++) {
  716. d = i
  717. if (qrcodeAlgObjCache[i].text == options.text && qrcodeAlgObjCache[i].text.correctLevel == options.correctLevel) {
  718. qrCodeAlg = qrcodeAlgObjCache[i].obj
  719. break
  720. }
  721. }
  722. if (d == l) {
  723. qrCodeAlg = new QRCodeAlg(options.text, options.correctLevel)
  724. qrcodeAlgObjCache.push({
  725. text: options.text,
  726. correctLevel: options.correctLevel,
  727. obj: qrCodeAlg
  728. })
  729. }
  730. /**
  731. * 计算矩阵点的前景色
  732. * @param {Obj} config
  733. * @param {Number} config.row 点x坐标
  734. * @param {Number} config.col 点y坐标
  735. * @param {Number} config.count 矩阵大小
  736. * @param {Number} config.options 组件的options
  737. * @return {String}
  738. */
  739. let getForeGround = function (config) {
  740. let options = config.options
  741. if (options.pdground && (
  742. (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
  743. (config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
  744. (config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
  745. )) {
  746. return options.pdground
  747. }
  748. return options.foreground
  749. }
  750. let count = qrCodeAlg.getModuleCount()
  751. let ratioSize = options.size
  752. let ratioImgSize = image.size || 30
  753. //计算每个点的长宽
  754. let tileW = (ratioSize / count).toPrecision(4)
  755. let tileH = (ratioSize / count).toPrecision(4)
  756. //绘制
  757. for (let row = 0; row < count; row++) {
  758. for (let col = 0; col < count; col++) {
  759. let w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW))
  760. let h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW))
  761. let foreground = getForeGround({
  762. row: row,
  763. col: col,
  764. count: count,
  765. options: options
  766. })
  767. Context.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background)
  768. Context.fillRect(x + Math.round(col * tileW), y + Math.round(row * tileH), w, h)
  769. }
  770. }
  771. if (image.src) {
  772. let { src, r, color, borderWidth, borderColor } = image
  773. let dx = x + Number(((ratioSize - ratioImgSize) / 2).toFixed(2))
  774. let dy = y + Number(((ratioSize - ratioImgSize) / 2).toFixed(2))
  775. let drawImage = await this.drawImage({
  776. x: dx,
  777. y: dy,
  778. w: ratioImgSize,
  779. h: ratioImgSize,
  780. src,
  781. r,
  782. color,
  783. borderWidth,
  784. borderColor,
  785. drawType: 'rect',
  786. isCompressImage: false,
  787. }, true)
  788. if (!drawImage.success) {
  789. return resolve(drawImage)
  790. }
  791. }
  792. return resolve({
  793. success: true
  794. })
  795. })
  796. }
  797. /**
  798. * 计算出文字一共有多少列,渲染位置之类
  799. * @param { Number } x x轴
  800. * @param { Number } y y轴
  801. * @param { Number } w 文字宽度
  802. * @param { String } text 文字内容
  803. * @param { Number } textIndent 首行缩进
  804. * @param { Number } lastWidth 最后一行的宽度
  805. * @param { Object } font 字体
  806. * @param { Object } line 行高
  807. * @param { String } textAlign 文字对齐方式
  808. * @param { String } windowAlign 窗口对齐方式
  809. * @returns
  810. */
  811. computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign) {
  812. let { Context, width } = this
  813. let { fontSize } = font
  814. let { lineNum, lineHeight, lineStyle } = line
  815. // 文字的总长度
  816. let textLength = countTextLength(Context, text, fontSize)
  817. let temp = ''
  818. let row = []
  819. if (text.includes('\n')) {
  820. let texts = text.split('\n')
  821. for (let text of texts) {
  822. computedTextLength(text)
  823. }
  824. } else {
  825. computedTextLength(text)
  826. }
  827. function computedTextLength(text) {
  828. for (let i in text) {
  829. let tempLength = countTextLength(Context, temp, fontSize)
  830. if (row.length == 0 && textIndent != 0 && textAlign == 'none' && windowAlign == 'none') {
  831. tempLength += textIndent * fontSize
  832. }
  833. if (tempLength <= w && countTextLength(Context, (temp + text[i]), fontSize) <= w) {
  834. temp += text[i]
  835. } else {
  836. row.push(temp)
  837. temp = text[i]
  838. }
  839. if (i == text.length - 1) {
  840. row.push(temp)
  841. temp = ''
  842. }
  843. }
  844. }
  845. if (lineNum != -1 && lastWidth != 0 && row.length != 0 && row.length > lineNum) {
  846. let lastText = row[lineNum - 1]
  847. let temp = ''
  848. for (let i in lastText) {
  849. let tempLength = countTextLength(Context, temp, fontSize)
  850. if (tempLength <= lastWidth && countTextLength(Context, (temp + lastText[i]), fontSize) <= lastWidth) {
  851. temp += lastText[i]
  852. continue
  853. }
  854. break
  855. }
  856. row[lineNum - 1] = temp
  857. }
  858. lineHeight = lineHeight == 1 ? fontSize + 2 : lineHeight
  859. // 获取一共有多少列
  860. let lineSize = Math.ceil(textLength / w)
  861. if (text.includes('\n') && lineNum == -1) {
  862. lineNum = row.length
  863. } else if (text.includes('\n') && lineNum != -1) {
  864. lineSize = row.length
  865. lineNum = lineNum > lineSize ? lineSize : lineNum
  866. } else if (lineNum != -1) {
  867. lineNum = lineNum > lineSize ? lineSize : lineNum
  868. }
  869. let size = lineNum != -1 ? lineNum : lineSize
  870. let textArr = []
  871. for (let i = 0; i < size; i++) {
  872. let obj = {}
  873. let text = row[i]
  874. let textLength = countTextLength(Context, text, fontSize)
  875. let wx = x
  876. let tx = x
  877. if (i == 0 && textIndent != 0 && textAlign == 'none' && windowAlign == 'none') {
  878. textLength += textIndent * fontSize
  879. wx += textIndent * fontSize
  880. tx = wx
  881. }
  882. if (textAlign != 'none' && textLength < w) {
  883. tx = this.computedCenterX(w, textLength, textAlign)
  884. wx = tx
  885. }
  886. if (windowAlign != 'none' && textAlign != 'none') {
  887. wx = this.computedCenterX(width, w, windowAlign)
  888. wx += tx
  889. tx = wx
  890. } else if (windowAlign != 'none') {
  891. wx = this.computedCenterX(width, w, windowAlign)
  892. tx = wx
  893. }
  894. if (text && lineNum != -1 && i == lineNum - 1) {
  895. if ((textLength + fontSize) >= w) {
  896. text = text.substring(0, text.length - 1) + '...'
  897. } else if (lastWidth != 0 && (textLength + fontSize) >= lastWidth) {
  898. text = text.substring(0, text.length - 1) + '...'
  899. }
  900. }
  901. if (lineStyle != 'none') {
  902. obj.tx = tx
  903. obj.tw = textLength
  904. if (lineStyle == 'underline') {
  905. obj.ty = y + (i * lineHeight) + fontSize
  906. }
  907. if (lineStyle == 'lineThrough') {
  908. obj.ty = y + (i * lineHeight) + fontSize / 2
  909. }
  910. }
  911. obj.text = text
  912. obj.x = wx
  913. obj.y = y + (i * lineHeight)
  914. text && textArr.push(obj)
  915. }
  916. return textArr
  917. }
  918. /**
  919. * 计算内容需要显示在画布中间的x轴的位置
  920. * @param { Number | String } boxWidth 容器的宽度
  921. * @param { Number | String } contentWidth 内容宽度
  922. * @param { String } type 类型 center: 水平 right: 右边
  923. * @returns
  924. */
  925. computedCenterX(boxWidth, contentWidth, type = 'center') {
  926. if (type == 'center') {
  927. return (boxWidth - contentWidth) / 2
  928. }
  929. if (type == 'right') {
  930. return boxWidth - contentWidth
  931. }
  932. }
  933. /**
  934. * 设置旋转角度
  935. * @param { String } x x轴
  936. * @param { String } y y轴
  937. * @param { String } w 宽度
  938. * @param { String } h 高度
  939. * @param { Object } rotate 旋转内容
  940. * @param { String } rotate.deg 旋转角度
  941. * @param { String } rotate.type 类型 topLeft: 中心点在上左 topMiddle 中心点在上中 topRight 中心点在上右
  942. * middleLeft: 中心点在中左 bottomMiddle 中心点在正中间 middleRight 中心点在中右
  943. * bottomLeft: 中心点在下左 bottomMiddle 中心点在下中 middleRight 中心点在下右
  944. */
  945. setRotate(x, y, w, h, rotate) {
  946. let { Context } = this
  947. let deg = rotate.deg || 0
  948. let type = rotate.type || 'middle'
  949. let rx = x
  950. let ry = y
  951. switch (type) {
  952. case 'topLeft':
  953. Context.translate(rx, ry)
  954. Context.rotate(deg * Math.PI / 180)
  955. Context.translate(-rx, -ry)
  956. break
  957. case 'topMiddle':
  958. rx = x + (w / 2)
  959. Context.translate(rx, ry)
  960. Context.rotate(deg * Math.PI / 180)
  961. Context.translate(-rx, -ry)
  962. break
  963. case 'topRight':
  964. rx = x + w
  965. Context.translate(rx, ry)
  966. Context.rotate(deg * Math.PI / 180)
  967. Context.translate(-rx, -ry)
  968. break
  969. case 'bottomLeft':
  970. ry = y + h
  971. Context.translate(rx, ry)
  972. Context.rotate(deg * Math.PI / 180)
  973. Context.translate(-rx, -ry)
  974. break
  975. case 'bottomMiddle':
  976. rx = x + (w / 2)
  977. ry = y + h
  978. Context.translate(rx, ry)
  979. Context.rotate(deg * Math.PI / 180)
  980. Context.translate(-rx, -ry)
  981. break
  982. case 'bottomRight':
  983. rx = x + w
  984. ry = y + h
  985. Context.translate(rx, ry)
  986. Context.rotate(deg * Math.PI / 180)
  987. Context.translate(-rx, -ry)
  988. break
  989. case 'middleLeft':
  990. ry = y + (h / 2)
  991. Context.translate(rx, ry)
  992. Context.rotate(deg * Math.PI / 180)
  993. Context.translate(-rx, -ry)
  994. break
  995. case 'middleRight':
  996. rx = x + w
  997. ry = y + (h / 2)
  998. Context.translate(rx, ry)
  999. Context.rotate(deg * Math.PI / 180)
  1000. Context.translate(-rx, -ry)
  1001. break
  1002. case 'middle':
  1003. rx = x + (w / 2)
  1004. ry = y + (h / 2)
  1005. Context.translate(rx, ry)
  1006. Context.rotate(deg * Math.PI / 180)
  1007. Context.translate(-rx, -ry)
  1008. break
  1009. }
  1010. }
  1011. /**
  1012. * 设置三角形旋转角度
  1013. * @param { String } x x轴
  1014. * @param { String } y y轴
  1015. * @param { String } w 宽度
  1016. * @param { String } h 高度
  1017. * @param { Object } rotate 旋转内容
  1018. * @param { String } rotate.deg 旋转角度
  1019. * @param { String } rotate.type 类型 top: 上 left: 左 right: 右 middle: 中心
  1020. * @param { String } triangType 三角形类型(不支持自定义的三角形 ) right: 直角三角形 isosceles: 等腰三角形
  1021. */
  1022. setTriangleRotate(x, y, w, h, rotate, triangType) {
  1023. let { Context } = this
  1024. let deg = rotate.deg || 0
  1025. let type = rotate.type || 'top'
  1026. let rx = x
  1027. let ry = y
  1028. switch (type) {
  1029. case 'top':
  1030. if (triangType == 'right') {
  1031. rx = x
  1032. ry = y
  1033. } else {
  1034. rx = x + w / 2
  1035. ry = y
  1036. }
  1037. Context.translate(rx, ry)
  1038. Context.rotate(deg * Math.PI / 180)
  1039. Context.translate(-rx, -ry)
  1040. break
  1041. case 'left':
  1042. rx = x
  1043. ry = y + h
  1044. Context.translate(rx, ry)
  1045. Context.rotate(deg * Math.PI / 180)
  1046. Context.translate(-rx, -ry)
  1047. break
  1048. case 'right':
  1049. rx = x + w
  1050. ry = y + h
  1051. Context.translate(rx, ry)
  1052. Context.rotate(deg * Math.PI / 180)
  1053. Context.translate(-rx, -ry)
  1054. break
  1055. case 'middle':
  1056. rx = x + w / 2
  1057. ry = y + h / 2
  1058. Context.translate(rx, ry)
  1059. Context.rotate(deg * Math.PI / 180)
  1060. Context.translate(-rx, -ry)
  1061. break
  1062. }
  1063. }
  1064. /**
  1065. * 排序drawArray 根据数据的zIndex进行排序,修改渲染顺序
  1066. * @param { Array } drawArray 绘制内容
  1067. */
  1068. sortDrawArray(drawArray) {
  1069. function compare() {
  1070. return function(after, current) {
  1071. let aZIndex = after.zIndex || 0
  1072. let cZIndex = current.zIndex || 0
  1073. return aZIndex - cZIndex
  1074. }
  1075. }
  1076. drawArray.sort(compare())
  1077. return drawArray
  1078. }
  1079. /**
  1080. * 往所有的回调信息里面添加内容
  1081. * @param { Object } params 内容
  1082. */
  1083. setAllCallBack(params) {
  1084. let { width } = this
  1085. let { type, x, y, r, w, h, lineWidth, size, name } = params
  1086. w = w || width
  1087. h = h || 0
  1088. x = x || 0
  1089. y = y || 0
  1090. r = r || 0
  1091. lineWidth = lineWidth || 1
  1092. size = size || 0
  1093. name = name || ''
  1094. let sx = x
  1095. let sy = y
  1096. let ex = x + w
  1097. let ey = y + h
  1098. // 圆形
  1099. if (type == 'arc') {
  1100. ex = x + r * 2
  1101. ey = y + r * 2
  1102. w = r * 2
  1103. h = r * 2
  1104. }
  1105. // 文字
  1106. if (type == 'text') {
  1107. let { text, textIndent, lastWidth, font, line, textAlign, windowAlign } = {
  1108. text: String(params.text) || '',
  1109. textIndent: params.textIndent || 0,
  1110. lastWidth: params.lastWidth || 0,
  1111. font: this.getFont(params.font),
  1112. line: this.getTextLine(params.line),
  1113. textAlign: params.textAlign || 'none',
  1114. windowAlign: params.windowAlign || 'none',
  1115. }
  1116. if (w == width) {
  1117. w -= x
  1118. }
  1119. let textArr = this.computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign)
  1120. let lastText = textArr[textArr.length - 1]
  1121. ex = lastText.x + (font.fontSize * lastText.text.length)
  1122. ey = lastText.y + font.fontSize
  1123. params.textArr = textArr
  1124. h = ey - sy
  1125. }
  1126. // 线
  1127. if (type == 'line') {
  1128. ey = y + lineWidth
  1129. h = lineWidth
  1130. }
  1131. // 二维码
  1132. if (type == 'qrcode') {
  1133. ex = x + size
  1134. ey = y + size
  1135. w = size
  1136. h = size
  1137. }
  1138. this.allCallBack.push({
  1139. sx,
  1140. sy,
  1141. ex,
  1142. ey,
  1143. w,
  1144. h,
  1145. name
  1146. })
  1147. }
  1148. /**
  1149. * 绘制内容
  1150. * @returns
  1151. */
  1152. drawCanvas(drawArray) {
  1153. let { Context } = this
  1154. return new Promise(async resolve => {
  1155. try {
  1156. for (let i of drawArray) {
  1157. if (i.callBack && typeof i.callBack == 'function' && i.type != 'custom') {
  1158. let beforeInfo = this.allCallBack.length == 0 ? {} : this.allCallBack[this.allCallBack.length - 1]
  1159. let callBackInfo = i.callBack(beforeInfo, this.allCallBack) || {}
  1160. let { callBack, ...data } = i
  1161. i = { ...data, ...callBackInfo }
  1162. }
  1163. if (i.type != 'custom' && i.drawType != 'custom') {
  1164. this.setAllCallBack(i)
  1165. }
  1166. switch (i.type) {
  1167. // 文字
  1168. case 'text':
  1169. this.drawText(i)
  1170. break
  1171. // 矩形
  1172. case 'rect':
  1173. this.drawRect(i)
  1174. break
  1175. // 图片
  1176. case 'image':
  1177. let image = await this.drawImage(i)
  1178. if (!image.success) {
  1179. return resolve(image)
  1180. }
  1181. break
  1182. // 圆形
  1183. case 'arc':
  1184. this.drawArc(i)
  1185. break
  1186. // 三角形
  1187. case 'triangle':
  1188. this.drawTriangle(i)
  1189. break
  1190. // 线条
  1191. case 'line':
  1192. this.drawLine(i)
  1193. break
  1194. // 二维码
  1195. case 'qrcode':
  1196. await this.drawQrCode(i)
  1197. break
  1198. // 自定义
  1199. case 'custom':
  1200. i.setDarw(Context, this)
  1201. break
  1202. }
  1203. }
  1204. resolve({
  1205. success: true
  1206. })
  1207. } catch(e) {
  1208. hideLoading()
  1209. return resolve({
  1210. success: false,
  1211. msg: '绘制内容失败:' + e
  1212. })
  1213. }
  1214. })
  1215. }
  1216. /**
  1217. * 绘制背景
  1218. * @returns
  1219. */
  1220. drawBackground() {
  1221. let { background, width, height } = this
  1222. return new Promise(async resolve => {
  1223. let { type, ...params } = background
  1224. // 绘制背景色
  1225. if (type == 'color') {
  1226. this.drawRect({
  1227. ...params,
  1228. w: params.w || width,
  1229. h: params.h || height,
  1230. color: params.color || '#ffffff'
  1231. }, true)
  1232. }
  1233. // 绘制背景图
  1234. if (type == 'image') {
  1235. await this.drawImage({
  1236. ...params,
  1237. w: params.w || width,
  1238. h: params.h || height,
  1239. }, true)
  1240. }
  1241. resolve({
  1242. success: true
  1243. })
  1244. })
  1245. }
  1246. /**
  1247. * 创建canvas导出文件
  1248. * @returns
  1249. */
  1250. createdCanvasFilePath() {
  1251. let { canvasId, width, height, _this, fileType, quality } = this
  1252. return new Promise(resolve => {
  1253. try {
  1254. uni.canvasToTempFilePath({
  1255. canvasId,
  1256. x: 0,
  1257. y: 0,
  1258. width,
  1259. height,
  1260. quality,
  1261. fileType,
  1262. success: res => {
  1263. resolve({
  1264. success: true,
  1265. data: res.tempFilePath,
  1266. msg: '绘画成功'
  1267. })
  1268. },
  1269. fail: err => {
  1270. resolve({
  1271. success: false,
  1272. msg: `导出图片失败: ${JSON.stringify(err)}`
  1273. })
  1274. hideLoading()
  1275. }
  1276. }, _this || null)
  1277. } catch (e) {
  1278. hideLoading()
  1279. resolve({
  1280. success: false,
  1281. msg: '导出图片失败: 绘画错误'
  1282. })
  1283. }
  1284. })
  1285. }
  1286. /**
  1287. * 预绘制背景
  1288. * @returns
  1289. */
  1290. preDrawBackground() {
  1291. return new Promise(async resolve => {
  1292. try {
  1293. showLoading('初始化中...')
  1294. // 绘制背景
  1295. const drawBackground = await this.drawBackground()
  1296. if (!drawBackground.success) {
  1297. hideLoading()
  1298. return resolve({
  1299. success: false,
  1300. msg: '初始化失败,绘制背景图失败'
  1301. })
  1302. }
  1303. hideLoading()
  1304. return resolve({
  1305. success: true,
  1306. msg: '初始化成功'
  1307. })
  1308. } catch(e) {
  1309. hideLoading()
  1310. resolve({
  1311. success: false,
  1312. msg: e
  1313. })
  1314. }
  1315. })
  1316. }
  1317. /**
  1318. * 将绘制的内容绘制到画布上
  1319. * @param { Boolean } complete 是否完成,如果为true会绘制图片并返回图片路径
  1320. * @returns
  1321. */
  1322. canvasDraw(complete = true) {
  1323. let { Context, drawDelayTime } = this
  1324. return new Promise(async resolve => {
  1325. setTimeout(async () => {
  1326. hideLoading()
  1327. await Context.draw(true, async () => {
  1328. if (complete) {
  1329. return resolve(await this.exportImage())
  1330. }
  1331. return resolve({
  1332. success: true,
  1333. msg: '成功'
  1334. })
  1335. })
  1336. }, drawDelayTime || 200)
  1337. })
  1338. }
  1339. /**
  1340. * 预绘画
  1341. * @param { Function } drawArray 绘制内容 返回一个数组
  1342. * @param { Boolean } complete 是否完成,如果为true会绘制图片
  1343. * @returns
  1344. */
  1345. preDraw(drawArray, complete = false) {
  1346. return new Promise(async resolve => {
  1347. try {
  1348. let { callBack, drawTipsText } = this
  1349. showLoading(drawTipsText)
  1350. let drawCanvas = await this.drawCanvas(this.sortDrawArray(drawArray(callBack)))
  1351. // 绘制内容
  1352. if (!drawCanvas.success) {
  1353. hideLoading()
  1354. return resolve(drawCanvas)
  1355. }
  1356. return resolve(await this.canvasDraw(complete))
  1357. } catch(e) {
  1358. hideLoading()
  1359. resolve({
  1360. success: false,
  1361. msg: e
  1362. })
  1363. }
  1364. })
  1365. }
  1366. /**
  1367. * 导出图片
  1368. * @returns
  1369. */
  1370. exportImage() {
  1371. let { canvasId, width, height, _this, delayTime } = this
  1372. return new Promise(resolve => {
  1373. showLoading('加载图片中...')
  1374. // 绘制图片
  1375. setTimeout(async () => {
  1376. resolve(await this.createdCanvasFilePath(canvasId, width, height, _this))
  1377. hideLoading()
  1378. }, delayTime || 200)
  1379. })
  1380. }
  1381. /**
  1382. * 创建绘制海报
  1383. * @param { Function } drawArray 绘制内容 返回一个数组
  1384. * @returns
  1385. */
  1386. createdSharePoster(drawArray) {
  1387. let { callBack } = this
  1388. return new Promise(async resolve => {
  1389. if (drawArray == null || drawArray == undefined) {
  1390. return resolve({
  1391. success: false,
  1392. msg: '请传递绘制内容'
  1393. })
  1394. }
  1395. let backgroundRes = await this.preDrawBackground()
  1396. if (!backgroundRes.success) {
  1397. return resolve(backgroundRes)
  1398. }
  1399. showLoading('绘制中,请稍等')
  1400. let drawRes = await this.drawCanvas(this.sortDrawArray(await drawArray(callBack)))
  1401. // 绘制内容
  1402. if (!drawRes.success) {
  1403. hideLoading()
  1404. return resolve(drawRes)
  1405. }
  1406. return resolve(await this.canvasDraw())
  1407. })
  1408. }
  1409. }