draw-poster.js 7.7 KB

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