draw-poster.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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. ewmUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-ewm-hehe.jpg',
  9. textArray: ['¥500.00¥300.00', '哈喽,赶快来参团吧!这件商品', '拼团买更便宜~'],
  10. scale: 0.5,
  11. })
  12. // 绘制海报
  13. export default class DrawPoster {
  14. constructor(ctx, options = {}) {
  15. this.ctx = ctx
  16. this.options = Object.assign(defaultOptions(), options)
  17. }
  18. // 开始绘制
  19. draw() {
  20. return new Promise(async (resolve, reject) => {
  21. try {
  22. await this.drawBgImage()
  23. await this.drawHeader()
  24. await this.drawCenterInfo()
  25. } catch (e) {
  26. console.log(e)
  27. reject(e)
  28. } finally {
  29. this.ctx.draw(true, res => {
  30. resolve(true)
  31. })
  32. }
  33. })
  34. }
  35. // 绘制海报头部(用户信息区域)
  36. async drawHeader() {
  37. try {
  38. const { avatarUrl, userName, scale, subTitle } = this.options
  39. const [error, result] = await uni.downloadFile({ url: avatarUrl })
  40. if (error) return
  41. // 绘制用户头像
  42. this.ctx.beginPath()
  43. this.ctx.arc(116 * scale, 108 * scale, 60 * scale, 0, 2 * Math.PI)
  44. this.ctx.setFillStyle('#fff')
  45. this.ctx.fill()
  46. this.ctx.clip()
  47. this.ctx.drawImage(result.tempFilePath, 56 * scale, 48 * scale, 120 * scale, 120 * scale)
  48. this.ctx.restore()
  49. // 绘制用户名和推荐语
  50. this.ctx.setFillStyle('#FFFFFF')
  51. this.ctx.font = 'bold 10px sans-serif'
  52. this.ctx.setFontSize(40 * scale)
  53. this.ctx.fillText(userName, 205 * scale, 96 * scale, 350 * scale)
  54. this.ctx.font = 'normal 10px sans-serif'
  55. this.ctx.setFontSize(26 * scale)
  56. this.ctx.fillText(subTitle, 205 * scale, 150 * scale, 350 * scale)
  57. this.ctx.save()
  58. return Promise.resolve(true)
  59. } catch (e) {
  60. return Promise.reject(e)
  61. }
  62. }
  63. // 绘制背景
  64. async drawBgImage() {
  65. try {
  66. const { bgImageUrl, scale } = this.options
  67. const [error, result] = await uni.downloadFile({ url: bgImageUrl })
  68. if (error) return
  69. this.ctx.drawImage(result.tempFilePath, 0, 0, 750 * scale, 1220 * scale)
  70. this.ctx.save()
  71. return Promise.resolve(true)
  72. } catch (e) {
  73. return Promise.reject(e)
  74. }
  75. }
  76. // 绘制中心区域
  77. async drawCenterInfo() {
  78. try {
  79. const { scale } = this.options
  80. this.ctx.beginPath()
  81. this.ctx.rect(28 * scale, 214 * scale, 694 * scale, 978 * scale)
  82. this.ctx.setFillStyle('#FFFFFF')
  83. this.ctx.fill()
  84. this.ctx.clip()
  85. await this.drawCoverImage()
  86. await this.drawFooterText()
  87. await this.drawEwmImage()
  88. return Promise.resolve(true)
  89. } catch (e) {
  90. return Promise.reject(e)
  91. }
  92. }
  93. // 绘制封面图片
  94. async drawCoverImage() {
  95. try {
  96. const { coverUrl, scale } = this.options
  97. const [error, result] = await uni.downloadFile({ url: coverUrl })
  98. if (error) return
  99. this.ctx.drawImage(result.tempFilePath, 56 * scale, 242 * scale, 638 * scale, 638 * scale)
  100. return Promise.resolve(true)
  101. } catch (e) {
  102. return Promise.reject(e)
  103. }
  104. }
  105. // 绘制底部
  106. drawFooterText(callback) {
  107. const { scale, textArray } = this.options
  108. if (callback) {
  109. callback({ scale, ctx: this.ctx, textArray })
  110. } else {
  111. textArray.forEach((txt, index) => {
  112. this.ctx.setFontSize(30 * scale)
  113. this.ctx.setFillStyle('#333')
  114. if (txt && index === 0) {
  115. this.drawPrice(txt)
  116. } else {
  117. this.ctx.fillText(txt, 70 * scale, (980 + 56 * index) * scale)
  118. }
  119. })
  120. }
  121. }
  122. drawPrice(txt) {
  123. const { scale } = this.options
  124. const txts = (txt.startsWith('¥') ? txt.slice(1) : txt).split('¥')
  125. let txtLeft = 60 * scale
  126. txts.forEach((priceText, index) => {
  127. if (index === 0) {
  128. this.ctx.setFillStyle('#FF457B')
  129. this.ctx.setFontSize(24 * scale)
  130. this.ctx.fillText('¥', txtLeft, 954 * scale)
  131. const m1 = this.ctx.measureText('¥')
  132. this.ctx.setFontSize(40 * scale)
  133. this.ctx.fillText(priceText, txtLeft + m1.width, 954 * scale)
  134. const m2 = this.ctx.measureText(priceText)
  135. txtLeft = txtLeft + m1.width + m2.width + 8
  136. } else {
  137. this.ctx.setFillStyle('#999')
  138. this.ctx.setFontSize(24 * scale)
  139. this.ctx.fillText('¥' + priceText, txtLeft, 954 * scale)
  140. let m = this.ctx.measureText('¥' + priceText)
  141. this.ctx.fillRect(txtLeft, 946 * scale, m.width + 4, 1)
  142. }
  143. })
  144. }
  145. // 绘制底部二维码
  146. async drawEwmImage() {
  147. try {
  148. const { ewmUrl, scale } = this.options
  149. const [error, result] = await uni.downloadFile({ url: ewmUrl })
  150. if (error) return
  151. this.ctx.drawImage(result.tempFilePath, 524 * scale, 976 * scale, 160 * scale, 160 * scale)
  152. return Promise.resolve(true)
  153. } catch (e) {
  154. return Promise.reject(e)
  155. }
  156. }
  157. }