uniapp生成canvas商品海报

uniapp生成canvas商品海报

之前做一个uniapp开发的微信小程序需要生成商品海报,在网上学习后完成,记录一下。
主要使用了canvas,uniapp官网链接:canvas使用方法
自定义生成海报组件tr-xqgenrate.vue

<template>
	<view>
		<u-mask :z-index="90" :show="share_qrcode_flag" :zoom="true" :custom-style="{ background: 'rgba(0,0,0,.6)' }"
			:duration="300">
			<view class="sq_box">
				<view class="tz_box qrcode_box">
					<view class="icon-close-text flex-column justify-content-conter align-items-conter"
						v-if="canvasToTempFilePath!=null">
						<text class="text-t">长按保存海报到相册即可分享~</text>
						<text class="iconfont iconshenheweitongguo slone-iconfont" @tap="Close"></text>
					</view>
					<view class="share_qrcode">
						<canvas canvas-id="myCanvas"
							style="width: 690px;height:1060px; position: fixed;top: -10000px;"></canvas>
						<image @longpress="this.showSaveImgWin = true"
							style="width: 100%; height: 100%;border-radius: 10rpx;" :src="canvasToTempFilePath"></image>
					</view>
				</view>
			</view>
		</u-mask>
		<u-modal v-model="showSaveImgWin" content="确定要保存图片吗" @confirm="saveShareImg(canvasToTempFilePath)"
			@cancel="this.showSaveImgWin = false" :show-cancel-button="true"></u-modal>
	</view>
</template>

