微信小程序生成海报库

快速访问:Kujiale-Mobile-painter

Painter 的优势

  • 功能全,支持文本、图片、矩形、qrcode 类型的 view 绘制
  • 布局全,支持多种布局方式,如 align(对齐方式)、rotate(旋转)
  • 支持圆角,其中图片,矩形,和整个画布支持 borderRadius 来设置圆角
  • 支持边框,同时支持 solid、dashed、dotted 三种类型
  • 支持渐变色,包括线性渐变与径向渐变。
  • 支持 box-shadow 和 text-shadow,统一使用 shadow 表示。
  • 支持文字背景、获取宽度、主动换行
  • 支持图片 mode
  • 支持元素的相对定位方法
  • 杠杠的性能优化,我们对网络素材图片加载实现了一套 LRU 存储机制,不用重复下载素材图片。
  • 杠杠的容错,因为某些特殊情况会导致 Canvas 绘图不完整。我们对此加入了对结果图片进行检测机制,如果绘图出错会进行重绘。
  • 生成的图片支持分辨率调节
  • 支持使用拖动等操作动态编辑绘制内容

How To Use

运行例子

git clone https://github.com/Kujiale-Mobile/Painter.git

代码下载后,用小程序 IDE 打开后即可使用。

注:请选择小程序项目,非小游戏,例子中无 appid,所以无法在手机上运行,如果需要真机调试,请在打开例子时,填上自己的小程序 id

以下是我在uniapp中使用

下载库到项目中,注册组件,使用组件

