|                                                                                                                                                                                                                                                                                                                                                                                                                                |  | <template>  <uv-popup     ref="popup"    :overlayOpacity="0.6"    mode="center"    bgColor="none"    :zIndex="1000000"    @change="onPopupChange"  >    <view class="popup__view">      <view class="canvas" style="width: 566rpx; height: 1060rpx; overflow: hidden;">        <canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas>      </view>
      <button class="btn" @click="saveImg">        <view class="content">保存到相册</view>      </button>    </view>  </uv-popup></template>
<script>	import { mapState } from 'vuex'
  export default {		data() {			return {				wxCodeImage: '',				baseUrl: 'https://image.hhlm1688.com/',				canvas: {},        retry: 10,			}		},    computed: {			...mapState(['userInfo', 'configList']),    },    async onLoad() {    },    methods: {      open() {        this.retry = 10        this.$refs.popup.open();      },      close() {        this.$refs.popup.close();      },			async fetchQrCode() {        // // todo: delete
        // this.wxCodeImage = 'https://uploadfile.bizhizu.cn/up/e3/64/e0/e364e0f7f6af11f11abdafc22d17b15c.jpg'
        // return
				try {          const path = `pages/index/index?shareId=${this.userInfo.id}`					this.wxCodeImage = (await this.$fetch('getInviteCode', { path }))?.url
				} catch (err) {
				}			},      // 生成有圆角的矩形
      drawBg(ctx, x, y, width, height, radius) {        ctx.beginPath();        ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);        ctx.lineTo(width - radius + x, y);        ctx.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2);        ctx.lineTo(width + x, height + y - radius);        ctx.arc(width - radius + x, height - radius + y, radius, 0, Math.PI * 1 / 2);        ctx.lineTo(radius + x, height + y);        ctx.arc(radius + x, height - radius + y, radius, Math.PI * 1 / 2, Math.PI);        ctx.closePath();        ctx.fillStyle = '#fff'        ctx.fill()      },      drawCoverImg(canvas, ctx, x, y, width, height, radius, lineWidth) {        return new Promise(resolve => {          // 海报图片
          const paperImage = canvas.createImage()          console.log('paperImage', paperImage)          // todo: fetch
          paperImage.src = 'https://i1.hdslb.com/bfs/archive/c0101b4ce06e6bdda803728408e79c8f8b8d0725.jpg'          paperImage.onload = () => {            console.log('paperImage onload')                        ctx.beginPath();            ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);            ctx.lineTo(width - radius + x, y);            ctx.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2);            ctx.lineTo(width + x, height + y);            ctx.lineTo(x, height + y);            ctx.closePath();            ctx.lineWidth = lineWidth;            ctx.strokeStyle = '#F8F8F8';            ctx.stroke();
            ctx.clip();
            ctx.drawImage(paperImage, x, y, width, height)
            resolve()          }        })      },      drawContentBg(ctx, x, y, width, height, radius) {                ctx.beginPath();        // ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
        ctx.moveTo(x, y);        ctx.lineTo(width + x, y);        ctx.lineTo(width + x, height + y - radius);        ctx.arc(width - radius + x, height - radius + y, radius, 0, Math.PI * 1 / 2);        ctx.lineTo(radius + x, height + y);        ctx.arc(radius + x, height - radius + y, radius, Math.PI * 1 / 2, Math.PI);        ctx.closePath();                let gradient = ctx.createLinearGradient(x, y, x, y + height);        gradient.addColorStop('0', '#DAF3FF');        gradient.addColorStop('0.2', '#FBFEFF');        gradient.addColorStop('1.0', '#FBFEFF');        ctx.fillStyle = gradient        ctx.fill()      },      drawAvatar(canvas, ctx, x, y, r) {        console.log('drawAvatar', 'x', x, 'y', y, 'r', r)
        return new Promise(resolve => {          // 头像图片
          const avatarImage = canvas.createImage()          avatarImage.src = this.userInfo.headImage          avatarImage.onload = () => {            console.log('avatarImage onload')                        ctx.beginPath();            console.log('arc', 'x', x + r, 'y', y + r, 'r', r)            ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);            ctx.clip();
            const size = r*2            console.log('size', size)            ctx.drawImage(avatarImage, x, y, size, size)
            resolve()          }        })      },      drawMultilineText(ctx, text, x, y, maxWidth, lineHeight) {        console.log('drawMultilineText', 'x', x, 'y', y)        let line = '';            for (let n = 0; n < text.length; n++) {          const testLine = line + text[n];          const metrics = ctx.measureText(testLine);          const testWidth = metrics.width;          if (testWidth > maxWidth && n > 0) {            ctx.fillText(line, x, y);            console.log(n, 'line', line, 'x', x, 'y', y)            line = text[n];            y += lineHeight;          } else {            line = testLine;          }        }        console.log('line', line, 'x', x, 'y', y)        ctx.fillText(line, x, y);      },      drawQrCodeImg(canvas, ctx, x, y, size) {        return new Promise(resolve => {          //二维码图片
          const coderImage = canvas.createImage()          coderImage.src = this.wxCodeImage          coderImage.onload = () => {            console.log('coderImage onload')            ctx.drawImage(coderImage, x, y, size, size)
            resolve()          }        })      },			draw() {
				wx.createSelectorQuery().in(this)					.select('#myCanvas') // 绘制的canvas的id
					.fields({						node: true,						size: true					})					.exec(async (res) => {            console.log('res', res)            if (!res?.[0]?.node) {              if (!this.retry) {                console.log('retry fail')                return              }              console.log('retry')              this.retry -= 1              setTimeout(() => {                this.draw()              }, 200)              return            }						const canvas = res[0].node						// 渲染上下文
						const ctx = canvas.getContext('2d')						// Canvas 画布的实际绘制宽高
						const width = res[0].width						const height = res[0].height						// 初始化画布大小
						const dpr = wx.getWindowInfo().pixelRatio
						//根据dpr调整
						// dpr  2 4
						//      3 6
						console.log("--dpr", dpr)						canvas.width = width * dpr						canvas.height = height * dpr
						let Ratio = canvas.width / 566
						this.canvas = canvas						ctx.scale(dpr, dpr)						ctx.clearRect(0, 0, width, height)
						ctx.fillStyle = 'transparent'						ctx.fillRect(0, 0, canvas.width, canvas.height)
            ctx.save()                        let radius = 48 * Ratio / dpr            let w = 566 * Ratio / dpr            let h = 1060 * Ratio / dpr            this.drawBg(ctx, 0, 0, w, h, radius)
            ctx.restore();            ctx.save()
            let lineWidth = 2 * Ratio / dpr            let x = lineWidth            let y = lineWidth            w = 566 * Ratio / dpr - lineWidth * 2            h = 400 * Ratio / dpr - lineWidth * 2            await this.drawCoverImg(canvas, ctx, x, y, w, h, radius, lineWidth)
            ctx.restore();            ctx.save()
            x = lineWidth            y = 400 * Ratio / dpr + lineWidth            h = 660 * Ratio / dpr - lineWidth * 2            this.drawContentBg(ctx, x, y, w, h, radius, lineWidth)
            ctx.restore();            ctx.save()
            radius = Math.floor(27 * Ratio / dpr)            x = Math.floor(40 * Ratio / dpr)            y = Math.floor(440 * Ratio / dpr)            await this.drawAvatar(canvas, ctx, x, y, radius)
            ctx.restore();            ctx.save()
            let text = this.userInfo.nickName            let maxWidth = 560 * Ratio / dpr            let lineHeight = 17 * Ratio / dpr            x = 100 * Ratio / dpr            y = 474 * Ratio / dpr            ctx.font = "normal normal normal 11px normal";            ctx.fillStyle = "#7D7D7D";            this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
            ctx.restore();            ctx.save()                        maxWidth = 560 * Ratio / dpr            lineHeight = 60 * Ratio / dpr            ctx.font = "normal normal 600 16px normal";            ctx.fillStyle = "#181818";            text = '邀请您'            x = 40 * Ratio / dpr            y = 564 * Ratio / dpr            this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)            y += lineHeight            text = '探索新世界,开启研学之旅!'            this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)                        ctx.restore();            ctx.save()
            text = '是否渴望一场充满知识与乐趣的冒险?现在,我们诚挚邀请你加入我们的研学小程序,开启一场别开生面的学习之旅!'            maxWidth = 486 * Ratio / dpr            lineHeight = 30 * Ratio / dpr            ctx.font = "normal normal normal 10px normal";            ctx.fillStyle = "#7D7D7D";            x = 40 * Ratio / dpr            y = 690 * Ratio / dpr            this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
            ctx.restore();            ctx.save()                        // 设置虚线样式
            ctx.setLineDash([4, 4]); // 第一个参数是实线长度, 第二个参数是间隔长度
            ctx.lineDashOffset = 2; // 设置虚线的起始偏移量
            ctx.lineWidth = lineWidth;            ctx.strokeStyle = '#DADADA';            // 开始绘制
            ctx.beginPath();            x = 40 * Ratio / dpr            y = 778 * Ratio / dpr            ctx.moveTo(x, y);            x += 486 * Ratio / dpr            ctx.lineTo(x, y);            ctx.stroke();
            ctx.restore();            ctx.save()
            maxWidth = 250 * Ratio / dpr            lineHeight = 44 * Ratio / dpr            ctx.font = "normal normal 600 13px normal";            ctx.fillStyle = "#181818";            text = '立即加入我们,'            x = 40 * Ratio / dpr            y = 879 * Ratio / dpr            this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)            text = '开启你的研学之旅!'            y += lineHeight            this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
            ctx.restore();            ctx.save()
            x = 316 * Ratio / dpr            y = 810 * Ratio / dpr            let size = 210 * Ratio / dpr            await this.drawQrCodeImg(canvas, ctx, x, y, size)
            uni.hideLoading()
					})
			},      async init() {        uni.showLoading({            title: '加载中...'        });
        await this.fetchQrCode()        uni.hideLoading();
				uni.showLoading({					title: "拼命绘画中..."				})        this.draw()      },			saveImg() {				this.$authorize('scope.writePhotosAlbum').then((res) => {					this.imgApi()				})			},			imgApi() {        uni.showLoading({            title: '保存中...'        });				wx.canvasToTempFilePath({					x: 0,					y: 0,					width: this.canvas.width,					height: this.canvas.height,					canvas: this.canvas,					success: (res) => {						let tempFilePath = res.tempFilePath;						this.saveImgToPhone(tempFilePath)					},					fail: (err) => {						console.log('--canvasToTempFilePath--fail', err)				    uni.hideLoading();					}				}, this);
			},			saveImgToPhone(image) {				/* 获取图片的信息 */				uni.getImageInfo({					src: image,					success: function(image) {						/* 保存图片到手机相册 */						uni.saveImageToPhotosAlbum({							filePath: image.path,							success: function() {								uni.showModal({									title: '保存成功',									content: '图片已成功保存到相册',									showCancel: false								});							},							complete(res) {								console.log(res);				        uni.hideLoading();							}						});					}				});			},      onPopupChange(e) {        if (!e.show) {          return        }
        this.init()      },    },  }</script>
<style scoped lang="scss">  .canvas {    border-radius: 48rpx;  }
  .btn {    margin-top: 32rpx;    width: 100%;    padding: 22rpx 0;    box-sizing: border-box;    font-family: PingFang SC;    font-weight: 500;    font-size: 36rpx;    line-height: 1.4;    color: #FFFFFF;    background: linear-gradient(to right, #21FEEC, #019AF9);    border: 2rpx solid #00A9FF;    border-radius: 41rpx;  }</style>
 |