吉光研途前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
6.1 KiB

  1. <template>
  2. <view class="page__view">
  3. <!-- 导航栏 -->
  4. <navbar leftClick @leftClick="$utils.navigateBack" bgColor="#4883F9" color="#FFFFFF" />
  5. <view style="width: 750rpx; height: 1184rpx; overflow: hidden;">
  6. <canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas>
  7. </view>
  8. <view class="flex bottom">
  9. <view class="flex">
  10. <button class="btn" @click="saveImg">保存到手机</button>
  11. </view>
  12. </view>
  13. </view>
  14. </template>
  15. <script>
  16. export default {
  17. data() {
  18. return {
  19. wxCodeImage: '',
  20. baseUrl: 'https://image.hhlm1688.com/',
  21. canvas: {},
  22. posterData: {},
  23. }
  24. },
  25. async onLoad() {
  26. uni.showLoading({
  27. title: '加载中...'
  28. });
  29. this.posterData = uni.getStorageSync('posterData')
  30. await this.fetchQrCode(this.posterData.path)
  31. uni.hideLoading();
  32. this.draw()
  33. },
  34. methods: {
  35. async fetchQrCode(path) {
  36. try {
  37. this.wxCodeImage = (await this.$fetch('getInviteCode', { path }))?.url
  38. } catch (err) {
  39. }
  40. },
  41. drawTextWithManualLineBreaks(ctx, text, x, y, maxWidth, lineHeight) {
  42. let rows = 0
  43. let size = 0
  44. let lines = text.split('\n'); // 支持手动输入的换行符
  45. for (let lIdx = 0; lIdx < lines.length; lIdx++) {
  46. let line = lines[lIdx]
  47. let row = ''
  48. for (let wIdx = 0; wIdx < line.length; wIdx++) {
  49. let word = line[wIdx]
  50. let metrics = ctx.measureText(row + word);
  51. if (metrics.width > maxWidth) {
  52. rows++
  53. size += row.length
  54. console.log('--rows', rows, '--size', size)
  55. if (rows === 4 && size < text.length) {
  56. row = row.slice(0, -2) + '......'
  57. }
  58. ctx.fillText(row, x, y);
  59. row = ''
  60. y += lineHeight
  61. if (rows === 4) {
  62. break
  63. }
  64. } else {
  65. row += word
  66. }
  67. }
  68. if (rows === 4) {
  69. break
  70. }
  71. if (row) {
  72. rows++
  73. size += row.length
  74. console.log('--rows', rows, '--size', size)
  75. if (rows === 4 && size < text.length) {
  76. row = row.slice(0, -2) + '......'
  77. }
  78. ctx.fillText(row, x, y);
  79. y += lineHeight
  80. if (rows === 4) {
  81. break
  82. }
  83. }
  84. }
  85. },
  86. draw() {
  87. uni.showLoading({
  88. title: "拼命绘画中..."
  89. })
  90. wx.createSelectorQuery()
  91. .select('#myCanvas') // 绘制的canvas的id
  92. .fields({
  93. node: true,
  94. size: true
  95. })
  96. .exec(async (res) => {
  97. const canvas = res[0].node
  98. // 渲染上下文
  99. const ctx = canvas.getContext('2d')
  100. // Canvas 画布的实际绘制宽高
  101. const width = res[0].width
  102. const height = res[0].height
  103. // 初始化画布大小
  104. const dpr = wx.getWindowInfo().pixelRatio
  105. //根据dpr调整
  106. // dpr 2 4
  107. // 3 6
  108. console.log("--dpr", dpr)
  109. canvas.width = width * dpr
  110. canvas.height = height * dpr
  111. let Ratio = canvas.width / 750
  112. this.canvas = canvas
  113. ctx.scale(dpr, dpr)
  114. ctx.clearRect(0, 0, width, height)
  115. ctx.fillStyle = '#fff'
  116. ctx.fillRect(0, 0, canvas.width, canvas.height)
  117. ctx.fillStyle = '#999999';
  118. const fontSize = 28 * Ratio / dpr
  119. ctx.font = `${fontSize}px PingFangSC-regular`;
  120. const descX = 37 * Ratio / dpr
  121. const descY = 751 * Ratio / dpr
  122. const maxWidth = 677 * Ratio / dpr
  123. const lineHeight = 45 * Ratio / dpr
  124. const text = this.posterData.paperDesc
  125. this.drawTextWithManualLineBreaks(ctx, text, descX, descY, maxWidth, lineHeight)
  126. // 海报图片
  127. const paperImage = canvas.createImage()
  128. paperImage.src = this.posterData.paperImage
  129. paperImage.onload = () => {
  130. const x = 37 * Ratio / dpr
  131. const y = 21 * Ratio / dpr
  132. const w = 677 * Ratio / dpr
  133. const h = 687 * Ratio / dpr
  134. ctx.drawImage(paperImage, x, y, w, h)
  135. //二维码图片
  136. const coderImage = canvas.createImage()
  137. coderImage.src = this.wxCodeImage
  138. coderImage.onload = () => {
  139. const x = 539 * Ratio / dpr
  140. const y = 987 * Ratio / dpr
  141. const size = 162 * Ratio / dpr
  142. ctx.drawImage(coderImage, x, y, size, size)
  143. uni.hideLoading()
  144. }
  145. }
  146. })
  147. },
  148. saveImg() {
  149. this.$authorize('scope.writePhotosAlbum').then((res) => {
  150. this.imgApi()
  151. })
  152. },
  153. imgApi() {
  154. uni.showLoading({
  155. title: '保存中...'
  156. });
  157. wx.canvasToTempFilePath({
  158. x: 0,
  159. y: 0,
  160. width: this.canvas.width,
  161. height: this.canvas.height,
  162. canvas: this.canvas,
  163. success: (res) => {
  164. let tempFilePath = res.tempFilePath;
  165. this.saveImgToPhone(tempFilePath)
  166. },
  167. fail: (err) => {
  168. console.log('--canvasToTempFilePath--fail', err)
  169. uni.hideLoading();
  170. }
  171. }, this);
  172. },
  173. saveImgToPhone(image) {
  174. /* 获取图片的信息 */
  175. uni.getImageInfo({
  176. src: image,
  177. success: function(image) {
  178. /* 保存图片到手机相册 */
  179. uni.saveImageToPhotosAlbum({
  180. filePath: image.path,
  181. success: function() {
  182. uni.showModal({
  183. title: '保存成功',
  184. content: '图片已成功保存到相册',
  185. showCancel: false
  186. });
  187. },
  188. complete(res) {
  189. console.log(res);
  190. uni.hideLoading();
  191. }
  192. });
  193. }
  194. });
  195. }
  196. },
  197. }
  198. </script>
  199. <style scoped lang="scss">
  200. .page__view {
  201. background: $uni-bg-color-grey;
  202. }
  203. .bottom {
  204. position: fixed;
  205. left: 0;
  206. bottom: 0;
  207. width: 100vw;
  208. height: 154rpx;
  209. padding-bottom: env(safe-area-inset-bottom);
  210. background: #FFFFFF;
  211. .btn {
  212. padding: 20rpx 77rpx;
  213. font-size: 28rpx;
  214. color: #FFFFFF;
  215. background: #4883F9;
  216. border-radius: 14rpx;
  217. }
  218. }
  219. </style>