<painter @imgOK="onImgOk" @imgErr="onImgErr" :widthPixels="windowWidth" :palette="template" />
// 微信海报模块
export default {
	"width": "750px",
	"height": "1334px",
	"background": "#FFFFFF",
	"views": [{
			"type": "image",
			"url": "",
			"css": {
				"width": "750px",
				"height": "1334px",
				"top": "0px",
				"left": "0px",
				"rotate": "0",
				"borderRadius": "",
				"borderWidth": "",
				"borderColor": "#000000",
				"shadow": "",
				"mode": "scaleToFill"
			}
		}, {
			"type": "image",
			"url": "",
			"css": {
				"color": "#000000",
				"background": "#ffffff",
				"width": "334px",
				"height": "146px",
				"top": "186px",
				"left": "208px",
				"rotate": "0"
			}
		}, {
			"type": "image",
			"url": "",
			"css": {
				"color": "#000000",
				"background": "#ffffff",
				"width": "610px",
				"height": "826px",
				"top": "435px",
				"left": "70px",
				"rotate": "0"
			}
		}, {
			"type": "image",
			"url": "",
			"css": {
				"color": "#000000",
				"background": "#ffffff",
				"width": "124px",
				"height": "124px",
				"top": "1078px",
				"left": "91px",
				"rotate": "0",
				"borderRadius": "62px"
			}
		}, {
			"type": "image",
			"url": "",
			"css": {
				"color": "#000000",
				"background": "#ffffff",
				"width": "609px",
				"height": "408px",
				"top": "435px",
				"left": "71px",
				"rotate": "0",
				"borderRadius": "20px"
			}
		}, {
			"type": "image",
			"url": "",
			"css": {
				"color": "#000000",
				"background": "#ffffff",
				"width": "160px",
				"height": "160px",
				"top": "1057px",
				"left": "489px",
				"rotate": "0",
				"borderRadius": "20px"
			}
		},
		{
			"type": "text",
			"text": "",
			"css": {
				"color": "#222222",
				"background": "rgba(0,0,0,0)",
				"width": "216px",
				"height": "42px",
				"top": "1076px",
				"left": "238px",
				"rotate": "0",
				"padding": "0px",
				"fontSize": "30px",
				"fontWeight": "normal",
				"maxLines": "1",
				"lineHeight": "42px",
				"textStyle": "fill",
				"textDecoration": "none",
				"fontFamily": "",
				"textAlign": "left"
			}
		},
		{
			"type": "text",
			"text": "精品推荐",
			"css": {
				"color": "#ffffff",
				"background": "rgba(0,0,0,0)",
				"width": "144px",
				"height": "120px",
				"top": "360px",
				"left": "304px",
				"rotate": "0",
				"padding": "0px",
				"fontSize": "36px",
				"fontWeight": "normal",
				"maxLines": "2",
				"lineHeight": "60px",
				"textStyle": "fill",
				"textDecoration": "none",
				"fontFamily": "",
				"textAlign": "center"
			}
		},
		{
			"type": "text",
			"text": "长按识别,立享优惠",
			"css": {
				"color": "#999999",
				"background": "rgba(0,0,0,0)",
				"width": "216px",
				"height": "33px",
				"top": "1172px",
				"left": "238px",
				"rotate": "0",
				"padding": "0px",
				"fontSize": "24px",
				"fontWeight": "normal",
				"maxLines": "2",
				"lineHeight": "33px",
				"textStyle": "fill",
				"textDecoration": "none",
				"fontFamily": "",
				"textAlign": "left"
			}
		},
		{
			"type": "text",
			"text": "约你一起买好货",
			"css": {
				"color": "#222222",
				"background": "rgba(0,0,0,0)",
				"width": "216px",
				"height": "42px",
				"top": "1120px",
				"left": "238px",
				"rotate": "0",
				"padding": "0px",
				"fontSize": "30px",
				"fontWeight": "normal",
				"maxLines": "2",
				"lineHeight": "42px",
				"textStyle": "fill",
				"textDecoration": "none",
				"fontFamily": "",
				"textAlign": "left"
			}
		},
		// 商品标题
		{
			"type": "text",
			"text": "无",
			"css": {
				"color": "#222222",
				"background": "rgba(0,0,0,0)",
				"width": "550px",
				"height": "40px",
				"top": "870px",
				"left": "100px",
				"rotate": "0",
				"padding": "0px",
				"fontSize": "36px",
				"fontWeight": "normal",
				"maxLines": "1",
				"lineHeight": "40px",
				"textStyle": "fill",
				"textDecoration": "none",
				"fontFamily": "",
				"textAlign": "left"
			}
		},
		// 商品价格
		{
			"type": "text",
			"text": "¥139.00/KG",
			"css": {
				"color": "#FF6060",
				"background": "rgba(0,0,0,0)",
				"width": "550px",
				"height": "40px",
				"top": "940px",
				"left": "100px",
				"rotate": "0",
				"padding": "0px",
				"fontSize": "36px",
				"fontWeight": "normal",
				"maxLines": "2",
				"lineHeight": "40px",
				"textStyle": "fill",
				"textDecoration": "none",
				"fontFamily": "",
				"textAlign": "left"
			}
		}
	]
}

 

<template>
	<view>
		<view class="">
			<image class="poster-img" :src="posterUrl" mode="widthFix"></image>
		</view>

		<!-- #ifdef MP-WEIXIN -->
		<view class="canvas-box">
			<painter @imgOK="onImgOk" @imgErr="onImgErr" :widthPixels="windowWidth" :palette="template" />
		</view>
		<!-- #endif -->
		<!-- #ifdef H5 -->
		<a style="display: none;" id="saveImg" href="">下载</a>
		<!-- #endif -->
		<!-- #ifdef MP-WEIXIN -->
		<button @click="savePicture" class="poster-button">保存图片</button>
		<!-- #endif -->
		<!-- #ifdef H5 -->
		<button >长按保存图片</button>
		<!-- #endif -->
	</view>
</template>

