uniapp 使用canvas画海报(微信小程序)
效果展示:
项目要求:点击分享绘制海报,并实现分享到好友,朋友圈,并保存
先实现绘制海报
<view class="data_item" v-for="(item,index) in dataList" :key="index"@click="goDetail(item,index)"><view class="data_bot"><view class="share" @click.stop="goShare(item,index)"><image :src="getimg('share_gray.png')" style="width: 36rpx;height: 36rpx;"></image><view class="share_tt">分享</view></view></view></view><view class="post_box" :class="isShare?' ':'post_box2'"><view class="post"><canvas style="width: 327px; height:482px;margin: 0 auto;" canvas-id="firstCanvas1"></canvas></view></view>
// 生成海报goShare(item, index) {let _this = this;this.shareId = item.idthis.isShare = true// uni.hideTabBar()let userInfo = uni.getStorageSync('userInfo')uni.getImageInfo({src: userInfo.avatar,success: function(res) {if (res.path) {_this.canvasr(item, index, res.path, userInfo.nickname);}},});},
// 画海报canvasr(item, index, avatar, username) {let ctx = uni.createCanvasContext('firstCanvas1');// 背景图let postImage = '../../static/images/poster_bg.png'; //背景图ctx.globalAlpha = 0; // 设置图像透明度为0 为了让背景透明ctx.setFillStyle('#ffffff'); // 默认白色ctx.fillRect(0, 0, 327, 482);ctx.globalAlpha = 1; // 设置图像透明度为1ctx.drawImage(postImage, 0, 0, 327, 482); // (图片,x,y,宽,高)背景图ctx.save();// 头像ctx.restore();// let avatar = userInfo.avatar + "?timestamp=" + Date.now(); //地址栏加入新参数 解决canvas图片跨域问题this.drawAvatar(ctx, avatar, 30, 20, 15)// 昵称 +ctx.setFontSize(12); //设置字体字号ctx.setFillStyle('#000000'); //设置字体颜色// ctx.fillText(username, 65, 39); // (文字,x,y)let _strlineW = 0;let nickname = '';let actI = 0;for (let i = 0; i < username.length; i++) {_strlineW += ctx.measureText(username[i]).width;if(_strlineW <= 60){ctx.fillText(username, 65, 39); // (文字,x,y)}else if (_strlineW > 60 && _strlineW <= 70) {actI = inickname = username.substring(0, i) + '…'ctx.fillText(nickname, 65, 39); // (文字,x,y)}else{nickname = username.substring(0, actI-1) + '…'ctx.fillText(nickname, 65, 39); // (文字,x,y)}}// 分享了一个点子let txt = '我发现了一个不错的点子';ctx.setFontSize(12); //设置字体字号ctx.setFillStyle('#999999'); //设置字体颜色ctx.fillText(txt, 135, 39); // (文字,x,y)// 内容开始引号图let startImage = '../../static/images/poster_quote_up.png'; //背景图ctx.drawImage(startImage, 30, 70, 32, 32); // (图片,x,y,宽,高)背景图ctx.save();ctx.restore();// 内容let content = item.idea_name;this.drawtext(ctx, content, 30, 130, 32, 267)// 内容结束引号图let endImage = '../../static/images/poster_quote_down.png'; //背景图ctx.drawImage(endImage, 265, 310, 32, 32); // (图片,x,y,宽,高)背景图ctx.save();ctx.restore();// 一条直线ctx.setFillStyle('#D8D8D8'); // 默认白色ctx.fillRect(30, 362, 267, 0.5); //x y 宽 高ctx.restore();// logo图let logoImage = '../../static/images/aiyop_logo.png'this.drawAvatar(ctx, logoImage, 30, 402, 15)ctx.save();ctx.restore();// AI有谱let title = 'AI有谱';ctx.setFontSize(13); //设置字体字号ctx.setFillStyle('#000000'); //设置字体颜色ctx.fillText(title, 70, 415); // (文字,x,y)// 心中有谱,奇思妙想也靠谱let intro = '心中有谱,奇思妙想也靠谱';ctx.setFontSize(10); //设置字体字号ctx.setFillStyle('#666666'); //设置字体颜色ctx.fillText(intro, 70, 430); // (文字,x,y)// 二维码let qrImage = '../../static/images/logo_program.jpg';this.drawAvatar(ctx, qrImage, 225, 381, 36)// 绘制ctx.draw();},// 绘制头像drawAvatar(ctx, img, x, y, r) {ctx.save();let d = r * 2;let cx = x + r;let cy = y + r;ctx.beginPath()ctx.arc(cx, cy, r, 0, 2 * Math.PI);ctx.strokeStyle = '#FFFFFF'; // 设置绘制圆形边框的颜色ctx.stroke(); // 绘制出圆形,默认为黑色,可通过 ctx.strokeStyle = '#FFFFFF', 设置想要的颜色ctx.clip();ctx.drawImage(img, x, y, d, d);ctx.restore();},// 文本n行换行与显示省略号// 1、canvas对象 2、文本 3、X轴 4、Y轴 5、单行行高 6、文本的宽度drawtext(ctx, str, axisX, axisY, titleHeight, maxWidth) {ctx.setFontSize(22); //设置字体字号ctx.setFillStyle('#000000'); //设置字体颜色// 文本处理let strArr = str.split("");let row = [];let temp = "";for (let i = 0; i < strArr.length; i++) {if (ctx.measureText(temp).width < maxWidth) {temp += strArr[i];} else {i--; //这里添加了i-- 是为了防止字符丢失,效果图中有对比row.push(temp);temp = "";}}row.push(temp); // row有多少项则就有多少行//如果数组长度大于6,现在只需要显示6行则只截取前两项,把第6行结尾设置成'...'if (row.length > 6) {let rowCut = row.slice(0, 6);let rowPart = rowCut[5];let test = "";let empty = [];for (let i = 0; i < rowPart.length; i++) {if (ctx.measureText(test).width < maxWidth) {test += rowPart[i];} else {break;}}empty.push(test);// let group = empty[0] //这里只显示两行,超出的用...表示let group = empty[0] + "..." //这里只显示两行,超出的用...表示rowCut.splice(5, 1, group);row = rowCut;}// 把文本绘制到画布中for (let i = 0; i < row.length; i++) {// 一次渲染一行ctx.fillText(row[i], axisX, axisY + i * titleHeight, maxWidth);}// 保存当前画布状态ctx.save()// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中。// ctx.draw()},
下面是海报下面的分享弹窗
<!-- 分享海报的遮罩层 --><view v-if="isShare" class="over_box" @touchmove.stop.prevent="moveHandle"></view><!-- 分享海报 --><u-popup :show="isShare" @close="closeShare" @open="openShare" mode="bottom" :round="10" :overlay="false"><view class="trans_box"><view class="per_box"><button open-type="share" class="per_but" @click="shareWeixin('WXSceneSession')"><image :src="getimg('wehcat.png')" style="width:86rpx;height: 86rpx;"></image><view class="per_txt">微信好友</view></button><button class="per_but" @click="shareWeixin('WXSceneTimeline')"><image :src="getimg('wechat-moments.png')" style="width:86rpx;height: 86rpx;"></image><view class="per_txt">朋友圈</view></button><button @click="saveLocal" class="per_but"><image :src="getimg('save-poster.png')" style="width:86rpx;height: 86rpx;"></image><view class="per_txt">保存图片</view></button></view><view class="share_line"></view><view class="close_tran" @click="closeShare">取消</view></view></u-popup>
因为分享到朋友圈实在没找到有使用自定义按钮的可能,所以还是需要点击右上角胶囊
onLoad(){//分享功能wx.showShareMenu({withShareTicket: true,//设置下方的Menus菜单,才能够让发送给朋友与分享到朋友圈两个按钮可以点击menus: ["shareAppMessage", "shareTimeline"]})}
//点击分享朋友,朋友圈事件
shareWeixin(scene) {if (scene == 'WXSceneTimeline') {uni.showToast({title: '点击右上角胶囊分享朋友圈',icon: 'none',duration: 2000})} else {uni.share({provider: "weixin",scene: scene,type: 0,href: '/subPackage/index/like_details?id=' + this.shareId + '&type=1',title: '点子分享',success(res) {console.log(res);},fail(err) {console.log(err);}})}},
onShareAppMessage(res) {if (res.from === 'button') { // 来自页面内分享按钮return {title: 'xxxxxxx', //分享的名称path: '/subPackage/index/like_details?id=' + this.shareId + '&type=1',mpId: 'wx000000000', //此处配置微信小程序的AppIdimageUrl: ''}}return {title: 'xxxxx', //分享的名称path: '/subPackage/index/like_details?id=' + this.shareId + '&type=1',mpId: 'wx0000000000', //此处配置微信小程序的AppIdimageUrl: ''}},//分享到朋友圈onShareTimeline(res) {return {title: this.timeItem,path: '/pages/index/index',type: 0,summary: "",imageUrl: ''}},
以上就是画海报以及分享的全部过程了,另有一个点:
就是分享朋友的地址path: '/subPackage/index/like_details?id=' + this.shareId + '&type=1'
这里这个加个type=1是因为点击分享进入小程序的是个详情页,可能会出现点击左上角返回不能返回首页,所以加上这个type可以在分享页加个判断
onLoad(option) {if(option.type && option.type==1){ //分享来的页面this.shareType = 1}},
//左上角的点击返回事件加判断,当是分享进入的时候回到首页goBack(){if(this.shareType==1){uni.switchTab({url:'/pages/index/index'})}else{uni.navigateBack()this.shareType = 0}},
相关文章:
uniapp 使用canvas画海报(微信小程序)
效果展示: 项目要求:点击分享绘制海报,并实现分享到好友,朋友圈,并保存 先实现绘制海报 <view class"data_item" v-for"(item,index) in dataList" :key"index"click"goDet…...
TiDB 应急运维脚本,更加方便的管理TiDB集群
TiDB 应急运维脚本,更加方便的管理TiDB集群 使用方法 使用方法:[tidblocalhost ~]$ which tiup ~/.tiup/bin/tiup编辑脚本,MYSQL_PASSWD 和 PORT 根据实际替换 [tidblocalhost ~]$ vi ~/.tiup/bin/ti#version 1.1 #author guanguanglei ##…...
第二部分:AOP
一、AOP简介 AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构。 AOP是OOP(面向对象编程)的进阶版。 作用:在不改变原始设计的基础上为其进行功能增强。 spring理念&#x…...
分享一组天气组件
先看效果: CSS部分代码(查看更多): <style>:root {--bg-color: #E9F5FA;--day-text-color: #4DB0D3;/* 多云 */--cloudy-background: #4DB0D3;--cloudy-temperature: #E6DF95;--cloudy-content: #D3EBF4;/* 晴 */--sunny-b…...
微服务中RestTemplate访问其他服务返回值转换问题
背景: 接收一个springcloud项目,UI模块访问其他服务的接口,返回数据统一都是使用fastjson进行转换,但是新开发了几个新模块之后发现fastjson很多bug(各种内存溢出),但是很多地方已经重度依赖fa…...
Centos7.9上(离线)安装Gitlab
1、下载Gitlab的rpm安装包Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 2、安装rpm -i gitlab-ce-10.0.0-ce.0.el7.x86_64.rpm,如果依赖缺失,yum安装即可 3、vi /etc/gitlab/gitlab.rb 配置external_url&…...
pikachu中RCE出现乱码的解决的方案
exec “ping” 输入127.0.0.1 这种乱码的解决办法就是在pikachu/vul/rce/rce_ping.php目录里面的第18行代码 header("Content-type:text/html; charsetgbk");的注释打开即可。 BUT但是吧!又出现了其他的乱码!但是搞完这个再把它给注释掉就行了…...
airflow是什么
Airflow 简介 Airflow是一个基于有向无环图(DAG)的可编程、调度和监控的工作流平台,它可以定义一组有依赖的任务,按照依赖依次执行。airflow提供了丰富的命令行工具用于系统管控,而其web管理界面同样也可以方便的管控调度任务,并…...
训练用于序列分类任务的 RoBERTa 模型的适配器
介绍 NLP当前的趋势包括下载和微调具有数百万甚至数十亿参数的预训练模型。然而,存储和共享如此大的训练模型非常耗时、缓慢且昂贵。这些限制阻碍了 RoBERTa 模型开发更多用途和适应性更强的 NLP 技术,该模型可以从多个任务中学习并针对多个任务进行学习;在本文中,我们将重…...
Linux之awk判断和循环
echo zhaoy 70 72 74 76 74 72 >> score.txt echo wangl 70 81 84 82 90 88 >> score.txt echo qiane 60 62 64 66 65 62 >> score.txt echo sunw 80 83 84 85 84 85 >> score.txt echo lixi 96 80 90 95 89 87 >> score.txt把下边的内容写入到s…...
Django入门
Day1 django环境安装 创建虚拟环境 # step1 创建虚拟环境 python3 -m venv datawhale_django # step2 mac进入虚拟环境 source ./datawhale_django/bin/activate # step3 退出虚拟环境 deactivate安装包 pip3 install django pip3 install djangorestframework pip3 …...
uniapp 格式化时间刚刚,几分钟前,几小时前,几天前…
效果如图: 根目录下新建utils文件夹,文件夹下新增js文件,文件内容: export const filters {dateTimeSub(data) {if (data undefined) {return;}// 传进来的data必须是日期格式,不能是时间戳//将字符串转换成时间格式…...
JProfiler —CPU评测
当JProfiler测量方法调用的执行时间及其调用堆栈时,我们称之为“CPU评测”。这些数据以多种方式呈现。根据你试图解决的问题,其中一个或另一个演示将是最有帮助的。默认情况下不会记录CPU数据,您必须打开CPU记录才能捕获有趣的用例。 一、调…...
994. 腐烂的橘子
994. 腐烂的橘子(面试题打卡/前缀和/简单) 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/rotting-oranges/ 题干: 在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:…...
Rx.NET in Action 第三章学习笔记
3 C#函数式编程思想 本章内容包括 将 C# 与函数式技术相结合使用委托和 lambda 表达式使用 LINQ 查询集合 面向对象编程为程序开发提供了巨大的生产力。它将复杂的系统分解为类,使项目更易于管理,而对象则是一个个孤岛,你可以集中精力分别处理…...
Windows11环境下VS2019调用Pytorch语义分割模型(C++版)
语义分割模型在训练时往往采用python脚本进行网络搭建和训练,并获得训练好的模型。为了提高效率方便整个工程项目部署,实际工程应用中通常希望使用C编程语言调用训练好的网络模型。查询大量网络资料并踩过无数坑后,经实际测试实现了在window1…...
Milkv Duo 以太网使用与配置
网络配置 使能网卡 使用ip link查看是否存在eth0网卡,若无使用如下命令使能网卡: ip link set eth0 up两次使用ip link确认网卡eth0已经使能。 配置IP、gws 本人设备连接到华为路由器下,故增加如下路由信息: ip route add d…...
bash: make: command not found
make之后报错信息如下:cd 对应的文件路径后 make 发现报错:bash: make: command not found 这个原因可能是没有安装make工具,也可能是安装了make之后,没有将make的文件路径添加到系统环境变量中 有没有安装make,可以使用Search Everything搜索是否有make…...
热点如何用于期刊写作——以chatGPT为例
交叉领域A,B 以自己为例子,A是教育 B是技术,我是教育技术学专业。 经验来源 知网关于GPT的140余篇专业论文的观察 截止至2023年8月14日15:35:45 学习每出现一个热点,如何应用于学术。 实践阅读发现 套路一:谈理论…...
IGV.js 的完全本地化运行探索
问题及解决方法 IGV.js 完全本地化是为了合规,不使用外网的情况下查看基因组。不联网需要下载 genomes.json 文件及其中的内容之外,还需要修改 igv.js本身,防止5s超时后才显示网页内容。修改的关键词是: genomes.json,改为本地的…...
日立电梯05版规格表智能计算工具(升级版)|WPS宏支持|适配WPS2024+Win10 64位
温馨提示:文末有联系方式日立电梯05规格表工具升级版正式发布 全新优化的日立电梯05规格表计算软件现已上线,专为电梯设计、安装与维保工程师打造,大幅提升参数录入与校验效率。功能标识更直观,操作一目了然 所有计算模块、输入项…...
Gephi实战:如何用外观和布局打造专业级网络可视化图表(附详细参数设置)
Gephi实战:如何用外观和布局打造专业级网络可视化图表(附详细参数设置) 当面对复杂的网络数据时,如何让节点和边的关联关系一目了然?Gephi作为开源的网络分析工具,其强大的可视化功能能帮助我们从杂乱的数据…...
从暴力搜索到理论最优:一道任务调度问题的完整算法演进历程
引言在算法竞赛的世界里,每一道题都像是一个等待解开的谜题。今天,我将与大家分享一道关于任务调度问题的完整解题心路历程。这个故事不仅记录了我从暴力搜索到最优算法的探索过程,更展现了在面对复杂问题时,如何通过逐步优化、深…...
彩虹云发卡商城源码二开美化版
在数字商品交易领域,自动发卡系统已成为许多创业者和商家的首选工具。彩虹云发卡商城作为业内知名的开源解决方案,凭借其稳定性和灵活性赢得了广泛认可。而基于原版进行二次开发的美化版本,则在保持核心功能的基础上,进一步提升了…...
2026奇点大会AI部署白皮书深度解密(Kubernetes+LLM Runtime双栈融合架构首次公开)
第一章:2026奇点智能技术大会:AI原生容器化部署 2026奇点智能技术大会(https://ml-summit.org) AI原生容器化部署已成为大模型服务落地的核心范式。与传统微服务容器化不同,AI原生部署需同时满足GPU资源弹性调度、模型权重分片加载、推理请求…...
AI原生研发ROI不达标?你可能漏算了这6个合规性折损因子(GDPR/《生成式AI服务管理暂行办法》双轨折价模型)
第一章:AI原生软件研发ROI计算方法详解 2026奇点智能技术大会(https://ml-summit.org) AI原生软件的研发投入产出比(ROI)不能沿用传统软件工程的静态人力-工时模型,而需构建融合模型训练成本、推理服务开销、数据飞轮收益与业务转…...
Venera漫画应用:开源漫画聚合阅读器的完整实战指南
Venera漫画应用:开源漫画聚合阅读器的完整实战指南 【免费下载链接】venera A comic app 项目地址: https://gitcode.com/gh_mirrors/ve/venera 在数字漫画阅读的广阔世界里,你是否曾为寻找一款既能阅读本地漫画、又能聚合全网资源的应用而烦恼&a…...
DAMO-YOLO TinyNAS模型评估全攻略:mAP/PR曲线
DAMO-YOLO TinyNAS模型评估全攻略:mAP/PR曲线 1. 为什么模型评估比训练更重要 刚跑通DAMO-YOLO TinyNAS的训练流程时,很多人会直接跳到部署环节,觉得“能出结果就行”。但实际项目中,我见过太多团队在交付前才发现模型在真实场景…...
从Kubernetes到KubeLLM:AI原生栈告警体系迁移实录(含TensorRT-LLM GPU显存泄漏自动定位脚本)
第一章:AI原生软件研发监控告警体系搭建 2026奇点智能技术大会(https://ml-summit.org) AI原生软件具备动态推理路径、模型权重热更新、多模态输入响应等特性,传统基于静态服务拓扑的监控体系难以捕获其运行时语义异常。构建面向AI原生应用的监控告警体…...
Python uiautomation实现微信消息自动监控与提醒
1. 为什么需要微信消息自动监控? 每天工作的时候,最烦的就是不断弹出的微信消息。频繁切换窗口查看消息,不仅打断工作思路,还严重影响效率。但完全不看又怕错过重要信息,这种矛盾相信很多人都遇到过。 我去年接手了一个…...
