draw-poster.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. })
  14. // 绘制海报
  15. export default class DrawPoster {
  16. constructor(ctx, options = {}) {
  17. this.ctx = ctx
  18. this.options = Object.assign(defaultOptions(), options)
  19. console.log('配置信息为:', this.options)
  20. }
  21. // 开始绘制
  22. draw() {
  23. return new Promise(async (resolve, reject) => {
  24. try {
  25. await this.drawBgImage()
  26. // await this.drawHeader()
  27. await this.drawUserCoverImage()
  28. await this.drawCenterInfo()
  29. } catch (e) {
  30. console.log(e)
  31. reject(e)
  32. } finally {
  33. this.ctx.draw(true, res => {
  34. resolve(true)
  35. })
  36. }
  37. })
  38. }
  39. // 绘制海报头部(用户信息区域)
  40. async drawHeader() {
  41. try {
  42. const { avatarUrl, userName, scale, subTitle } = this.options
  43. const [error, result] = await uni.downloadFile({ url: avatarUrl })
  44. if (error) return
  45. // 绘制用户头像
  46. this.ctx.beginPath()
  47. this.ctx.arc(116 * scale, 108 * scale, 60 * scale, 0, 2 * Math.PI)
  48. this.ctx.setFillStyle('#fff')
  49. this.ctx.fill()
  50. this.ctx.clip()
  51. this.ctx.drawImage(result.tempFilePath, 56 * scale, 48 * scale, 120 * scale, 120 * scale)
  52. this.ctx.restore()
  53. // 绘制用户名和推荐语
  54. this.ctx.setFillStyle('#FFFFFF')
  55. this.ctx.font = 'bold 10px sans-serif'
  56. this.ctx.setFontSize(40 * scale)
  57. this.ctx.fillText(userName, 205 * scale, 96 * scale, 350 * scale)
  58. this.ctx.font = 'normal 10px sans-serif'
  59. this.ctx.setFontSize(26 * scale)
  60. this.ctx.fillText(subTitle, 205 * scale, 150 * scale, 350 * scale)
  61. this.ctx.save()
  62. return Promise.resolve(true)
  63. } catch (e) {
  64. return Promise.reject(e)
  65. }
  66. }
  67. // 绘制背景
  68. async drawBgImage() {
  69. try {
  70. const { bgImageUrl, scale } = this.options
  71. const [error, result] = await uni.downloadFile({ url: bgImageUrl })
  72. if (error) return
  73. this.ctx.setFillStyle('#999999')
  74. // this.ctx.drawImage(result.tempFilePath, 0, 0, 750 * scale, 1220 * scale)
  75. this.ctx.save()
  76. return Promise.resolve(true)
  77. } catch (e) {
  78. return Promise.reject(e)
  79. }
  80. }
  81. // 绘制圆角矩形
  82. async drawRoundRect(x, y, w, h, r) {
  83. this.ctx.beginPath()
  84. this.ctx.moveTo(x + r , y)
  85. this.ctx.arcTo(x + w, y, x + w, y + h, r)
  86. this.ctx.arcTo(x + w, y + h, x , y + h, r)
  87. this.ctx.arcTo(x, y + h, x, y, r)
  88. this.ctx.arcTo(x, y, x + w, y , r)
  89. this.ctx.setFillStyle('#FFFFFF')
  90. this.ctx.fill()
  91. this.ctx.clip()
  92. }
  93. // 绘制中心区域
  94. async drawCenterInfo() {
  95. try {
  96. const { scale } = this.options
  97. this.ctx.beginPath()
  98. this.drawRoundRect(28 * scale, 28 * scale, 694 * scale, 878 * scale, 8)
  99. // this.ctx.rect(28 * scale, 28 * scale, 694 * scale, 878 * scale)
  100. await this.drawCoverImage()
  101. await this.drawFooterText()
  102. await this.drawEwmImage()
  103. return Promise.resolve(true)
  104. } catch (e) {
  105. return Promise.reject(e)
  106. }
  107. }
  108. // 绘制封面图片
  109. async drawCoverImage() {
  110. try {
  111. const { coverUrl, scale } = this.options
  112. const [error, result] = await uni.downloadFile({ url: coverUrl })
  113. if (error) return
  114. this.ctx.drawImage(result.tempFilePath, 56 * scale, 24 * scale, 638 * scale, 678 * scale)
  115. return Promise.resolve(true)
  116. } catch (e) {
  117. return Promise.reject(e)
  118. }
  119. }
  120. // 绘制名片封面
  121. async drawUserCoverImage() {
  122. try {
  123. const { userBg, scale } = this.options
  124. const [error, result] = await uni.downloadFile({ url: userBg })
  125. if (error) return
  126. this.drawBorderReduisImage(result.tempFilePath, 28 * scale, 920 * scale, 694 * scale, 290 * scale, 8)
  127. await this.drawUserCardInner() // 绘制名片内容
  128. return Promise.resolve(true)
  129. } catch (e) {
  130. return Promise.reject(e)
  131. }
  132. }
  133. async drawUserCardInner() {
  134. try {
  135. const { userTitleBg, scale, avatarUrl } = this.options
  136. const [error, result] = await uni.downloadFile({ url: userTitleBg })
  137. if (error) return
  138. // this.drawBorderReduisImage(result.tempFilePath, 28 * scale, 920 * scale, 694 * scale, 280 * scale, 8)
  139. this.ctx.drawImage(result.tempFilePath, 56 * scale, 940 * scale, 200 * scale, 70 * scale)
  140. await this.drawCircleImg(avatarUrl, 170 * scale, 1000 * scale, 50)
  141. return Promise.resolve(true)
  142. } catch (e) {
  143. return Promise.reject(e)
  144. }
  145. }
  146. async drawCircleImg(img, x, y, r) {
  147. const [error, result] = await uni.downloadFile({ url: img })
  148. if (error) return
  149. this.ctx.save()
  150. this.ctx.beginPath()
  151. let size = 2 * r
  152. this.ctx.arc(x, y, r, 0, 2 * Math.PI)
  153. this.ctx.clip()
  154. this.ctx.drawImage(result.tempFilePath, x - r, y - r, size, size)
  155. this.ctx.restore()
  156. }
  157. drawBorderReduisImage(imgUrl, bg_x, bg_y, bg_w, bg_h, bg_r) {
  158. this.ctx.save()
  159. this.ctx.beginPath()
  160. this.ctx.arc(bg_x + bg_r, bg_y + bg_r, bg_r, Math.PI, Math.PI*1.5)
  161. this.ctx.arc(bg_x + bg_w - bg_r, bg_y + bg_r, bg_r, Math.PI * 1.5, Math.PI * 2)
  162. this.ctx.arc(bg_x + bg_w - bg_r, bg_y + bg_h - bg_r, bg_r, 0, Math.PI * 0.5)
  163. this.ctx.arc(bg_x + bg_r, bg_y + bg_h - bg_r, bg_r, Math.PI * 0.5, Math.PI)
  164. this.ctx.clip()
  165. this.ctx.drawImage(imgUrl, bg_x, bg_y, bg_w, bg_h)
  166. this.ctx.restore()
  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. if (txt && index === 0) {
  178. this.drawPrice(txt)
  179. } else {
  180. this.ctx.fillText(txt, 56 * scale, (678 + 56 * index) * scale)
  181. }
  182. })
  183. }
  184. }
  185. drawPrice(txt) {
  186. const { scale } = this.options
  187. const txts = (txt.startsWith('¥') ? txt.slice(1) : txt).split('¥')
  188. let txtLeft = 60 * scale
  189. txts.forEach((priceText, index) => {
  190. if (index === 0) {
  191. this.ctx.setFillStyle('#FF457B')
  192. this.ctx.setFontSize(24 * scale)
  193. this.ctx.fillText('¥', txtLeft, 954 * scale)
  194. const m1 = this.ctx.measureText('¥')
  195. this.ctx.setFontSize(40 * scale)
  196. this.ctx.fillText(priceText, txtLeft + m1.width, 954 * scale)
  197. const m2 = this.ctx.measureText(priceText)
  198. txtLeft = txtLeft + m1.width + m2.width + 8
  199. } else {
  200. this.ctx.setFillStyle('#999')
  201. this.ctx.setFontSize(24 * scale)
  202. this.ctx.fillText('¥' + priceText, txtLeft, 954 * scale)
  203. let m = this.ctx.measureText('¥' + priceText)
  204. this.ctx.fillRect(txtLeft, 946 * scale, m.width + 4, 1)
  205. }
  206. })
  207. }
  208. // 绘制底部二维码
  209. async drawEwmImage() {
  210. try {
  211. const { ewmUrl, scale } = this.options
  212. const [error, result] = await uni.downloadFile({ url: ewmUrl })
  213. if (error) return
  214. this.ctx.drawImage(result.tempFilePath, 524 * scale, 678 * scale, 160 * scale, 160 * scale)
  215. return Promise.resolve(true)
  216. } catch (e) {
  217. return Promise.reject(e)
  218. }
  219. }
  220. }