[uni-app] 海报图片分享方案 -canvas绘制

文章目录

      • canvas使用记录
        • 先看下实际效果图
        • 绘制流程及思路
          • 1. 绘制头像, 通过`drawImage`来绘制
          • 2.绘制文字部分
        • 具体代码

分享海报图片的方式,以前再RN端采用的是截图方案, 我记得组件好像是

react-native-view-shot

现在要处理uni-app的海报图片分享, 一般也有 html2canvas的相关插件

不过其缺点也有,
比如说遇到bug,有时候没办法修改什么的
手绘canvas虽然麻烦,但是胜在自由灵活

canvas使用记录

先看下实际效果图

请添加图片描述

绘制流程及思路

CanvasContext文档

其实使用下来发现, canvas绘制和iOS原生开发进行UI绘制有很多相似之处, 比如draw的入参 ,都需要x,y坐标,设置width/height等

整个canvas绘制的思路如下

0.绘制整个大矩形背景 : 通过uni.downloadFile下载网络图片来获取tempFilePath, 结合几个坐标和宽高参数, 就可以绘制了

1. 绘制头像, 通过drawImage来绘制

不过这里需要注意的是, 如果要对头像图片进行裁剪,比如圆心之类的
需要用到clip的情况下,
需要提前保存好上下文画布

		// 2.顶部头像ctx.save(); // 先保存之前的画布ctx.beginPath()ctx.arc((200 + 190 / 2) * scaleNum, (60 + 190 / 2) * scaleNum, 190 / 2 * scaleNum, 0,Math.PI * 2)ctx.clip()ctx.drawImage(this.headUrl, 200 * scaleNum, 60 * scaleNum, 190 * scaleNum, 190 *scaleNum)

(200: 左边间距)
(190: 圆的直径)
(60: top的间距)

(这里需要注意的事, 圆的圆心坐标, 是相对于x/y坐标的哦 )

2.绘制文字部分

这个没有太大的问题, 不过要注意下 textAlign的具体用法
在这里插入图片描述

具体代码

  1. html部分
			<canvas :style="{width: boxDetail.width +'px', height: boxDetail.height + 'px' }"id="sharePic" canvas-id="sharePic"></canvas>

