// 默认配置参数 const defaultOptions = () => ({ avatarUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-join-us.png', userName: '采美', subTitle: '强烈为你推荐该商城', coverUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-share.png', bgImageUrl: 'https://static.caimei365.com/app/mini-hehe/icon/bg-share-01.png', userBg: 'https://static.caimei365.com/app/img/icon/bg-card.png', userTitleBg: 'https://static.caimei365.com/app/img/icon/logo-fanbai.png', // ewmUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-ewm-hehe.jpg', textArray: [], scale: 0.5, }) // 绘制海报 export default class DrawPoster { constructor(ctx, options = {}) { this.ctx = ctx this.options = Object.assign(defaultOptions(), options) console.log('配置信息为:', this.options) } // 开始绘制 draw() { return new Promise(async (resolve, reject) => { try { await this.drawBgImage() // await this.drawHeader() await this.drawUserCoverImage() await this.drawCenterInfo() } catch (e) { console.log(e) reject(e) } finally { this.ctx.draw(true, res => { resolve(true) }) } }) } // 绘制海报头部(用户信息区域) async drawHeader() { try { const { avatarUrl, userName, scale, subTitle } = this.options const [error, result] = await uni.downloadFile({ url: avatarUrl }) if (error) return // 绘制用户头像 this.ctx.beginPath() this.ctx.arc(116 * scale, 108 * scale, 60 * scale, 0, 2 * Math.PI) this.ctx.setFillStyle('#fff') this.ctx.fill() this.ctx.clip() this.ctx.drawImage(result.tempFilePath, 56 * scale, 48 * scale, 120 * scale, 120 * scale) this.ctx.restore() // 绘制用户名和推荐语 this.ctx.setFillStyle('#FFFFFF') this.ctx.font = 'bold 10px sans-serif' this.ctx.setFontSize(40 * scale) this.ctx.fillText(userName, 205 * scale, 96 * scale, 350 * scale) this.ctx.font = 'normal 10px sans-serif' this.ctx.setFontSize(26 * scale) this.ctx.fillText(subTitle, 205 * scale, 150 * scale, 350 * scale) this.ctx.save() return Promise.resolve(true) } catch (e) { return Promise.reject(e) } } // 绘制背景 async drawBgImage() { try { const { bgImageUrl, scale } = this.options const [error, result] = await uni.downloadFile({ url: bgImageUrl }) if (error) return this.ctx.setFillStyle('#999999') // this.ctx.drawImage(result.tempFilePath, 0, 0, 750 * scale, 1220 * scale) this.ctx.save() return Promise.resolve(true) } catch (e) { return Promise.reject(e) } } // 绘制圆角矩形 async drawRoundRect(x, y, w, h, r) { this.ctx.beginPath() this.ctx.moveTo(x + r , y) this.ctx.arcTo(x + w, y, x + w, y + h, r) this.ctx.arcTo(x + w, y + h, x , y + h, r) this.ctx.arcTo(x, y + h, x, y, r) this.ctx.arcTo(x, y, x + w, y , r) this.ctx.setFillStyle('#FFFFFF') this.ctx.fill() this.ctx.clip() } // 绘制中心区域 async drawCenterInfo() { try { const { scale } = this.options this.ctx.beginPath() this.drawRoundRect(28 * scale, 28 * scale, 694 * scale, 878 * scale, 8) // this.ctx.rect(28 * scale, 28 * scale, 694 * scale, 878 * scale) await this.drawCoverImage() await this.drawFooterText() await this.drawEwmImage() return Promise.resolve(true) } catch (e) { return Promise.reject(e) } } // 绘制封面图片 async drawCoverImage() { try { const { coverUrl, scale } = this.options const [error, result] = await uni.downloadFile({ url: coverUrl }) if (error) return this.ctx.drawImage(result.tempFilePath, 56 * scale, 24 * scale, 638 * scale, 678 * scale) return Promise.resolve(true) } catch (e) { return Promise.reject(e) } } // 绘制名片封面 async drawUserCoverImage() { try { const { userBg, scale } = this.options const [error, result] = await uni.downloadFile({ url: userBg }) if (error) return this.drawBorderReduisImage(result.tempFilePath, 28 * scale, 920 * scale, 694 * scale, 290 * scale, 8) await this.drawUserCardInner() // 绘制名片内容 return Promise.resolve(true) } catch (e) { return Promise.reject(e) } } async drawUserCardInner() { try { const { userTitleBg, scale, avatarUrl } = this.options const [error, result] = await uni.downloadFile({ url: userTitleBg }) if (error) return // this.drawBorderReduisImage(result.tempFilePath, 28 * scale, 920 * scale, 694 * scale, 280 * scale, 8) this.ctx.drawImage(result.tempFilePath, 56 * scale, 940 * scale, 200 * scale, 70 * scale) await this.drawCircleImg(avatarUrl, 170 * scale, 1000 * scale, 50) return Promise.resolve(true) } catch (e) { return Promise.reject(e) } } async drawCircleImg(img, x, y, r) { const [error, result] = await uni.downloadFile({ url: img }) if (error) return this.ctx.save() this.ctx.beginPath() let size = 2 * r this.ctx.arc(x, y, r, 0, 2 * Math.PI) this.ctx.clip() this.ctx.drawImage(result.tempFilePath, x - r, y - r, size, size) this.ctx.restore() } drawBorderReduisImage(imgUrl, bg_x, bg_y, bg_w, bg_h, bg_r) { this.ctx.save() this.ctx.beginPath() this.ctx.arc(bg_x + bg_r, bg_y + bg_r, bg_r, Math.PI, Math.PI*1.5) this.ctx.arc(bg_x + bg_w - bg_r, bg_y + bg_r, bg_r, Math.PI * 1.5, Math.PI * 2) this.ctx.arc(bg_x + bg_w - bg_r, bg_y + bg_h - bg_r, bg_r, 0, Math.PI * 0.5) this.ctx.arc(bg_x + bg_r, bg_y + bg_h - bg_r, bg_r, Math.PI * 0.5, Math.PI) this.ctx.clip() this.ctx.drawImage(imgUrl, bg_x, bg_y, bg_w, bg_h) this.ctx.restore() } // 绘制底部 drawFooterText(callback) { const { scale, textArray } = this.options if (callback) { callback({ scale, ctx: this.ctx, textArray }) } else { textArray.forEach((txt, index) => { this.ctx.setFontSize(30 * scale) this.ctx.setFillStyle('#333') if (txt && index === 0) { this.drawPrice(txt) } else { this.ctx.fillText(txt, 56 * scale, (678 + 56 * index) * scale) } }) } } drawPrice(txt) { const { scale } = this.options const txts = (txt.startsWith('¥') ? txt.slice(1) : txt).split('¥') let txtLeft = 60 * scale txts.forEach((priceText, index) => { if (index === 0) { this.ctx.setFillStyle('#FF457B') this.ctx.setFontSize(24 * scale) this.ctx.fillText('¥', txtLeft, 954 * scale) const m1 = this.ctx.measureText('¥') this.ctx.setFontSize(40 * scale) this.ctx.fillText(priceText, txtLeft + m1.width, 954 * scale) const m2 = this.ctx.measureText(priceText) txtLeft = txtLeft + m1.width + m2.width + 8 } else { this.ctx.setFillStyle('#999') this.ctx.setFontSize(24 * scale) this.ctx.fillText('¥' + priceText, txtLeft, 954 * scale) let m = this.ctx.measureText('¥' + priceText) this.ctx.fillRect(txtLeft, 946 * scale, m.width + 4, 1) } }) } // 绘制底部二维码 async drawEwmImage() { try { const { ewmUrl, scale } = this.options const [error, result] = await uni.downloadFile({ url: ewmUrl }) if (error) return this.ctx.drawImage(result.tempFilePath, 524 * scale, 678 * scale, 160 * scale, 160 * scale) return Promise.resolve(true) } catch (e) { return Promise.reject(e) } } }