【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.widthcanDom.height = data.heightlet ctx = canDom.getContext('2d')let d = data.txtlet font = ` ${d.weight || 'italic'} ${d.size || 30}px ${d.fontFamily || '微软雅黑'}`// console.log('font', font);ctx.font = fontctx.textAlign = d.alignlet 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.borderctx.strokeStyle = d.colorctx.stroke()} else {ctx.fillStyle = d.colorctx.fill()}ctx.restore()},// 绘制文字drawText(d, ctx) {let font = ` ${d.weight || 'italic'} ${d.size || 30}px ${d.fontFamily || '微软雅黑'}`// console.log('font', font);ctx.font = fontctx.textAlign = d.alignlet val = d.valuelet isSlice = falseif (d.maxWidth) {let tw = ctx.measureText(val)// console.log('tw', tw);while (tw.width > d.maxWidth) {isSlice = truelet 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.colorctx.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.ylet leftWidth = d.xvar 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.colorctx.fillText(str.substring(lastSubStrIndex, i + 1), leftWidth, initHeight);}}},// 绘制矩形drawRect(d, ctx) {ctx.save()if (d.border) {ctx.strokeStyle = d.colorctx.lineWidth = d.borderctx.strokeRect(d.x, d.y, d.w, d.h)} else {ctx.fillStyle = d.colorctx.fillRect(d.x, d.y, d.w, d.h)}ctx.restore()},loadFont(d) {if (!d.font) returnlet 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.widthcanDom.height = data.heightreturn 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.imgUrllet 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.mzph.cn/web/2153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RT-Thread电源管理组件

电源管理组件 嵌入式系统低功耗管理的目的在于满足用户对性能需求的前提下&#xff0c;尽可能降低系统能耗以延长设备待机时间。 高性能与有限的电池能量在嵌入式系统中矛盾最为突出&#xff0c;硬件低功耗设计与软件低功耗管理的联合应用成为解决矛盾的有效手段。 现在的各种…

排序算法之桶排序

目录 一、简介二、代码实现三、应用场景 一、简介 算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度排序方式稳定性桶排序O(nk )O(nk)O(n^2)O(nk)Out-place稳定 稳定&#xff1a;如果A原本在B前面&#xff0c;而AB&#xff0c;排序之后A仍然在B的前面&#xff1b; 不…

Kotlin语法快速入门--条件控制和循环语句(2)

Kotlin语法入门–条件控制和循环语句&#xff08;2&#xff09; 文章目录 Kotlin语法入门--条件控制和循环语句&#xff08;2&#xff09;二、条件控制和循环语句1、if...else2、when2.1、常规用法2.2、特殊用法--并列&#xff1a;2.3、特殊用法--类型判断&#xff1a;2.4、特殊…

C语言进阶课程学习记录-第48课 - 函数设计原则

C语言进阶课程学习记录 - 函数设计原则 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录

无人驾驶 自动驾驶汽车 环境感知 精准定位 决策与规划 控制与执行 高精地图与车联网V2X 深度神经网络学习 深度强化学习 Apollo

无人驾驶 百度apollo课程 1-5 百度apollo课程 6-8 七月在线 无人驾驶系列知识入门到提高 当今,自动驾驶技术已经成为整个汽车产业的最新发展方向。应用自动驾驶技术可以全面提升汽车驾驶的安全性、舒适性,满足更高层次的市场需求等。自动驾驶技术得益于人工智能技术的应用…

端口被占用的解决方案汇总

端口被占用的解决方案汇总 【一】windows系统端口被占用【二】Linux系统端口被占用【三】Linux的ps命令查找&#xff08;1&#xff09;ps命令常用的方式有三种&#xff08;2&#xff09;ps -ef |grep 8080 【一】windows系统端口被占用 &#xff08;1&#xff09;键盘上按住Wi…

【LeetCode刷题记录】21. 合并两个有序链表

21 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 …

# MySQL中的并发控制,读写锁,和锁的粒度

MySQL中的并发控制&#xff0c;读写锁&#xff0c;和锁的粒度 并发控制的概述 在数据库系统中&#xff0c;并发控制是一种用于确保当多个用户同时访问数据库时&#xff0c;系统能够提供数据的一致性和隔离性的机制。MySQL支持多种并发控制技术&#xff0c;其中包括锁机制、多…

调试 WebSocket API 技巧分享

