【uniapp】 合成海报组件

之前公司的同事写过一个微信小程序用的 合成海报的组件 非常十分好用 最近的项目是uni的 把组件改造一下也可以用 记录一下

<template>
	<view>
		<canvas type="2d" class="_mycanvas" id="my-canvas" canvas-id="my-canvas" :style="canCss" />
	</view>
</template>

<script>
	export default {
		name: "draw-2d",
		data() {
			return {

			};
		},
		methods: {
			getCanvas(canvasId) {
				return new Promise((r) => {
					this.createSelectorQuery().select(canvasId).fields({
						node: true
					}).exec(res => {
						// console.log(res);
						r(res[0].node)
					})
				})
			},
			// 给定一串文字样式 获取他在canvas的宽度
			async getTxtWidth(data) {
				// this.setData({
				// 	canCss: `width:${data.width}px;height:${data.height}px;`
				// })
				this.canCss=`width:${data.width}px;height:${data.height}px;`
				let canvasId = '#my-canvas'
				await this.loadFont(data)
				let canDom = await this.getCanvas(canvasId)
				canDom.width = data.width
				canDom.height = data.height
				let ctx = canDom.getContext('2d')
				let d = data.txt
				let font = ` ${d.weight || 'italic'} ${d.size || 30}px ${d.fontFamily || '微软雅黑'}`
				// console.log('font', font);
				ctx.font = font
				ctx.textAlign = d.align
				let tw = ctx.measureText(d.value)
				return tw
			},
			// 绘制图片
			drawImage(d, ctx, cav) {
				let img = cav.createImage()
				return new Promise((r) => {
					img.onerror = () => {
						console.log('下载失败');
						r()
					}
					img.onload = () => {
						ctx.save();
						if (d.isCir) {
							ctx.beginPath(); //开始绘制
							ctx.arc(d.w / 2 + d.x, d.h / 2 + d.y, d.w / 2, 0, Math.PI * 2, true);
							ctx
						.clip(); //画好了圆 剪切  原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内 这也是我们要save上下文的原因
						}
						if (d.radius) {
							// 需要裁剪圆角矩形图片
							ctx.save();
							ctx.beginPath();
							ctx.moveTo(d.x + d.radius, d.y);
							ctx.arcTo(d.x + d.w, d.y, d.x + d.w, d.y + d.h, d.radius);
							ctx.arcTo(d.x + d.w, d.y + d.h, d.x, d.y + d.h, d.radius);
							ctx.arcTo(d.x, d.y + d.h, d.x, d.y, d.radius);
							ctx.arcTo(d.x, d.y, d.x + d.w, d.y, d.radius);
							ctx.strokeStyle = 'transparent'
							ctx.closePath()
							ctx.stroke();
							ctx.clip();
						}
						ctx.drawImage(img, d.x, d.y, d.w, d.h)
						// if (d.border) {
						//   ctx.save()
						//   ctx.strokeStyle = d.border.color
						//   ctx.lineWidth = d.border.size
						//   ctx.strokeRect(d.x, d.y, d.w, d.h)
						//   ctx.restore()
						// }
						ctx.restore()
						r()
					}
					img.src = d.src
				})
			},
			// 绘制圆形
			drawCir(d, ctx) {
				ctx.save()
				ctx.beginPath();
				ctx.arc(d.x, d.y, d.size, d.size, 0 * Math.PI / 180, 360 * Math.PI / 180)
				if (d.border) {
					ctx.lineWidth = d.border
					ctx.strokeStyle = d.color
					ctx.stroke()
				} else {
					ctx.fillStyle = d.color
					ctx.fill()

				}
				ctx.restore()
			},
			// 绘制文字
			drawText(d, ctx) {
				let font = ` ${d.weight || 'italic'} ${d.size || 30}px ${d.fontFamily || '微软雅黑'}`
				// console.log('font', font);
				ctx.font = font
				ctx.textAlign = d.align
				let val = d.value
				let isSlice = false
				if (d.maxWidth) {
					let tw = ctx.measureText(val)
					// console.log('tw', tw);
					while (tw.width > d.maxWidth) {
						isSlice = true
						let len = val.pointLen()
						val = val.sliceByPoint(0, len - 1)
						tw = ctx.measureText(val + '...')
						// console.log('tw', tw);
					}
				}
				if (isSlice) {
					// console.log('裁剪过了 需要拼接');
					val = val + '...'
				}
				// console.log('val', val);
				ctx.fillStyle = d.color
				ctx.fillText(val, d.x, d.y)
				// 字体描边
				// ctx.strokeStyle = "blue";
				// ctx.font = " italic 40px 宋体";
				// ctx.strokeText("你好", d.x, d.y);
			},
			// 绘制需要自动换行的文字
			drawText1(d, ctx) {
				let font = ` ${d.weight || 'normal'} ${d.size || 16}px ${d.fontFamily || 'Arial'}`;
				ctx.font = font;
				ctx.textAlign = d.align || 'left';
				ctx.textBaseline = 'top'; // 确保文本从顶部开始绘制  
				let str = d.value;
				let maxWidth = d.maxWidth // 默认设置为无限大,以确保没有限制  

				let linesize = d.linesize || 100; // 默认行高为字体大小  
				let initHeight = d.y
				let leftWidth = d.x
				var lineWidth = 0;
				var lastSubStrIndex = 0; //每次开始截取的字符串的索引
				for (let i = 0; i < str.length; i++) {
					lineWidth += ctx.measureText(str[i]).width;
					if (lineWidth > maxWidth) {
						console.log('str.substring(lastSubStrIndex, i)', str.substring(lastSubStrIndex, i),
							lastSubStrIndex, i);
						ctx.fillText(str.substring(lastSubStrIndex, i), leftWidth, initHeight); //绘制截取部分
						initHeight += linesize; //字体的高度
						lineWidth = 0;
						lastSubStrIndex = i;
						i--
						// titleHeight += 30;
					}
					if (i == str.length - 1) { //绘制剩余部分
						ctx.fillStyle = d.color
						ctx.fillText(str.substring(lastSubStrIndex, i + 1), leftWidth, initHeight);
					}
				}


			},
			// 绘制矩形
			drawRect(d, ctx) {
				ctx.save()
				if (d.border) {
					ctx.strokeStyle = d.color
					ctx.lineWidth = d.border
					ctx.strokeRect(d.x, d.y, d.w, d.h)
				} else {
					ctx.fillStyle = d.color
					ctx.fillRect(d.x, d.y, d.w, d.h)
				}
				ctx.restore()
			},
			loadFont(d) {
				if (!d.font) return
				let r1 = []
				d.font.map(v => {
					let p = new Promise((r) => {
						uni.loadFontFace({
							family: v.name,
							scopes: ['native'],
							source: d.cdn + v.src,
							global: true,
							complete: r
						})
					})
					r1.push(p)
				})
				return Promise.all(r1)

			},
			async goDraw(data) {
				console.log('async goDraw(data)');
				// this.setData({
				// 	canCss: `width:${data.width}px;height:${data.height}px;`
				// })
				this.canCss=`width:${data.width}px;height:${data.height}px;`
				let canvasId = '#my-canvas'
				await this.loadFont(data)

				let canDom = await this.getCanvas(canvasId)
				canDom.width = data.width
				canDom.height = data.height
				return new Promise(async r => {
					if (data.loading) uni.showLoading({
						title: '合成中'
					})
					let ctx = canDom.getContext('2d')
					for (let i = 0; i < data.data.length; i++) {
						let v = data.data[i]
						if (v.type == 'image') {
							if (v.isNeedCdn) {
								v.src = data.cdn + v.src
							}
							await this.drawImage(v, ctx, canDom)
						}
						if (v.type == 'text') {
							this.drawText(v, ctx)
						}
						if (v.type == 'text1') {
							this.drawText1(v, ctx)
						}
						if (v.type == 'rect') {
							this.drawRect(v, ctx)
						}
						if (v.type == 'cir') {
							this.drawCir(v, ctx)
						}
					}
					uni.canvasToTempFilePath({
						canvasId: canvasId,
						canvas: canDom,
						x: 0,
						y: 0,
						width: data.width,
						height: data.height,
						destWidth: data.width * data.scale,
						destHeight: data.height * data.scale,
						success: (file) => {
							if (data.loading) uni.hideLoading()
							r(file.tempFilePath)
						}
					}, this)
				})
				// return new Promise((r) => {
				//   uni.nextTick(() => {
				//   })
				// })
			}
		}
	}
