draw-poster.js 7.5 KB

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