WebSocket 是一种在单个 TCP 连接上实现全双工通信的先进 API 技术。与传统的 HTTP 请求相比&#xff0c;WebSocket 提供了更低的延迟和更高的通信效率&#xff0c;使其成为在线游戏、实时聊天等应用的理想选择。 开始使用 Apifox 的 WebSocket 功能 首先&#xff0c;在项目界…

node和go的列表转树形, 执行速度测试对比

保证数据一致性&#xff0c;先生成4000条json数据到本地&#xff0c;然后分别读取文本执行处理 node代码 node是用midway框架 forNum1:number 0forNum2:number 0//执行测试async index(){// 生成菜单列表// const menuList await this.generateMenuList([], 4000);const men…

双周总结#008 - AIGC

本周参与了公司同事对 AIGC 的分享会&#xff0c;分享了 AIGC 在实际项目中的实践经验&#xff0c;以及如何进行 AIGC 的落地。内容分几项内容&#xff1a; 什么是 AIGCAIGC 能做什么AIGC 工具 以年终总结为例&#xff0c;分享了哪些过程应用了 AIGC&#xff0c;以及 AIGC 落地…

QA测试开发工程师面试题满分问答19: url请求到响应整个过程,涉及到什么技术细节

概述 当你点击鼠标发起一个请求&#xff0c;直到页面显示响应数据&#xff0c;整个过程可以详细展开为以下步骤&#xff1a; 用户点击鼠标&#xff1a;用户在浏览器中点击某个链接或按钮&#xff0c;触发请求的发起。 URL 解析&#xff1a;浏览器解析点击的链接中的 URL&…

在线音乐播放网站项目测试(selenium+Junit5)

在做完在线音乐播放网站项目之后&#xff0c;需要对项目的功能、接口进行测试&#xff0c;利用测试的工具&#xff1a;selenium以及Java的单元测试工具Junit进行测试&#xff0c;下面式测试的思维导图&#xff0c;列出该项目需要测试的所有测试用例&#xff1a; 测试结果&#…

下列程序定义了NxN的二维数组,并在主函数中自动赋值。请编写函数fun(int a[][N],int n),该函数的功能是:使数组右上半三角元素中的值乘以m。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 下列…

【QML】State组件

State(状态)组件是一组来自默认配置的批处理更改。所有项都有一个默认状态&#xff0c;该状态定义对象和属性值的默认配置。可以通过将State项添加到states属性来定义新的状态&#xff0c;以允许项在不同的配置之间切换。 State组件的基本用法如下&#xff1a; Window {id: …

【ARM Trace32(劳特巴赫) 使用介绍 12.1 -- Trace32 读写 64位地址】

请阅读【Trace32 ARM 专栏导读】 文章目录 Trace32 读写 64位地址读 64 位地址写64位地址Trace32 读写 64位地址 在使用TRACE32进行调试时,有时需要读取或操作64位的地址,特别是在处理64位的处理器或操作系统时。以下是如何在TRACE32中读取64位地址的一般方法。 读 64 位地…

MySQL行级锁——技术深度+1

引言 本文是对MySQL行级锁的学习&#xff0c;MySQL一直停留在会用的阶段&#xff0c;需要弄清楚锁和事务的原理并DEBUG查看。 PS:本文涉及到的表结构均可从https://github.com/WeiXiao-Hyy/blog中获取&#xff0c;欢迎Star&#xff01; MySQL行级锁 行级锁&#xff08;Row-…

js连接抖音打印组件实现打印

js连接抖音打印组件实现打印小票 安装抖音打印组件 抖音打印组件文档&#xff1a; https://bytedance.larkoffice.com/docs/doccn2vbOOdd3KWrCd6Z93nIlvg 跟着文档案例一步步配基本上没问题&#xff0c; 打印的时候需要设置下打印机名称 export class DouyinPrint {construct…

怎么理解算力?1000P算力是什么概念?

算力&#xff0c;指计算机系统在单位时间内能够完成的计算任务量&#xff0c;它涵盖了CPU、GPU、TPU等硬件&#xff0c;每秒能处理的数据量&#xff0c;通常以“P”&#xff08;PetaFLOPS&#xff0c;即千万亿次浮点运算每秒&#xff09;为单位来衡量&#xff0c;是评估计算机性…

PDF被加密无法打印的解决办法

思路很清晰&#xff1a;先解密→再打印 分享四个工具&#xff0c;可以轻松解密PDF&#xff1a; ⭐i love pdf I LOVE PDF是一款免费的PDF网站&#xff0c;界面设计简洁&#xff0c;首页没有广告&#xff0c;但每个功能的操作界面是有广告的&#xff0c;不会影响使用。 部分功…