</script>

<style lang="scss">
	._mycanvas {
		position: absolute;
		right: -1000000000px;
		top: -100000000px;
		/* top: 0;
  left: 0;
  width: 750rpx;
  background-color: pink; */

	}
</style>

这次用的组件是放在分包里了 顺便记录一下 分包调用组件
在这里插入图片描述
目录结构是这样子的 想在index.vue页面调用

<template>
	<Draw id='draw' ref='draw'></Draw>
</template>

<script>
	import Draw from '@/threeSubManage/components/draw-2d/draw-2d.vue'; // 引入draw组件
	export default {
		components: {
			Draw // 注册draw组件  
		},
		}
</script>

重点来了 合成图片的函数是这样子

  async goDraw() {
    let cdn = 'http://192.168.1.1/cdn/'
    let width = 500, height = 400
    // || this.data.Url.imgUrl
    let font = [{ name: 'egg1', src: 'egg1.ttf' }]
    let data = [
      // 普通图片 需要拼接cdn 如果是头像或者后台返回的图片链接 isNeedCdn就不用填 默认false
      { type: 'image', x: 0, y: 0, w: width, h: height, src: 'share.jpg', isNeedCdn: true },
      // isCir 是否是圆形图片 一般用作头像
      { type: 'image', x: width - 120, y: 100, isCir: 1, w: 80, h: 80, src: 'mall/image.png', isNeedCdn: true },
      // 圆角图片 deg 就是被裁的px
      {
        type: 'image', x: width / 2 - 40 / 2, y: 20, radius: 10,
        // border: { size: 6, color: 'red' },
        w: 40, h: 40, src: 'event/share-h2.png', isNeedCdn: true
      },
      // 文字 size 是文件大小 color 颜色  fontfamily  字体
      {
        type: 'text', value: '居中的字阿阿阿阿', x: width / 2, y: height / 2,
        maxWidth: 300, //最大宽度
        size: 34, weight: '100',
        align: 'center', color: 'red', fontFamily: 'egg1'
      },
      {
        type: 'text', value: '靠左的文字阿阿阿阿', x: 10, y: 100,
        maxWidth: 300, //最大宽度
        size: 34, weight: '100',
        align: 'left', color: 'red', fontFamily: 'egg1'
      },
      {
        type: 'text', value: '靠右的文字阿阿阿阿', x: width - 10, y: 140,
        maxWidth: 300, //最大宽度
        size: 34, weight: '100',
        align: 'right', color: 'red', fontFamily: 'egg1'
      },
      //需要换行的文字,type传text1
            {
        type: 'text1', value: this.currentIns, x: 92 / 1.5, y: (713) / 1.5,
        maxWidth: 540 / 1.5, //最大宽度
        linesize: 64 / 1.5,
        size: 32 / 1.5, weight: '100',
        align: 'left', color: 'white', fontFamily: 'egg1'
      },
      // 矩形框 填充色为蓝色  没有border 默认就是背景填充
      { type: 'rect', x: 10, y: 10, w: 50, h: 50, color: 'blue', border: 0 },
      // 圆形框  填充色color
      { type: 'cir', x: width / 2, y: height - 80, border: 0, size: 40, color: 'red' },
    ]
    let d = { width, height, loading: true, font, scale: 2, cdn, data }
    let drawDom = this.selectComponent('#draw')
    // let r = await drawDom.goDraw(d)
    let r = await this.$refs.draw.goDraw(d);
    uni.previewImage({
      urls: [r]
    })
  },

