一、index.wxml
1 2 3 4 5 6 7 8 |
<view class="myview"> <view class="mb-20 md lighter text-center">保存到相册</view> <view class="mb-20"> <canvas type="2d" id="myCanvas" style='width:{{canvasWidth}}px; height:{{canvasHeight}}px' class='canvas-style'></canvas> </view> <van-button type="danger" round block bindtap="saveImage">保存图片</van-button> <view class="lighter mt-10 text-center">保存图片到手机相册后,将图片分享到您的圈子</view> </view> |
二、index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
Component({ options: { addGlobalClass: true }, properties: { }, data: { canvasDom: {}, canvas: {}, ctx: {}, dpr: 0, canvasWidth: 200, canvasHeight: 0, posterHeight: 0, bottomInfoHeight: 70, poster: '海报地址', imgFilePath: '', avatarDiam: 30, // 头像直径 avatarUrl: '头像地址', }, ready() { setTimeout(() => { this.init() }, 100) }, methods: { init() { const query = wx.createSelectorQuery().in(this) query.select('#myCanvas').fields({ node: true, size: true }).exec(res => { // console.log(res) const canvas = res[0].node const ctx = canvas.getContext('2d') const dpr = wx.getSystemInfoSync().pixelRatio // 获取设备像素比,未来整体画布根据像素比缩放 this.setData({ canvasDom: res[0], canvas, ctx, dpr }, () => { this.drawing() }) }) }, drawing() { wx.showLoading({ title: '海报生成中' }) this.drawPoster().then(res => { this.drawInfoBg() this.drawAvatar() this.drawQrcode() this.drawText() wx.hideLoading() }) }, drawPoster() { return new Promise((resolve, reject) => { let poster = this.data.canvas.createImage() // 创建一个图片 poster.src = this.data.poster poster.onload = () => { console.log(poster.width, poster.height) this.computeCanvasSize(poster.width, poster.height).then(res => { console.log(res) this.data.ctx.drawImage(poster, 0, 0, poster.width, poster.height, 0, 0, res.width, res.height) resolve(res) }) } }) }, computeCanvasSize(imgWidth: number, imgHeight: number) { return new Promise(resolve => { const canvasWidth = this.data.canvasDom.width // 获取画布宽度 const posterHeight = canvasWidth * (imgHeight / imgWidth) // 计算海报高度 const canvasHeight = posterHeight + this.data.bottomInfoHeight // 计算画布高度 海报高度 + 底部高度 this.setData({ canvasWidth, // 设置画布容器宽 canvasHeight, // 设置画布容器高 posterHeight // 设置海报高 }, () => { this.data.canvas.width = canvasWidth * this.data.dpr // 设置画布宽 this.data.canvas.height = canvasHeight * this.data.dpr // 设置画布高 this.data.ctx.scale(this.data.dpr, this.data.dpr) // 根据像素比放大 setTimeout(() => { resolve({ width: canvasWidth, height: posterHeight }) }, 1000) }) }) }, saveImage() { wx.canvasToTempFilePath({ canvas: this.data.canvas, x: 0, y: 0, width: this.data.canvas.width, height: this.data.canvas.height, destWidth: this.data.canvas.width, // 截取canvas宽度 destHeight: this.data.canvas.height, // 截取canvas高度 fileType: 'jpg', quality: 1, success: res => { this.setData({ imgFilePath: res.tempFilePath }) wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success() { wx.showToast({ title: '已保存到相册', icon: 'success', duration: 3000 }) } }) } }) }, // 绘制底部背景色 drawInfoBg() { const { ctx, canvasWidth, canvasHeight, bottomInfoHeight } = this.data ctx.save() ctx.fillStyle = '#fff' // 设置画布背景色 ctx.fillRect(0, canvasHeight - bottomInfoHeight, canvasWidth, bottomInfoHeight) // 填充整个画布 ctx.restore() }, // 绘制头像 drawAvatar() { const { avatarDiam, ctx, canvas, avatarUrl, canvasHeight } = this.data const photo = canvas.createImage() // 创建一个头像对象 photo.src = avatarUrl // 图像对象地址赋值 photo.onload = () => { const radius = avatarDiam / 2 // 圆形头像半径 const x = 10 // 左上角相对 x 轴距离 const y = canvasHeight - avatarDiam - 10 // 左上角相对 y 轴距离:整体高度 - 头像直径 - 距底部距离 ctx.save() ctx.arc(x + radius, y + radius, radius, 0, 2 * Math.PI) // arc方法画曲线,按照中心点坐标计算,所以要加上半径 ctx.clip() ctx.drawImage(photo, 0, 0, photo.width, photo.height, x, y, avatarDiam, avatarDiam) ctx.restore() } }, // 绘制小程序码 drawQrcode() { const { ctx, canvas, canvasWidth, canvasHeight } = this.data const qrcode = canvas.createImage() qrcode.src = '../../assets/images/qrcode.jpg' qrcode.onload = () => { const x = canvasWidth - 50 const y = canvasHeight - 65 ctx.drawImage(qrcode, 0, 0, qrcode.width, qrcode.height, x, y, 45, 45) ctx.restore() } }, // 绘制文字 drawText() { const { ctx, canvasWidth, canvasHeight, avatarDiam } = this.data ctx.save() ctx.font = '10px PingFang SC, Arial' ctx.fillStyle = '#333' ctx.fillText('标题文字', 10, canvasHeight - 50) ctx.font = '9px PingFang SC, Arial' ctx.fillStyle = '#333' const nickname = '昵称' ctx.fillText(nickname, avatarDiam + 15, canvasHeight - 20) const nicknameWidth = ctx.measureText(nickname).width ctx.fillStyle = '#FDEDEF' // 设置画布背景色 ctx.fillRect(avatarDiam + 20 + nicknameWidth, canvasHeight - 28, 33, 10) // 填充整个画布 ctx.font = '7px PingFang SC, Arial' ctx.fillStyle = '#D55375' ctx.fillText('向您推荐', avatarDiam + 22 + nicknameWidth, canvasHeight - 21) ctx.font = '6px PingFang SC, Arial' ctx.fillStyle = '#999' ctx.fillText('长按识别 去看看', canvasWidth - 50, canvasHeight - 10) ctx.restore() } } }) |
三、index.scss
1 2 3 4 5 6 |
.canvas-style { width: 60%; margin: 0 auto; border-radius: 10rpx; box-shadow: 0 0 4rpx 0 rgba(199, 186, 186, 0.4); } |
四、index.json
1 2 3 4 5 6 7 8 9 10 11 |
{ "component": true, "usingComponents": { "van-tab": "@vant/weapp/tab/index", "van-tabs": "@vant/weapp/tabs/index", "van-button": "@vant/weapp/button/index", "van-toast": "@vant/weapp/toast/index", "van-action-sheet": "@vant/weapp/action-sheet/index" } } |