2.js部分

	setCanvasSize() {const query = uni.createSelectorQuery().in(this);query.select('#content').boundingClientRect(data => {this.boxDetail.width = data.widththis.boxDetail.height = data.heightconsole.log(this.boxDetail, data)this.$nextTick(() => {setTimeout(() => {console.log('create and set canvas')this.createCanvas()// this.asyncAwaitCanvas()}, 800)})}).exec();},/*** @Description: 创建canvas画布*/async createCanvas() {this.context = uni.createCanvasContext('sharePic', this)const ctx = this.context;function drawCanvas(url, top) {if (url) {return new Promise((resolve, reject) => {let objobj = { url }uni.downloadFile({url,success: (res) => {top ? resolve({ ...res, top }) : resolve({ ...res })},fail: (err) => {reject(err)}})})} else {return new Promise((resolve, reject) => {top ? resolve({ top }) : resolve()})}}const scaleNum = this.boxDetail.width / 590// 1.背景图// - 读背景图资源let res = await drawCanvas("https://cdn.froglesson.com/static/cert/share_notes_content_bg.png");ctx.save();// - 绘制背景图路径/坐标/宽/高ctx.drawImage(res.tempFilePath, 0, 0, scaleNum * 590, scaleNum * 864)// 2.顶部头像ctx.save(); // 先保存之前的画布ctx.beginPath()ctx.arc((200 + 190 / 2) * scaleNum, (60 + 190 / 2) * scaleNum, 190 / 2 * scaleNum, 0,Math.PI * 2)ctx.clip()ctx.drawImage(this.headUrl, 200 * scaleNum, 60 * scaleNum, 190 * scaleNum, 190 *scaleNum)// 3.titlectx.restore() //恢复一下绘画板let title = this.returnName(this.shareData.user_name + ",已学习" + (this.shareData.count || 0) +"天")ctx.setFontSize(32 * scaleNum) //字体大小ctx.setFillStyle('#fff') //文字颜色ctx.setTextAlign('center') //文本左对其ctx.fillText(title, 590 / 2 * scaleNum, (280 + 30) * scaleNum, 530 *scaleNum); // marginleft:182rpx;maxWidth:374 最大宽度// 4.学习内容let content = "今天在「XXXXAPPXXXXX」" + (this.shareData.times > 30 ?`${this.shareData.times || 0}分钟` : "学习了")content = content + this.studyTitle() + this.studyCount();content = "我是中间文字部分,阿爸不不不不不不不不不,啊电话多好多好多好多好等哈,多撒MDJSLSJL"// - 字符拆分/计算行数let content_top = 0let contents = content.trim().split("")contents = contents.map((item, index) => {if (index && !(index % 20)) {item = item + '\n';}return item})// 逐行绘制标题文字contents.join("").split('\n').forEach((item, index) => {ctx.setFontSize(28 * scaleNum) //字体大小ctx.setFillStyle('#fff') //文字颜色ctx.setTextAlign('center') //文本左对其content_top = (365 + 30) * scaleNum + 48 * scaleNum * index //高度计算, 40是行高ctx.fillText(item, 590 / 2 * scaleNum, content_top, 530 *scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度})// 5.时间let time = this.shareData.dayctx.setFontSize(24 * scaleNum) //字体大小ctx.setFillStyle('#fff') //文字颜色ctx.setTextAlign('center') //文本左对其ctx.fillText(time, 590 / 2 * scaleNum, (609 + 30) * scaleNum, 530 *scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度// 6.icon res = await drawCanvas("https://cdn.froglesson.com/static/cert/index_logo.png")ctx.drawImage(res.tempFilePath, 210 * scaleNum, (716 + 30) * scaleNum, 80 * scaleNum,80 * scaleNum)// 7.qrcoderes = await drawCanvas(this.qrCode);ctx.drawImage(res.tempFilePath, 299 * scaleNum, (716 + 30) * scaleNum, 80 * scaleNum,80 * scaleNum)// 8.底部titlelet appName = "XXXXAPPNAMEXXXXXX"ctx.setFontSize(24 * scaleNum) //字体大小ctx.setFillStyle('#fff') //文字颜色ctx.setTextAlign('center') //文本左对其ctx.fillText(appName, 590 / 2 * scaleNum, (801 + 50) * scaleNum, 530 *scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度// 完成绘制 - 执行保存ctx.draw()console.log('绘制完毕')uni.setStorageSync('openSaveShareImage', true)this.saveOver()},saveOver() {console.log("绘制----------end")setTimeout(() => {uni.canvasToTempFilePath({canvasId: 'sharePic',destWidth: this.boxDetail.width * 2, //展示图片尺寸=画布尺寸1*像素比2destHeight: this.boxDetail.height * 2,quality: 1,fileType: 'png',success: (result) => {console.log(result.tempFilePath)uni.saveImageToPhotosAlbum({filePath: result.tempFilePath,success: function() {uni.hideToast()uni.showToast({icon: 'none',title: "图片已下载至【相册】,请打开【相册】查看", // res.tempFilePath});},fail: function() {uni.hideToast()uni.showToast({icon: 'none',title: "保存失败", // res.tempFilePath});}})},}, this);}, 1000)},

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

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

相关文章

索尼 toio™应用创意开发征文|联盟国战

✨ 能用众力&#xff0c;则无敌于天下矣&#xff1b;能用众智&#xff0c;则无畏于圣人矣。 —— 孙权 前言&#xff1a; 从火爆全网的ChatGPT&#xff0c;到人人都是开发者。AI无疑贯彻了整个2023年的主题&#xff0c;从刚上幼儿园的小朋友到耄耋之年的老顽童&#xff0c;都对…

0基础学习VR全景平台篇 第96篇:VR电子楼书

大家好&#xff0c;欢迎观看蛙色VR官方系列课程——VR电子楼书&#xff01; 作为2021年底全新上线的行业解决方案&#xff0c;是专门针对地产、园区数字化营销的一站式VR解决方案&#xff0c;为行业潜在客户提供优质的7x24小时线上看房体验。 本期教程将通过功能介绍后台操作&…

SpringBoot项目集成Druid

文章目录 一、前言二、Druid相关内容1、Druid简介1.1数据库连接池 2、项目集成Druid2.1、环境准备2.2、依赖准备2.3、编写配置文件2.4、测试访问 3、功能介绍3.1、查看数据源3.2、SQL监控3.3、URI监控 三、总结提升 一、前言 本文将介绍Druid的相关内容以及项目如何集成Druid&…

C#,《小白学程序》第十二课:日历的编制,时间DateTime的计算方法与代码

1 文本格式 /// <summary> /// 《小白学程序》第十二课&#xff1a;日历的编制&#xff0c;时间DateTime的计算方法与代码 /// 本课学习时间类型的数据 DateTime 的简单方法&#xff0c;并编制一个月的日历。 /// </summary> /// <param name"sender"…

OpenWrt系统开发笔记

openWrt英文官网&#xff1a; https://openwrt.org/ 中文官网&#xff1a; http://www.openwrt.org.cn/ 一、开发环境及编译 在github上有两个源码使用的比较多   一个是lede,地址为&#xff1a;https://github.com/coolsnowwolf/lede   另一个为OpenWrt的官方源码&#…

笔记 | 排序算法实现(Python)

排序算法 一、选择排序二、合并/归并排序三、快速排序四、计数排序 排序类型时间复杂度选择排序(Selection Sort) O ( n 2 ) O(n^{2} ) O(n2)合并/归并排序&#xff08;Merge Sort&#xff09; O ( n log ⁡ n ) O(n\log n ) O(nlogn)快速排序(Quick Sort)平均情况 O ( n log ⁡…

STM32F4X RTC

STM32F4X RTC 什么是RTCSTM32F4X RTCSTM32F4X RTC框图STM32F4X RTC计数频率STM32F4X RTC日历STM32F4X RTC闹钟 STM32F4X RTC例程 什么是RTC RTC全程叫Real-Time Clock实时时钟&#xff0c;是MCU中一个用来计时的模块。RTC的一个主要作用是用来显示实时时间&#xff0c;就像日常…

利用less实现多主题切换(配合天气现象)

1. 先看效果&#xff1a; 2. 话不多说直接撸吧&#xff1a; 原理&#xff1a;先给body元素添加style&#xff0c;再根据天气现象动态更改style 开撸&#xff1a; 创建src/assets/style/variables.less 使用 XXX:var(–XXX,‘style’) 声明系列变量&#xff0c;之后添加其他变…

单臂路由实验:通过Trunk和子接口实现VLAN互通

文章目录 一、实验背景与目的二、实验拓扑三、实验需求四、实验解法1. PC 配置 IP 地址2. PC3 属于 Vlan10&#xff0c;PC4 属于 Vlan20&#xff0c;配置单臂路由实现 Vlan10 和 Vlan20 三层互通3. 测试在 PC3 上 Ping PC4 &#xff0c;可以 Ping 通 PC4 摘要&#xff1a; 本文…

任意文件读取及漏洞复现

文章目录 渗透测试漏洞原理任意文件读取1. 任意文件读取概述1.1 漏洞成因1.2 漏洞危害1.3 漏洞分类1.4 任意文件读取1.4.1 文件读取1.4.2 任意文件读取1.4.3 权限问题 1.5 任意文件下载1.5.1 一般情况1.5.2 PHP实现1.5.3 任意文件下载 2. 任意文件读取攻防2.1 路径过滤2.1.1 过…

引爆用户参与:消息重弹,让您的推送不再被忽略

在当前各大APP拉新促活成本居高不下的大背景下&#xff0c;如何稳定存量用户、提升用户粘性就显得尤为关键。从促销活动到个性化推荐&#xff0c;从互动通知到功能提醒&#xff0c;消息推送已成为各大APP连接存量用户和目标市场之间的桥梁&#xff0c;通过点击推送&#xff0c;…

springboot初试elasticsearch

引入依赖 elasticsearch的依赖版本与你elasticsearch要一致 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency> 索引库的操作 创建索引库 impo…

Visual Studio 2019下使用C++与Python进行混合编程——环境配置与C++调用Python API接口

前言 在vs2019下使用C与Python进行混合编程,在根源上讲&#xff0c;Python 本身就是一个C库&#xff0c;那么这里使用其中最简单的一种方法是把Python的C API来嵌入C项目中&#xff0c;来实现混合编程。当前的环境是&#xff0c;win10,IDE是vs2019,python版本是3.9&#xff0c…

LeetCode 刷题记录——从零开始记录自己一些不会的

1. 最多可以摧毁的敌人城堡数目 题意 思路 两层循环&#xff0c;太low了 用一个变量记录前一个位置 代码 class Solution { public:int captureForts(vector<int>& forts) {int ans 0, pre -1;for (int i 0; i < forts.size(); i) {if (forts[i] 1 || forts…

04、javascript 修改对象中原有的属性值、修改对象中原有属性的名字(两种方式)、添加对象中新属性等的操作

1、修改对象中原有的属性值 其一、代码为&#xff1a; // 想将 obj 中的 flag 值&#xff0c;根据不同的值来变化(即&#xff1a;修改对象中原有的属性值)&#xff1b; let obj {"port": "port_0","desc": "desc_0","flag&quo…

IP应用场景查询API:深入了解网络用户行为的利器

前言 随着数字时代的不断发展&#xff0c;互联网已经成为人们生活的重要组成部分。而随着越来越多的业务和社交活动迁移到在线平台上&#xff0c;了解和理解网络用户行为变得至关重要。为了满足这个需求&#xff0c;IP 应用场景查询 API 崭露头角&#xff0c;成为深入了解网络…

安卓绘制原理概览

绘制原理 Android 程序员都知道 Android 的绘制流程分为 Measure、Layout、Draw 三步骤&#xff0c;其中 Measure 负责测量 View 的大小Layout 负责确定 View 的位置Draw 负责将 View 画在屏幕上 由 ViewRootImpl 实现的 performTraversal 方法是 Measure、layout、draw 的真正…

Linux创建新文件的几种方式

第一种是 vi 文件名&#xff0c;然后进入vi编辑&#xff0c;完了之后保存退出&#xff1b;然后ls看一下&#xff0c;文件有了&#xff1b; 在终端输入 cat > 文件名&#xff0c;这没用过&#xff1b;输入以后回车&#xff0c;不会退出命令&#xff1b;输入一行文字&#xff…

TLA+学习记录1——hello world

0x01 TLA是个好工具 编程人员一个好习惯是凡事都想偷懒&#xff0c;当然是指要科学地偷懒&#xff0c;而不是真的偷懒。一直想找到一种能检验写出的代码&#xff0c;做出的设计是否真的完全正确&#xff0c;而不是靠经验检视、代码Review、反复测试去检验。因为上述方法不管怎…

学习心得07:C#

之前也没有看过C#的书&#xff0c;C#的程序倒是搞了一些。好在项目不大&#xff0c;我又会套路。 C#很象是JAVA。好像就是JAVA出来之后&#xff0c;微软抄的。好东西就要学习&#xff0c;这不丢脸。 我倒是想&#xff0c;有没有办法把JAVA和C#进行映射&#xff0c;然后直接编译…