<script>
	// #ifdef H5
	import MC from "mcanvas";
	import qs from "@/utils/qs.js"
	import QR_CODE from "qrious";
	// #endif
	import $mConfig from '@/config/index.config.js';
	import WechatCode from "@/api/base/WechatCode"
	import tpl from "./mpPosterData.js"
	import {
		mapState,
		mapActions
	} from "vuex"
	export default {
		data() {
			return {
				windowWidth: 750,
				show: false,
				posterUrl: "",
				template: tpl,
				poster_bg: ""
			}
		},
		onLoad() {
			this.getUserInfo()
			this.getCode()
			// #ifdef H5
			this.createPoster()
			// #endif
		},
		computed: {
			...mapState(["userInfo", "goodsDetail"])
		},
		methods: {
			init() {
				this.poster_bg = $mConfig.assetsPath + "poster-bg.png";
				this.template.views[0].url = $mConfig.assetsPath + "poster-bg.png";
				this.template.views[1].url = $mConfig.assetsPath + "poster-logo.png";
				this.template.views[2].url = $mConfig.assetsPath + "poster-bg2.png";
				this.template.views[3].url = this.userInfo.avatar.url;
				this.template.views[4].url = this.goodsDetail.list_cover.url;
				this.template.views[5].url = this.wechatCode;
				this.template.views[6].text = this.userInfo.nickname;
				this.template.views[10].text = this.goodsDetail.title;
				this.template.views[11].text = `¥${this.goodsDetail.price}/${this.goodsDetail.company}`
				
				setTimeout(() => {
					this.show = true;

				})
			},
			// H5生成海报
			createPoster() {
				let qr_url = window.location.origin;
				let parmas = {
					id: this.goodsDetail.id,
					parent_id: this.userInfo.id,
					is_share: 1
				}
				qr_url = qr_url + "/#/pages/goods_detail/goods_detail?" + qs.stringify(parmas, {
					encode: false
				})
				console.log(qr_url)
				const qr_code = new QR_CODE({
					value: qr_url,
				});
				const qr_img = new Image();
				qr_img.src = qr_code.toDataURL("image/jpeg");
				const mc = new MC({
					"width": 750,
					"height": 1334,
					backgroundColor: "white",
				});

				let nickname = this.userInfo.nickname.length > 8 ? this.userInfo.nickname.slice(0, 8) : this.userInfo
					.nickname;

				mc.add($mConfig.assetsPath + "poster-bg.png", {
						"width": "750px",
						"height": "1334px",
						pos: {
							y: 0,
							x: 0,
						},
					})
					.add($mConfig.assetsPath + "poster-logo.png", {
						"width": "334px",
						"height": "146px",
						pos: {
							y: "186px",
							x: "208px",
						},
					})
					.add($mConfig.assetsPath + "poster-bg2.png", {
						"width": "610px",
						"height": "826px",
						pos: {
							y: "435px",
							x: "70px",
						},
					})
					.add(this.userInfo.avatar.url, {
						"width": "124px",
						"height": "124px",
						"align": "center",
						pos: {
							y: "1078px",
							x: "91px",
						},
						crop: {
							radius: 120,
						},
					})
					.add(this.goodsDetail.list_cover.url, {
						"width": "609px",
						"height": "408px",
						"align": "center",
						crop: {
							"y": "0",
							"x": "0",
							"width": "100%",
							"height": "66.99%",
							"radius": "20px"
						},
						pos: {
							y: "435px",
							x: "71px",
						},
					})
					.add(qr_img, {
						"width": "160px",
						"height": "160px",
						pos: {
							y: "1057px",
							x: "489px",
						},
					})
					.text(this.userInfo.nickname, {
						width: "350px",
						align: "left",
						normalStyle: {
							// 字体颜色;
							color: "#222222",
							font: "30px arial sans-serif",
						},
						pos: {
							y: "1076px",
							x: "238px",
						},
					})
					.text(this.goodsDetail.title, {
						width: "550px",
						align: "left",
						normalStyle: {
							// 字体颜色;
							color: "#222222",
							font: "36px arial sans-serif",
						},
						pos: {
							y: "870px",
							x: "100px",
						},
					})
					.text(`¥${this.goodsDetail.price}/${this.goodsDetail.company}`, {
						width: "550px",
						align: "left",
						normalStyle: {
							// 字体颜色;
							color: "#FF6060",
							font: "36px arial sans-serif",
						},
						pos: {
							y: "950px",
							x: "100px",
						},
					})
					.text("精品推荐", {
						width: "160px",
						align: "center",
						normalStyle: {
							// 字体颜色;
							color: "#ffffff",
							font: "36px arial sans-serif",
						},
						pos: {
							y: "360px",
							x: "304px",
						},
					})
					.text("长按识别,立享优惠", {
						width: "216px",
						align: "left",
						normalStyle: {
							// 字体颜色;
							color: "#999999",
							font: "24px arial sans-serif",
						},
						pos: {
							y: "1172px",
							x: "238px",
						},
					})
					.text("约你一起买好货", {
						width: "216px",
						align: "left",
						normalStyle: {
							// 字体颜色;
							color: "#222222",
							font: "30px arial sans-serif",
						},
						pos: {
							y: "1120px",
							x: "238px",
						},
					})
					.draw((b64) => {
						// window.console.log(b64);
						this.posterUrl = b64;
					});
			},
			convertImageToCanvas() {
				const qr_img = new Image();
				qr_img.src = this.posterUrl;
				var canvas = document.createElement("canvas");
				canvas.width = qr_img.width;
				canvas.height = qr_img.height;
				canvas.getContext("2d").drawImage(qr_img, 0, 0);
				return canvas;
			},
			// 点击保存图片
			savePicture: function(e) {
				if (!this.posterUrl) {
					uni.showToast({
						title: "海报生成失败"
					})
					return
				}
				// #ifdef H5
				// var sampleImage = document.querySelector(".erweima");
				var canvas = this.convertImageToCanvas()
				var url = canvas.toDataURL("image/png"); //生成下载的url
				var triggerDownload = document.querySelector("#saveImg")
				// .attr("href", url).attr("download", "ewm.png");
				triggerDownload.setAttribute("href", url)
				triggerDownload.setAttribute("download", `${new Date().getTime()}.png`)
				triggerDownload.click();
				return
				// #endif
				// let {
				// 	poster
				// } = e.currentTarget.dataset;
				var $this = this;
				uni.getSetting({
					success: function(res) {
						var accredit = res.authSetting['scope.writePhotosAlbum']
						if (accredit) {
							uni.showLoading({
								title: '图片下载中...',
							})
							uni.saveImageToPhotosAlbum({
								filePath: $this.posterUrl,
								success: function(ret) {
									// core.toast("保存图片成功");
									uni.showToast({
										title: "保存图片成功"
									})
								},
								fail: function(ret) {
									console.log(ret)
									// core.toast("保存图片失败");
									uni.showToast({
										title: "保存图片失败"
									})
								}
							})

						} else {
							uni.authorize({
								scope: 'scope.writePhotosAlbum',
								fail: function() {
									/*获取权限时点击了拒绝以后的弹窗*/
									uni.showModal({
										title: '警告',
										content: '您点击了拒绝授权,将无法正常使用保存图片或视频的功能体验,请删除小程序重新进入。'
									})
								}
							})
						}
					}
				})
			},
			getCode() {
				WechatCode(`id=${this.goodsDetail.id}&parent_id=${this.userInfo.id || ""}&is_share=1`,
						"pages/goods_detail/goods_detail")
					.then(res => {
						console.log(res)
						this.wechatCode = res.data.wechatCode
						this.init()
					})
			},
			onImgOk(res) {
				console.log(res)
				this.posterUrl = res.detail.path;
			},
			onImgErr(res) {
				console.log(res)
			},
			...mapActions(["getUserInfo"])
		}
	}
</script>

<style>
	.canvas-box {
		position: fixed;
		top: -99999px;
	}

	.poster-img {
		width: 100vw;
		padding-bottom: 100rpx;
	}
	.poster-button {
		position: fixed;
		bottom: 0;
		width: 100%;
		padding: 20rpx;
		background: #EFEDF8;
		color: #715DC2;
	}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值