<script>
	export default {
		name: 'tr-xqgenrate',
		data() {
			return {
				ratio: 1,
				ctx: null, // 创建canvas对象
				canvasToTempFilePath: null, // 保存最终生成的导出的图片地址
				openStatus: true, // 声明一个全局变量判断是否授权保存到相册
				share_qrcode_flag: false,
				showSaveImgWin: false, //保存图片到相册
			};
		},
		methods: {
			Close() {
				this.share_qrcode_flag = false
				setTimeout(() => {
					this.$emit('IsbuttomBuy', true)
				}, 180)
			},
			share_qrcode(XqOption) {
				if (!this.canvasToTempFilePath) {
					this.createCanvasImage(XqOption);
				} else {
					this.$emit('IsbuttomBuy', false)
				}
				this.share_qrcode_flag = true;
			},
			//获取图片信息
			downloadFileImg(url) {
				return new Promise(resolve => {
					uni.getImageInfo({
						src: url,
						success: res => {
							resolve(res.path);
						},
						fail: err => {
							uni.showToast({
								title: '网络错误请重试',
								icon: 'loading'
							});
						}
					});
				});
			},
			// 生成海报
			async createCanvasImage(option) {
				if (!this.ctx) {
					uni.showLoading({
						title: '正在生成海报...'
					});
					let code = this.downloadFileImg(option.codeUrl); //小程序太阳码
					let cover = this.downloadFileImg(option.GoodsImage); //商品图片
					let headImg = this.downloadFileImg(option.StoreHeadUrl); //店铺头像
					let bgUrl = ''; //背景图片
					if (option.bgUrl) {
						bgUrl = new Promise(resolve => {
							uni.downloadFile({
								url: option.bgUrl,
								success: res => {
									resolve(res.tempFilePath);
								},
								fail: erros => {
									uni.showToast({
										title: '网络错误请重试',
										icon: 'loading'
									});
								}
							});
						});
					}
					Promise.all([headImg, code, cover, bgUrl]).then(result => {
						const ctx = uni.createCanvasContext('myCanvas', this);
						let canvasWidthPx = 620 * this.ratio,
							canvasHeightPx = 1060 * this.ratio,
							codeurl_width = 130, //小程序太阳码宽度
							codeurl_heigth = 130, //小程序太阳码高度
							codeurl_x = 525, //小程序太阳码在画布上的位置
							codeurl_y = 760, //小程序太阳码在画布上的位置
							coverurl_width = 630, //封面宽度
							coverurl_heigth = 635, //封面高度
							coverurl_x = 30, //封面在画布上的位置
							coverurl_y = 30, //封面在画布上的位置
							avatarurl_width = 80, //店铺头像宽度
							avatarurl_heigth = 80, //店铺头像高度
							avatarurl_x = 50, //头像在画布上的位置
							avatarurl_y = 930; //头像在画布上的位置

						//绘制圆角矩形
						ctx.save();
						ctx.translate(0, 0);
						//绘制圆角矩形的各个边
						this.drawRoundRectPath(ctx, 690, 1060, 10);
						ctx.fillStyle = option.fillStyle || '#ffffff';
						ctx.fill();
						ctx.restore();
						ctx.save(); 
						ctx.beginPath(); //开始绘制

						ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y,
							avatarurl_width / 2, 0, Math.PI * 2, false);
						this.drawRoundRect(ctx, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth,
							10)
						ctx.clip(); 
						ctx.drawImage(result[0], avatarurl_x, avatarurl_y, avatarurl_width,
							avatarurl_heigth); // 推进去图片
						ctx.restore(); 

						ctx.setFillStyle('#333333');
						if (option.GoodsName) {
							this.dealWords({
								ctx: ctx, 
								fontSize: 30, 
								word: option.GoodsName, 
								maxWidth: 640, 
								x: 30, 
								y: 670, 
								maxLine: 2 
							});
						}

						ctx.font = 'normal 500 27px sans-serif'; 
						ctx.setFillStyle('#E09212'); 
						ctx.fillText('¥', 30, 805); 

						ctx.font = 'normal bold 42px sans-serif'; //现价
						ctx.setFillStyle('#E09212');
						ctx.fillText(option.money, 62, 805);

						ctx.font = 'normal 500 24px sans-serif'; //已拼  or  已售
						ctx.setFillStyle('#808080');
						ctx.fillText(option.Sold, option.SoldElementLeft, 801);

						ctx.font = 'normal 500 24px sans-serif'; //原价
						ctx.setFillStyle('#808080');
						ctx.fillText(option.primaryMoney, 30, 860);

						ctx.setFillStyle('#333333');
						if (option.StoreName) {
							this.dealWords({
								ctx: ctx, 
								fontSize: 28, 
								word: option.StoreName, 
								maxWidth: 480, 
								x: 150,
								y: 920,
								maxLine: 1 
							});
						}

						ctx.setFillStyle('#999999');
						if (option.SoreAddress) {
							this.dealWords({
								ctx: ctx, 
								fontSize: 24, 
								word: option.SoreAddress, 
								maxWidth: 480, 
								x: 150, 
								y: 960, 
								maxLine: 1 
							});
						}

						ctx.drawImage(result[2], coverurl_x, coverurl_y, coverurl_width,
							coverurl_heigth);
						ctx.drawImage(result[1], codeurl_x, codeurl_y, codeurl_width, codeurl_heigth);

						// 绘制矩形
						ctx.lineWidth = 2;
						ctx.setStrokeStyle('rgba(0, 0, 0, 0.05)');
						this.drawRoundRect(ctx, 30, 910, 620, 120, 10)
						ctx.stroke();
						ctx.closePath();

						ctx.draw(false, () => {
							uni.canvasToTempFilePath({
									canvasId: 'myCanvas',
									success: res => {
										this.canvasToTempFilePath = res.tempFilePath;
										uni.showToast({
											title: '绘制成功'
										});
									},
									fail: err => {
										uni.showToast({
											title: '绘制失败'
										});
									},
									complete: () => {
										uni.hideLoading();
										uni.hideToast();
										this.$emit('IsbuttomBuy', false)
									},
								},
								this
							);
						});
					});
				}
			},

			drawRoundRect(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();
			},

			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();
			},
			//处理文字多出省略号显示
			dealWords(options) {
				options.ctx.setFontSize(options.fontSize); //设置字体大小
				options.ctx.setFont; //设置字体大小
				let allRow = Math.ceil(options.ctx.measureText(options.word).width / options.maxWidth); //实际总共能分多少行
				let count = allRow >= options.maxLine ? options.maxLine : allRow; //实际能分多少行与设置的最大显示行数比,谁小就用谁做循环次数
				let endPos = 0; //当前字符串的截断点
				for (let j = 0; j < count; j++) {
					let nowStr = options.word.slice(endPos); //当前剩余的字符串
					let rowWid = 0; //每一行当前宽度
					if (options.ctx.measureText(nowStr).width > options.maxWidth) {
						//如果当前的字符串宽度大于最大宽度,然后开始截取
						for (let m = 0; m < nowStr.length; m++) {
							rowWid += options.ctx.measureText(nowStr[m]).width; //当前字符串总宽度
							if (rowWid > options.maxWidth) {
								if (j === options.maxLine - 1) {
									//如果是最后一行
									options.ctx.fillText(nowStr.slice(0, m - 1) + '...', options.x, options.y + (j + 1) *
										40); //(j+1)*18这是每一行的高度
								} else {
									options.ctx.fillText(nowStr.slice(0, m), options.x, options.y + (j + 1) * 40);
								}
								endPos += m; //下次截断点
								break;
							}
						}
					} else {
						//如果当前的字符串宽度小于最大宽度就直接输出
						options.ctx.fillText(nowStr.slice(0), options.x, options.y + (j + 1) * 40);
					}
				}
			},

			// 保存到系统相册
			saveShareImg(canvasToTempFilePath) {
				uni.saveImageToPhotosAlbum({
					filePath: canvasToTempFilePath,
					success: () => {
						this.$u.toast('保存成功,快去分享到朋友圈吧~');
					},
					fail: () => {
						this.$u.toast('保存失败~');
					}
				});
			}
		}
	};
