湘妃到家前端代码仓库
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.

292 lines
8.1 KiB

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