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.

274 lines
8.2 KiB

8 months ago
  1. <template>
  2. <view class="placard">
  3. <view class="placard-content">
  4. <view class="img-box" :style="{ width: canvasW + 'px', height: canvasH + 'px' }">
  5. <img v-show="tempFilePath" :style="{ width: canvasW + 'px', height: canvasH + 'px' }" :src="tempFilePath" alt="" />
  6. </view>
  7. <div class="qrcode" style="display: none;">
  8. <vue-qrcode :value="qrCodeValue" :width="qrCodeSize"
  9. :color="{ dark: qrCodeDarkColor, light: qrCodeLightColor }" :margin="margin"
  10. type="image/png"></vue-qrcode>
  11. </div>
  12. <canvas :style="{ width: canvasW + 'px', height: canvasH + 'px' }"
  13. canvas-id="myCanvas" id="myCanvas"></canvas>
  14. <view class="add-btn">
  15. <view class="btn">
  16. 长按图片保存到手机
  17. </view>
  18. </view>
  19. </view>
  20. </view>
  21. </template>
  22. <script>
  23. import drawTextVertical from '@/utils/Canvas.js'
  24. import VueQrcode from 'vue-qrcode'
  25. export default {
  26. name: 'Placard',
  27. components : { VueQrcode },
  28. data() {
  29. return {
  30. qrCodeValue: import.meta.env.VITE_REDIRECT_URI + `?vid=${this.getUserInfo()}`,
  31. qrCodeSize: 180 * (window.innerWidth / 750),
  32. qrCodeDarkColor: '#000',
  33. qrCodeLightColor: '#fff',
  34. margin: 0,
  35. //画布信息
  36. canvasW: 299,
  37. canvasH: 403,
  38. //设备信息
  39. systemInfo: {},
  40. //图片路径
  41. tempFilePath: '',
  42. _rpx: 0,
  43. _center: 0
  44. }
  45. },
  46. methods: {
  47. async draw() { //绘制海报
  48. let self = this
  49. //获取设备信息
  50. self.systemInfo = await self.getSystemInfo();
  51. //转换相对单位
  52. let rpx = self.systemInfo.windowWidth / 750
  53. self._rpx = rpx
  54. //设置画布宽高
  55. self.canvasW = 542 * rpx
  56. self.canvasH = 731 * rpx
  57. //海报背景
  58. let bg = new Image();
  59. bg.src = import.meta.env.VITE_REDIRECT_URI + '/static/placard/placard-bg.png'
  60. //logo
  61. let logo = new Image();
  62. logo.src = import.meta.env.VITE_REDIRECT_URI + '/static/placard/logo.png'
  63. bg.onload = () => {
  64. logo.onload = () => { //确保背景和logo加载完成(不然海报可能会少logo)
  65. setTimeout(() => {
  66. let ctx = document.querySelector('canvas').getContext('2d')
  67. let img = document.querySelector('.qrcode img')
  68. let center = self.canvasW / 2; //画布中心位置
  69. self._center = center
  70. //绘制图片
  71. ctx.drawImage(bg, 0, 0, self.canvasW, self.canvasH)
  72. ctx.drawImage(img, center - (self.qrCodeSize / 2), 295 * rpx, self.qrCodeSize, self
  73. .qrCodeSize)
  74. ctx.drawImage(logo, center - (100 * rpx / 2) , 60 * rpx , 100 * rpx, 100 * rpx)
  75. //绘制文字
  76. ctx.font = `bold ${40 * rpx}px 楷体`
  77. ctx.textAlign = 'center'
  78. ctx.fillStyle = "#59B495";
  79. ctx.fillText('上门服务', center, 200 * rpx, 400)
  80. ctx.fillStyle = "#0A543B";
  81. ctx.font = `100 ${25 * rpx}px 楷体`
  82. ctx.fillText(`一扫疲劳 舒服入眠`, center, 240 * rpx, 400)
  83. ctx.font = `100 ${25 * rpx}px 宋体`
  84. ctx.fillStyle = "#0A543B";
  85. ctx.textAlign = 'center';
  86. drawTextVertical(ctx, '微信扫码下单', 130 * rpx, 315 * rpx, self.qrCodeSize);
  87. ctx.textAlign = 'center';
  88. drawTextVertical(ctx, '在线选择技师', 400 * rpx, 315 * rpx, self.qrCodeSize);
  89. ctx.font = `100 ${18 * rpx}px 宋体`
  90. ctx.fillStyle = '#053323'
  91. ctx.fillText(`长按扫一扫下单可领取`, center, 525 * rpx, 400)
  92. ctx.font = `bold ${43 * rpx}px 宋体`
  93. ctx.fillStyle = '#EC8D44'
  94. ctx.fillText(`200元`, center, 580 * rpx, 400)
  95. ctx.font = `100 ${18 * rpx}px 宋体`
  96. ctx.fillText(`百万明星技师在线接单`, center, 625 * rpx, 400)
  97. //画图形
  98. this.paintingCapsule(ctx, center - (90 * rpx / 2), 250 * rpx, 90 * rpx, 25 * rpx,
  99. 13 * rpx, '#59B495', 'time')
  100. this.paintingCapsule(ctx, center + (130 * rpx / 2), 530 * rpx, 25 * rpx, 70 * rpx,
  101. 13 * rpx, '#BCEED6', 'coupon')
  102. ////画海报最后的标签列表
  103. this.drawList(ctx, 135 * rpx, 650 * rpx, 60 * rpx, 20 * rpx, 10 * rpx, '#59B495')
  104. uni.canvasToTempFilePath({
  105. x: 0, // 起点坐标
  106. y: 0,
  107. width: self.canvasW, // canvas 宽
  108. height: self.canvasH, // canvas 高
  109. canvasId: 'myCanvas', // canvas id
  110. success(res) {
  111. self.tempFilePath = res.tempFilePath //相对路径
  112. }
  113. })
  114. })
  115. }
  116. }
  117. },
  118. // 获取设备信息
  119. getSystemInfo() {
  120. return new Promise((req, rej) => {
  121. uni.getSystemInfo({
  122. success: function(res) {
  123. req(res)
  124. }
  125. });
  126. })
  127. },
  128. paintingCapsule(ctx, x, y, width, height, radius, color, current) { //画椭圆(胶囊)
  129. // 开始新的路径
  130. ctx.beginPath();
  131. // 绘制左上角圆角
  132. ctx.moveTo(x + radius, y);
  133. ctx.arcTo(x + width, y, x + width, y + height, radius);
  134. // 绘制右上角圆角
  135. ctx.arcTo(x + width, y + height, x, y + height, radius);
  136. // 绘制右下角圆角
  137. ctx.arcTo(x, y + height, x, y, radius);
  138. // 绘制左下角圆角
  139. ctx.arcTo(x, y, x + width, y, radius);
  140. // 闭合路径(实际上在arcTo后已经是闭合的,但显式调用可以增加可读性)
  141. ctx.closePath();
  142. // 设置填充颜色
  143. ctx.fillStyle = color;
  144. // 填充路径
  145. ctx.fill();
  146. //下面是再写不同区域的内容
  147. if (current == 'time') {
  148. ctx.fillStyle = "#fff";
  149. ctx.font = `100 ${17 * this._rpx}px 楷体`
  150. ctx.fillText(`24小时`, this._center, 267 * this._rpx);
  151. } else if (current == 'coupon') {
  152. ctx.fillStyle = "#EC8D44";
  153. ctx.font = `100 ${13 * this._rpx}px 楷体`
  154. let offset = 13 * this._rpx
  155. drawTextVertical(ctx, '优惠券', 349 * this._rpx, 555 * this._rpx, height - offset);
  156. }
  157. },
  158. drawList(ctx, x, y, width, height, radius, color) { //画海报最后的标签列表
  159. let list = ['安全', '正规', '健康', '便捷']
  160. let offset = 15 //偏移量
  161. let currentTagInfo = {
  162. x
  163. }
  164. list.forEach(item => {
  165. // 开始新的路径
  166. ctx.beginPath();
  167. // 绘制左上角圆角
  168. ctx.moveTo(currentTagInfo.x + radius, y);
  169. ctx.arcTo(currentTagInfo.x + width, y, currentTagInfo.x + width, y + height, radius);
  170. // 绘制右上角圆角
  171. ctx.arcTo(currentTagInfo.x + width, y + height, currentTagInfo.x, y + height, radius);
  172. // 绘制右下角圆角
  173. ctx.arcTo(currentTagInfo.x, y + height, currentTagInfo.x, y, radius);
  174. // 绘制左下角圆角
  175. ctx.arcTo(currentTagInfo.x, y, currentTagInfo.x + width, y, radius);
  176. // 设置边框颜色
  177. ctx.strokeStyle = color;
  178. // 设置边框宽度(可选)
  179. ctx.lineWidth = 1; // 或者你想要的任何宽度
  180. // 闭合路径(实际上在arcTo后已经是闭合的,但显式调用可以增加可读性)
  181. ctx.closePath();
  182. // 绘制边框
  183. ctx.stroke();
  184. //圆点
  185. ctx.beginPath();
  186. ctx.fillStyle = color;
  187. ctx.arc(currentTagInfo.x + (10 * this._rpx), y + (10 * this._rpx), 2, 0,360)
  188. ctx.fill()
  189. //绘制文本
  190. ctx.font = `100 ${13 * this._rpx}px 楷体`
  191. ctx.fillText(item, currentTagInfo.x + (33 * this._rpx), y + (15 * this._rpx), 400)
  192. let next = currentTagInfo.x + width + offset; //下一个标签的位置
  193. currentTagInfo = { x : next }
  194. })
  195. },
  196. getUserInfo(){
  197. if(localStorage.getItem('userInfo')){
  198. return JSON.parse(localStorage.getItem('userInfo')).id
  199. }else{
  200. uni.navigateTo({
  201. url: '/pages/login/login'
  202. })
  203. }
  204. }
  205. }
  206. }
  207. </script>
  208. <style lang="scss" scoped>
  209. .placard {
  210. display: flex;
  211. min-height: 100vh;
  212. align-items: center;
  213. justify-content: center;
  214. .placard-content{
  215. display: flex;
  216. flex-direction: column;
  217. align-items: center;
  218. .add-btn {
  219. display: flex;
  220. justify-content: center;
  221. align-items: center;
  222. height: 100rpx;
  223. width: 750rpx;
  224. .btn {
  225. display: flex;
  226. align-items: center;
  227. justify-content: center;
  228. width: 72%;
  229. height: 80rpx;
  230. border-radius: 40rpx;
  231. color: white;
  232. font-size: 28rpx;
  233. background: linear-gradient(180deg, #6FDFBE, #5AC796);
  234. margin-top: 40rpx;
  235. }
  236. }
  237. }
  238. }
  239. canvas{
  240. opacity: 0;
  241. position: fixed;
  242. top: 100%;
  243. left: 0;
  244. }
  245. </style>