</script>

使用组件


调用海报组件的方法给子组件传需要渲染的值

async SetBill() { //生成海报
				let url = '',
					data = {},
					SoldElementLeft = 62;
				if (this.codeUrl.length <= 0) {
					if (this.GoodsData.GoodsType == 1) { //普通商品获取小程序太阳码
						url = this.$api.goods.paramCode
						data = {
							product_id: this.GoodsData.GoodsId,
							child_store_id: this.StoreDataList.id
						}
					} else if (this.GoodsData.GoodsType == 0) { //拼团商品获取小程序太阳码
						url = this.$api.Collage.spellParamCode
						data = {
							spell_goods_id: this.GoodsData.GoodsId,
							child_store_id: this.StoreDataList.id
						}
					}
					let ReauestList = await this.$request({
						url: url,
						method: "GET",
						data,
						loading: true
					});
					uni.hideLoading()
					if (ReauestList.code != 200) {
						uni.showToast({
							title: "获取小程序太阳码失败",
							icon: "none",
							duration: 3000
						})
						return false
					} else {
						this.codeUrl = ReauestList.data.qrcode
					}
				}
				SoldElementLeft += (this.GoodaDetails.price.toString().length * 30)
				this.$refs.bottomShare.close()
				let MyXqOption = {
					codeUrl: this.codeUrl,
					GoodsImage: (this.GoodsData.GoodsType == 1 ? this.GoodaDetails.pic_url[0].url : this
						.GoodaDetails
						.images[0].url),
					StoreHeadUrl: this.StoreDataList.logo,
					fillStyle: '#FFFFFF',
					money: this.GoodaDetails.price,
					primaryMoney: '原价' + (this.GoodsData.GoodsType == 0 ? this.GoodaDetails
						.shop_price : this
						.GoodaDetails
						.original_price),
					Sold: (this.GoodsData.GoodsType == 0 ? '已拼' + this.GoodaDetails.bum_number + '人' : '已售' +
						this
						.GoodaDetails
						.sale_number),
					GoodsName: this.GoodaDetails.title,
					StoreName: this.StoreDataList.cs_name,
					SoreAddress: this.StoreDataList.address,
					SoldElementLeft: SoldElementLeft
				}
				this.$refs.TrXqgen.share_qrcode(MyXqOption)
			},

效果图
uniapp生成canvas商品海报_第1张图片

你可能感兴趣的:(前端,canvas,uni-app)