| @ -0,0 +1,261 @@ | |||
| <template> | |||
| <view class="page__view"> | |||
| <!-- 导航栏 --> | |||
| <navbar leftClick @leftClick="$utils.navigateBack" bgColor="#4883F9" color="#FFFFFF" /> | |||
| <view style="width: 750rpx; height: 1184rpx; overflow: hidden;"> | |||
| <canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas> | |||
| </view> | |||
| <view class="flex bottom"> | |||
| <view class="flex"> | |||
| <button class="btn" @click="saveImg">保存到手机</button> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| data() { | |||
| return { | |||
| wxCodeImage: '', | |||
| baseUrl: 'https://image.hhlm1688.com/', | |||
| canvas: {}, | |||
| details: {}, | |||
| } | |||
| }, | |||
| async onLoad({ thesisId }) { | |||
| uni.showLoading({ | |||
| title: '加载中...' | |||
| }); | |||
| await this.getData(thesisId) | |||
| await this.fetchQrCode(thesisId) | |||
| uni.hideLoading(); | |||
| this.draw() | |||
| }, | |||
| methods: { | |||
| async getData(thesisId) { | |||
| try { | |||
| this.details = await this.$fetch('queryThesisById', { thesisId }) | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| async fetchQrCode(thesisId) { | |||
| try { | |||
| const path = `pages_order/thesis/index?thesisId=${thesisId}` | |||
| this.wxCodeImage = (await this.$fetch('getInviteCode', { path }))?.url | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| drawTextWithManualLineBreaks(ctx, text, x, y, maxWidth, lineHeight) { | |||
| let rows = 0 | |||
| let size = 0 | |||
| let lines = text.split('\n'); // 支持手动输入的换行符 | |||
| for (let lIdx = 0; lIdx < lines.length; lIdx++) { | |||
| let line = lines[lIdx] | |||
| let row = '' | |||
| for (let wIdx = 0; wIdx < line.length; wIdx++) { | |||
| let word = line[wIdx] | |||
| let metrics = ctx.measureText(row + word); | |||
| if (metrics.width > maxWidth) { | |||
| rows++ | |||
| size += row.length | |||
| console.log('--rows', rows, '--size', size) | |||
| if (rows === 4 && size < text.length) { | |||
| row = row.slice(0, -2) + '......' | |||
| } | |||
| ctx.fillText(row, x, y); | |||
| row = '' | |||
| y += lineHeight | |||
| if (rows === 4) { | |||
| break | |||
| } | |||
| } else { | |||
| row += word | |||
| } | |||
| } | |||
| if (rows === 4) { | |||
| break | |||
| } | |||
| if (row) { | |||
| rows++ | |||
| size += row.length | |||
| console.log('--rows', rows, '--size', size) | |||
| if (rows === 4 && size < text.length) { | |||
| row = row.slice(0, -2) + '......' | |||
| } | |||
| ctx.fillText(row, x, y); | |||
| y += lineHeight | |||
| if (rows === 4) { | |||
| break | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| draw() { | |||
| uni.showLoading({ | |||
| title: "拼命绘画中..." | |||
| }) | |||
| wx.createSelectorQuery() | |||
| .select('#myCanvas') // 绘制的canvas的id | |||
| .fields({ | |||
| node: true, | |||
| size: true | |||
| }) | |||
| .exec(async (res) => { | |||
| 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 / 750 | |||
| this.canvas = canvas | |||
| ctx.scale(dpr, dpr) | |||
| ctx.clearRect(0, 0, width, height) | |||
| ctx.fillStyle = '#fff' | |||
| ctx.fillRect(0, 0, canvas.width, canvas.height) | |||
| ctx.fillStyle = '#999999'; | |||
| const fontSize = 28 * Ratio / dpr | |||
| ctx.font = `${fontSize}px PingFangSC-regular`; | |||
| const descX = 37 * Ratio / dpr | |||
| const descY = 751 * Ratio / dpr | |||
| const maxWidth = 677 * Ratio / dpr | |||
| const lineHeight = 45 * Ratio / dpr | |||
| const text = this.details.paperDesc || this.details.title | |||
| this.drawTextWithManualLineBreaks(ctx, text, descX, descY, maxWidth, lineHeight) | |||
| // 海报图片 | |||
| const paperImage = canvas.createImage() | |||
| paperImage.src = this.details.paperImage || this.details.image?.split?.(',')?.[0] | |||
| paperImage.onload = () => { | |||
| const x = 37 * Ratio / dpr | |||
| const y = 21 * Ratio / dpr | |||
| const w = 677 * Ratio / dpr | |||
| const h = 687 * Ratio / dpr | |||
| ctx.drawImage(paperImage, x, y, w, h) | |||
| //二维码图片 | |||
| const coderImage = canvas.createImage() | |||
| coderImage.src = this.wxCodeImage | |||
| coderImage.onload = () => { | |||
| const x = 539 * Ratio / dpr | |||
| const y = 987 * Ratio / dpr | |||
| const size = 162 * Ratio / dpr | |||
| ctx.drawImage(coderImage, x, y, size, size) | |||
| uni.hideLoading() | |||
| } | |||
| } | |||
| }) | |||
| }, | |||
| 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(); | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| }, | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .page__view { | |||
| background: $uni-bg-color-grey; | |||
| } | |||
| .bottom { | |||
| position: fixed; | |||
| left: 0; | |||
| bottom: 0; | |||
| width: 100vw; | |||
| height: 154rpx; | |||
| padding-bottom: env(safe-area-inset-bottom); | |||
| background: #FFFFFF; | |||
| .btn { | |||
| padding: 20rpx 77rpx; | |||
| font-size: 28rpx; | |||
| color: #FFFFFF; | |||
| background: #4883F9; | |||
| border-radius: 14rpx; | |||
| } | |||
| } | |||
| </style> | |||