draw-poster.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // 默认配置参数
  2. const defaultOptions = () => ({
  3. avatarUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-join-us.png',
  4. userName: '采美',
  5. subTitle: '强烈为你推荐该商城',
  6. coverUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-share.png',
  7. bgImageUrl: 'https://static.caimei365.com/app/mini-hehe/icon/bg-share-01.png',
  8. userBg: 'https://static.caimei365.com/app/img/icon/bg-card.png',
  9. userTitleBg: 'https://static.caimei365.com/app/img/icon/logo-fanbai.png',
  10. // ewmUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-ewm-hehe.jpg',
  11. textArray: [],
  12. scale: 0.5,
  13. duty: {
  14. text: '客户经理',
  15. bgColor: '#FFBB00',
  16. color: '#fff'
  17. }
  18. })
  19. // 绘制海报
  20. export default class DrawPoster {
  21. constructor(ctx, options = {}) {
  22. this.ctx = ctx
  23. this.options = Object.assign(defaultOptions(), options)
  24. console.log('配置信息为:', this.options)
  25. }
  26. // 开始绘制
  27. draw() {
  28. return new Promise(async (resolve, reject) => {
  29. try {
  30. await this.drawBgImage()
  31. // await this.drawHeader()
  32. await this.drawUserCoverImage()
  33. await this.drawCenterInfo()
  34. } catch (e) {
  35. console.log(e)
  36. reject(e)
  37. } finally {
  38. this.ctx.draw(true, res => {
  39. resolve(true)
  40. })
  41. }
  42. })
  43. }
  44. // 绘制背景
  45. async drawBgImage() {
  46. try {
  47. const { bgImageUrl, scale } = this.options
  48. const [error, result] = await uni.downloadFile({ url: bgImageUrl })
  49. if (error) return
  50. this.ctx.setFillStyle('#999999')
  51. this.ctx.save()
  52. return Promise.resolve(true)
  53. } catch (e) {
  54. return Promise.reject(e)
  55. }
  56. }
  57. // 绘制圆角矩形
  58. async drawRoundRect(x, y, w, h, r) {
  59. this.ctx.beginPath()
  60. this.ctx.moveTo(x + r , y)
  61. this.ctx.arcTo(x + w, y, x + w, y + h, r)
  62. this.ctx.arcTo(x + w, y + h, x , y + h, r)
  63. this.ctx.arcTo(x, y + h, x, y, r)
  64. this.ctx.arcTo(x, y, x + w, y , r)
  65. this.ctx.setFillStyle('#FFFFFF')
  66. this.ctx.fill()
  67. this.ctx.clip()
  68. }
  69. // 绘制中心区域
  70. async drawCenterInfo() {
  71. try {
  72. const { scale } = this.options
  73. this.ctx.beginPath()
  74. this.drawRoundRect(28 * scale, 28 * scale, 694 * scale, 878 * scale, 8)
  75. await this.drawCoverImage()
  76. await this.drawFooterText()
  77. await this.drawEwmImage()
  78. return Promise.resolve(true)
  79. } catch (e) {
  80. return Promise.reject(e)
  81. }
  82. }
  83. // 绘制封面图片
  84. async drawCoverImage() {
  85. try {
  86. const { coverUrl, scale } = this.options
  87. const [error, result] = await uni.downloadFile({ url: coverUrl })
  88. if (error) return
  89. this.ctx.drawImage(result.tempFilePath, 56 * scale, 56 * scale, 638 * scale, 658 * scale)
  90. return Promise.resolve(true)
  91. } catch (e) {
  92. return Promise.reject(e)
  93. }
  94. }
  95. // 绘制名片封面
  96. async drawUserCoverImage() {
  97. try {
  98. const { userBg, scale, image, qrCode, linkMan, contractMobile, duty } = this.options
  99. await this.drawBorderReduisImage(userBg, 28 * scale, 920 * scale, 694 * scale, 290 * scale, 8)
  100. await this.drawUserCardInner() // 绘制名片内容
  101. await this.drawCircleImg(image, 120 * scale, 1090 * scale, 30) //头像
  102. this.drawFontText(30 * scale, 200 * scale, 1070 * scale, '#fff', linkMan, 'bold', true)
  103. this.drawFontText(30 * scale, 200 * scale, 1130 * scale, '#fff', contractMobile, '400')
  104. await this.drawBorderReduisImage(qrCode, 550 * scale, 980 * scale, 150 * scale, 150 * scale, 6)
  105. this.drawFontText(20 * scale, 550 * scale, 1160 * scale, '#fff', ['长按或扫二维码', '——联系我——'], '400')
  106. return Promise.resolve(true)
  107. } catch (e) {
  108. return Promise.reject(e)
  109. }
  110. }
  111. async drawUserCardInner() {
  112. try {
  113. const { userTitleBg, scale } = this.options
  114. const [error, result] = await uni.downloadFile({ url: userTitleBg })
  115. if (error) return
  116. this.ctx.drawImage(result.tempFilePath, 56 * scale, 940 * scale, 200 * scale, 70 * scale)
  117. return Promise.resolve(true)
  118. } catch (e) {
  119. return Promise.reject(e)
  120. }
  121. }
  122. async drawCircleImg(img, x, y, r) {
  123. const [error, result] = await uni.downloadFile({ url: img })
  124. console.log('result', result, x, y, r)
  125. if (error) return
  126. this.ctx.save()
  127. this.ctx.beginPath()
  128. let size = 2 * r
  129. this.ctx.arc(x, y, r, 0, 2 * Math.PI)
  130. this.ctx.clip()
  131. this.ctx.drawImage(result.tempFilePath, x - r, y - r, size, size)
  132. console.log('x - r, y - r, size, size', x - r, y - r, size, size)
  133. this.ctx.restore()
  134. }
  135. async drawBorderReduisImage(imgUrl, bg_x, bg_y, bg_w, bg_h, bg_r) {
  136. const [error, result] = await uni.downloadFile({ url: imgUrl })
  137. if (error) return
  138. this.ctx.save()
  139. this.ctx.beginPath()
  140. this.ctx.arc(bg_x + bg_r, bg_y + bg_r, bg_r, Math.PI, Math.PI*1.5)
  141. this.ctx.arc(bg_x + bg_w - bg_r, bg_y + bg_r, bg_r, Math.PI * 1.5, Math.PI * 2)
  142. this.ctx.arc(bg_x + bg_w - bg_r, bg_y + bg_h - bg_r, bg_r, 0, Math.PI * 0.5)
  143. this.ctx.arc(bg_x + bg_r, bg_y + bg_h - bg_r, bg_r, Math.PI * 0.5, Math.PI)
  144. this.ctx.clip()
  145. this.ctx.drawImage(result.tempFilePath, bg_x, bg_y, bg_w, bg_h)
  146. this.ctx.restore()
  147. }
  148. // 绘制文字
  149. drawFontText(fontsize, x, y, color, text, bold, isDuty = false) {
  150. this.ctx.beginPath()
  151. this.ctx.font = `${bold} ${fontsize}px sans-serif`
  152. this.ctx.setFillStyle(color)
  153. if (Array.isArray(text)) {
  154. text.forEach((e, i) => {
  155. this.ctx.fillText(e, x, y + fontsize * i)
  156. })
  157. } else {
  158. this.ctx.fillText(text, x, y)
  159. if (isDuty) {
  160. this.ctx.fillStyle = `${this.options.duty.bgColor}`
  161. this.ctx.fillRect(x + (text.length * fontsize)- 30, y-fontsize, 66, fontsize + 4)
  162. this.ctx.font = `${400} ${fontsize}px sans-serif`
  163. this.ctx.setFillStyle('#fff')
  164. this.ctx.fillText(this.options.duty.text, x + (text.length * fontsize) -30, y)
  165. }
  166. }
  167. }
  168. // 绘制底部
  169. drawFooterText(callback) {
  170. const { scale, textArray } = this.options
  171. if (callback) {
  172. callback({ scale, ctx: this.ctx, textArray })
  173. } else {
  174. textArray.forEach((txt, index) => {
  175. this.ctx.setFontSize(30 * scale)
  176. this.ctx.setFillStyle('#333')
  177. this.ctx.fillText(txt, 56 * scale, (710 + 56 * index) * scale)
  178. })
  179. }
  180. }
  181. // 绘制底部二维码
  182. async drawEwmImage() {
  183. try {
  184. const { ewmUrl, scale } = this.options
  185. const [error, result] = await uni.downloadFile({ url: ewmUrl })
  186. if (error) return
  187. this.ctx.drawImage(result.tempFilePath, 524 * scale, 730 * scale, 160 * scale, 160 * scale)
  188. return Promise.resolve(true)
  189. } catch (e) {
  190. return Promise.reject(e)
  191. }
  192. }
  193. }