文字、图片、矩形、圆角等等情况都考虑了 使用起来非常方便
有需要可以试一试哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/558461.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

全开源小狐狸Ai系统 小狐狸ai付费创作系统 ChatGPT智能机器人2.7.6免授权版

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 测试环境&#xff1a;Linux系统CentOS7.6、宝塔、PHP7.4、MySQL5.6&#xff0c;根目录public&#xff0c;伪静态thinkPHP&#xff0c;开启ssl证书 具有文章改写、广告营销文案、编程…

Windows:web端UI自动化=python+selenium+pycharm框架

本篇写怎么写一个UI自动化代码。mac和Windows是一样的 都是这样写 不过&#xff0c;习惯用Windows了 如果python没有安装可以看我另一篇安装python的教程 先安装python先 下载完python 下载pip 1 安装pip $ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py # 下载…

宝塔面板使用docker+nginx+gunicorn部署Django项目实战教程

第一步&#xff1a;创建Django项目 使用pip install django安装创建django项目的依赖在电脑某个根目录下执行django-admin startproject app创建一个名为app的Django项目。目录结构如下: ├── app │ ├── init.py │ ├── asgi.py │ ├── settings.py │ ├── url…

机器学习:考试复习提纲

该页仅为复习资料&#xff0c;内含博客链接均通过搜索得到。 当然直接访问我的GitHub博客会更方便。 1. 线性回归 Linear Regression https://www.cnblogs.com/geo-will/p/10468253.html 要求1&#xff1a;可以按照自己的理解简述线性回归问题。 回归分析是一种预测性的建模…

