最终生成的样式
注释已经打好,详情看代码
<template> <view class="canvas-box"> <!-- 导航栏 --> <view class="nav-box"> <view class="title-top" :style="'padding-top:' + statusBarHeight + 'rpx'"> <u-icon class="title-icon" name="arrow-left" color="#ffffff" size="36" @click="getBack"></u-icon> <text>海报分享</text> </view> </view> <!-- 开发完成之前,取消 fixed;opacity: 0;--> <canvas style=" 346px;height: 500px;position: fixed;opacity: 0;" class="canvas" canvas-id="canvasID"></canvas> <!-- 完成海报制作后,需要把canvas移到看不见的地方,或者隐藏,把image显示出来 --> <image :src="imgUrl" mode=""></image> <view class="footer"> <view class="download" @click="saveImage"> <!-- 小于符号图标 --> <u-icon name="download" color="#ffffff" size="34"></u-icon> <text>保存到相册</text> </view> </view> </view> </template> <script> export default { data() { return { imgUrl: '', statusBarHeight: 0, avatar: '../../../../static/images/default-chat-avatar.png', //头像地址 hello: '../../../../static/images/share/weixin-hi.png', // hello图标 mony: '../../../../static/images/share/mony.png' //圆的钱图标 } }, created() { this.$tool.getSystemInfo().then(res => { this.statusBarHeight = res }) }, mounted() { let ctx = uni.createCanvasContext('canvasID', this); // ctx.setFillStyle("transparent"); //设置canvas背景颜色 // ctx.fillRect(0, 0, 346, 500) //设置canvas画布大小 this.fillRoundRect(ctx, 0, 0, 346, 500, 15); //绘制一个圆角矩形 this.fillRoundRect(ctx, 0, 0, 346, 182, 15, '#333231'); //绘制一个圆角矩形 this.drawCircular(ctx, this.avatar, 36, 32, 50, 50) //绘制圆形头像 ctx.setFontSize(18) ctx.setFillStyle("#ffffff") ctx.fillText('明天依然是晴天', 98, 65) ctx.drawImage(this.hello, 240, 10, 86, 86) //二维码 ctx.font = '20px normal' ctx.setFillStyle("#09CFB1") ctx.fillText('我为“贤马”带盐', 30, 122) ctx.font = '16px normal' ctx.setFillStyle("#ffffff") ctx.fillText('“闲么?上贤马做兼职”', 20, 152) // 绘制职位标题,多余文字自动换行 ctx.setFontSize(28) ctx.setFillStyle("#333333") let str = '店铺实习生ZAra重庆龙湖时代' // 字符串总长度 let _strLength = str.length // 总结截取次数 let _strNum = Math.ceil(_strLength / 9) // 每次开始截取字符串的索引 let _strHeight = 0 // 绘制的字体 x,y的初始位置 let _strX = 27, _strY = 223 let strIndex = 223 // 开始截取 for (let i = 0; i < _strNum; i++) { strIndex = _strY + i * 40 ctx.fillText(str.substr(_strHeight + i * 9, 9), _strX, _strY + i * 40) } strIndex += 30 ctx.setFontSize(14) ctx.setFillStyle("#1BB99A") let strtitle = '环境好/结算快/时间短' ctx.fillText(strtitle, _strX, strIndex) ctx.setFontSize(20) ctx.setFillStyle("#333231") ctx.fillText('16元/小时', _strX, 418) this.drawCircular(ctx, this.mony, _strX, 429, 14, 14) //绘制圆形头像 ctx.setFontSize(12) ctx.setFillStyle("#F7BA65") ctx.fillText('已预付', _strX + 20, 440) // 绘制微信二维码 ctx.drawImage(this.hello, 208, 370, 120, 120) //二维码 ctx.draw(false, () => { // 返回canvas图片信息 uni.canvasToTempFilePath({ canvasId: 'canvasID', success: (res) => { this.imgUrl = res.tempFilePath // console.log(res.tempFilePath) }, fail: function(err) { console.log(err) } }) }) }, methods: { getBack() { uni.navigateBack({ delta: 1 }); }, saveImage() { //点击保存 var _this = this; uni.saveImageToPhotosAlbum({ filePath: _this.imgUrl, success() { uni.showModal({ title: "保存成功", content: "图片已成功保存到相册,快去分享到您的圈子吧", showCancel: false }) } }) }, // 将网络图片转为临时图片地址 async getImageInfo({imgSrc}) { return new Promise((resolve, errs) => { uni.downloadFile({ src: imgSrc, success: function(image) { resolve(image); }, fail(err) { errs(err); } }); }); }, // 绘制圆形头像 drawCircular(ctx, url, x, y, width, height) { //画圆形头像 var avatarurl_width = width; var avatarurl_heigth = height; var avatarurl_x = x; var avatarurl_y = y; ctx.save(); //先保存状态,已便于画完园再用 ctx.beginPath(); //开始绘制 ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math .PI * 2, false); ctx.setFillStyle("#FFFFFF") ctx.fill() //保证图片无bug填充 ctx.clip(); //剪切 ctx.drawImage(url, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); //推进去图片 ctx.restore(); }, // 绘制带圆角的矩形方法 fillRoundRect(cxt, x, y, width, height, radius, fillColor) { //圆的直径必然要小于矩形的宽高 if (2 * radius > width || 2 * radius > height) { return false; } cxt.save(); cxt.translate(x, y); //绘制圆角矩形的各个边 this.drawRoundRectPath(cxt, width, height, radius); cxt.fillStyle = fillColor || '#fff'; //若是给定了值就用给定的值否则给予默认值 cxt.fill(); cxt.restore(); }, drawRoundRectPath(cxt, width, height, radius) { cxt.beginPath(0); //从右下角顺时针绘制,弧度从0到1/2PI cxt.arc(width - radius, height - radius, radius, 0, Math.PI / 2); //矩形下边线 cxt.lineTo(radius, height); //左下角圆弧,弧度从1/2PI到PI cxt.arc(radius, height - radius, radius, Math.PI / 2, Math.PI); //矩形左边线 cxt.lineTo(0, radius); //左上角圆弧,弧度从PI到3/2PI cxt.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2); //上边线 cxt.lineTo(width - radius, 0); //右上角圆弧 cxt.arc(width - radius, radius, radius, (Math.PI * 3) / 2, Math.PI * 2); //右边线 cxt.lineTo(width, height - radius); cxt.closePath(); } } } </script> <style lang="scss" scoped> .canvas-box { height: 100vh; display: flex; align-items: center; justify-content: center; background-color: #1ABC9C; /deep/.nav-box { 100%; padding: 0 20rpx; position: absolute; z-index: 9999; top: 0; left: 0; .title-top { font-size: 36rpx; font-weight: 550; color: #FFFFFF; display: flex; justify-content: center; align-items: center; position: relative; margin-bottom: 30rpx; .title-icon { position: absolute; left: 0; } } } image { 335px; height: 500px; } .footer { display: flex; align-items: center; justify-content: space-between; position: absolute; justify-content: center; padding: 0 40rpx; 100%; left: 0; bottom: 10%; .download { border: 1rpx solid #ffffff; color: #ffffff; display: flex; align-items: center; } view { padding: 0 20rpx; height: 78rpx; text-align: center; line-height: 78rpx; font-size: 30rpx; border-radius: 36rpx; } } } </style>