buuctf re 37-40

[WUSTCTF2020]Cr0ssfun 打开 #include<iostream> using namespace std; int main() {char a1[32];a1[1] c;a1[25] ; a1[27] e;a1[4] 2;a1[17] r;a1[29] f;a1[17] r;a1[24] _;a1[2] t;a1[9] c;a1[32] };a1[19] v;a1[5] 0;a1[14] n;a1[15] d;a1[8] {;a1[18]…

【Leetcode每日一题】 动态规划 - 地下城游戏(难度⭐⭐⭐)(61)

1. 题目解析 题目链接&#xff1a;174. 地下城游戏 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 一、状态表定义 在解决地下城游戏问题时&#xff0c;我们首先需要对状态进行恰当的定义。一个直观的想法是&#x…

Oracle EBS Interface/API(54)- GL日记账审批

背景: 客户化创建薪酬凭证或者银企付款入账日记账以后,用户希望自动提交审批流程,无需到系统标准功能点击审批,减少用户操作。 快速参考 参考点内容功能导航N: GL->日记账->输入并发请求None基表GL.GL_JE_BATCHESAPI参考下面介绍错误信息表None接口FormNone接口Reque…

wiringpi库的应用 -- sg90 定时器 oled

sg 90舵机: 接线: VCC -- 红 GND -- 地 信号线 -- 黄 -- pwm 定时器: 先玩定时器: sg90 需要的pwm波需要定时器输出&#xff0c;so我们得先来玩一下定时器 分析&#xff1a;实现定时器&#xff0c;通过itimerval结构体以及函数setitimer产生的信号&#xff0c;系统…

Flume在大数据集群下的配置以及监控工具Ganglia的部署安装

前提&#xff1a;需要有三台虚拟机&#xff08;hadoop102,103,104&#xff09;配置好相关基础环境 安装 将安装包上传到/opt/software中 tar -zxf /opt/software/apache-flume-1.9.0-bin.tar.gz -C /opt/module/修改 apache-flume-1.9.0-bin 的名称为 flume mv /opt/module/…

Web安全知识

第二章 虚拟机运行架构&#xff1a; 1.寄居结构 2.原生架构 软件 注&#xff1a;Hyper-V是在Windows 2008操作系统上 附录 连接FTP服务器过程&#xff1a; 1.下载了软件&#xff1a; 2.连接到ftp://10.0.105.223/服务器&#xff08;访问老师课堂资源地址&#xff09; 关闭…

Node.JS后端开发笔记整理(简洁版)

前端 1. 开发环境和技术栈 开发工具&#xff1a;Visual Studio CodeNode.js版本&#xff1a;18.19.0&#xff08;建议保持在18&#xff09;包管理器&#xff1a;npm前端框架&#xff1a;Vue3.4脚本语言&#xff1a;TypeScript构建工具&#xff1a;Vite后端框架&#xff1a;Ex…

Django项目使用uwsgi+nginx部署上线

Django项目使用uwsginginx部署上线 前言settings 配置安装uwsgi 和配置uwsgi推荐配置文件启用wsgi不使用nginx的配置&#xff08;不推荐&#xff09;使用nginx的配置 安装 nginx和配置niginx 配置 运行参考资料 前言 代码已经开发完成&#xff0c;正式部署上线 settings 配置…

视频教程下载:用ChatGPT玩转海外抖音TikTok

CHATGPT for TikTok是一门前沿课程&#xff0c;旨在帮助您充分发挥TikTok广告活动的全部潜力。随着数字营销的爆炸性增长&#xff0c;企业需要使用先进的工具来保持竞争优势。在这门课程中&#xff0c;您将学习如何利用CHATGPT——一种先进的人工智能工具——来创建与目标受众产…

潜藏10年的恶意软件被发现;利用漏洞在K8S上挖矿;AWS、Google和Azure 出现信息泄露危机 | 安全周报0419

关键词&#xff1a;OfflRouter、恶意软件、VBA宏病毒、机密文件、可执行文件、iOS间谍软件、LightSpy、F_Warehouse、Azure CLI、AWS CLI、Google Cloud CLI 1. 近十年来&#xff0c;OfflRouter恶意软件在乌克兰一直未被发现 自2015年以来&#xff0c;部分乌克兰政府网络一直…

【软考】软件设计师中级

视频课 计算机组成原理 进制转换 定点数vs浮点数 校验码 计算机体系结构 指令系统 I/O 存储系统 直接映射&#xff1a;简单粗暴的死板派 全相联映射&#xff1a;跳脱的自由发挥派 组相联映射&#xff1a;折中派&#xff0c;组间直接映射&组内全相联映射 命中率&#xf…

你的mongodb客户端是哪个呢?

MongoDB 是一种流行的文档数据库&#xff0c;它可以支持多种场景和应用。有很多客户端工具可以用来管理和操作 MongoDB&#xff0c;以下是一些常用的工具&#xff0c;以及它们的介绍&#xff1a; 一、MongoDB Shell MongoDB Shell 是连接&#xff08;和使用&#xff09;MongoD…

【银角大王——Django课程Day1】

Django框架第一课 安装Django框架方式一&#xff08;命令行的形式创建Django项目&#xff09;方式二&#xff08;适合企业版的pycharm&#xff09;默认文件介绍app文件介绍快速上手我的导包一直爆红是因为我没使用解释器&#xff0c;没导入包&#xff0c;去设置里面导入包即可—…

(保姆级教学)跨站请求伪造漏洞

1. CSRF漏洞 CSRF&#xff08;Cross-site request forgery&#xff09;跨站请求伪造&#xff0c;也被称为One Click Attack 或者Session Riding&#xff0c;通常缩写为CSRF或者XSRF&#xff0c;是一种对网站的恶意利用。尽管听起来像跨站脚本&#xff08;XSS&#xff09;&…

【银角大王———Django学习DAY0——基础准备】

银角大王——Django学习前情提要 &#xff08;1&#xff09;在pycharm中下载Flask&#xff08;2&#xff09;使用Flask&#xff08;3&#xff09;下载BootStrap框架&#xff08;4&#xff09; 使用BootStrap框架 &#xff08;1&#xff09;在pycharm中下载Flask 在设置——项目…

搭建sql-lab出现的php不兼容

下载不了的时候&#xff0c;直接打开该网址下载5.xphp版本&#xff0c;解压到C:\php_studyv8\phpstudy\phpstudy_pro\Extensions\php&#xff08;可能路径都不一样&#xff0c;找到Extensions\php放到该目录下&#xff09;
最新文章