首页 > 其他分享 >uni-app实现生成海报

uni-app实现生成海报

时间:2024-01-14 20:13:45浏览次数:41  
标签:海报 12 avatarurl app ctx height width uni

示例图:


文献参考:

参考文档:https://zhuanlan.zhihu.com/p/597629702
uni-app官网:uni.createSelectorQuery() | uni-app官网
微信小程序官网:canvas | 微信开放文档

海报内容准备:

1、背景图片
2、二维码
3、用户头像,用户名称
4、简短的描述文案

PS:

1、若使用<canvas type="2d" id="myCanvas"></canvas> 则报错canvasToTempFilePath:fail fail canvas is empty。
2、若canvas不设置canvas-id,则报错<canvas>: canvas-id attribute is undefined(env: Windows,mp,1.06.2308310; lib: 2.25.3)。
3、若只设置canvas-id,则节点信息为null。
const query = uni.createSelectorQuery().in(this); query.select('#canvas').boundingClientRect(data => { console.log("节点信息" + JSON.stringify(data)); // null }).exec();
4、canvas绘制的图片不支持网络图片,因此使用网络需使用uni.getImageInfo获取图片信息。

实现

HTML页面

// 触发按钮
<view data-event-opts="{{[['tap',[['handleClick']]]]}}" bindtap="__e">生成海报</view>
// 海报容器
<canvas canvas-id="canvass" id="canvas" style="width:100%; height: 1000rpx;" />
// 生成的海报图片
<image src="{{imgAddRess}}" style="width:100%; height: 1000rpx;"></image>

JS页面

// 单网图
data: {
    return {
        imgAddRess: "",
        shareBg: "https://img2.baidu.com/it/u=3334525604,928778682&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1703955600&t=c88ebba9f3d8aeee92c6afd219945f9a",
        memberCount: 100,
        userImage:"/static/img/code/kong-img.png",
        userName: "快乐星球",
        shareQrImageUrl: "/static/img/code/kong-img.png"
    }
}
// 生成海报
handleClick() {
    const _this = this;
    // 因shareBg图片为线上图片,需做转换uni.getImageInfo({ src: this.shareBg })
    // 单个图片处理
    uni.getImageInfo({ src: this.shareBg }).then(res => {
    this.shareBg = res[1].path; // 获取转换后的图片路径
    this.createdCanvas().then(()=>{
      // 绘制需要延迟,不然第一次加载为空白
      setTimeout(() => {
        // 将生成的canvas图片,转为真实图片-地址
        uni.canvasToTempFilePath({
        x: 0,
        y: 0,
        canvasId: "canvass",
        success: res => {
          _this.imgAddRess =  res.tempFilePath;
          console.log('真实图片>>>>>>', res);
        },
        fail: (err) => { console.log('error', err) }
        },this); 
      }, 500);
    });
  });
},
handleClick2() {
    const _this = this;
    // 多张图片处理
    const Images = [this.shareBg, this.shareQrImageUrl, this.userImage];
    Promise.all(Images.map(img => uni.getImageInfo({ src: img }))).then(
      imageInfos => {
      console.log('Images then>>>>>>', imageInfos);
      this.shareBg = imageInfos[0][1].path;
      this.shareQrImageUrl = imageInfos[1][1].path;
      this.userImage = imageInfos[2][1].path;

      //根据参数开始绘图
      this.createdCanvas().then(()=>{
         // 绘制需要延迟,不然第一次加载为空白
        setTimeout(() => {
        // 将生成的canvas图片,转为真实图片-地址
        uni.canvasToTempFilePath({
        x: 0,
        y: 0,
        canvasId: "canvass",
        success: res => {
          _this.imgAddRess =  res.tempFilePath;
          console.log('真实图片>>>>>>', res);
        },
        fail: (err) => { console.log('error', err) }
        },this); 
      }, 500);
      })
    }
  );
}
createdCanvas(){
    const _this = this;
    return new Promise((resolve, reject) => {
        //绘图上下文
        const ctx = uni.createCanvasContext('canvass', this);
        uni.createSelectorQuery()
            .in(this)
            .select('#canvas')
            .boundingClientRect(rect => {
                if(!rect) return
                var width = rect.width;
                var height = rect.height;
                // 背景图
                ctx.drawImage( _this.shareBg, 0, 0, width, height); 

                // 文案:百位超能大神带你飞
                ctx.font = "bold 31px 'DINAlternate-Bold, DINAlternate'"
                ctx.setFillStyle('#ff9900');
                ctx.setTextAlign('left');
                ctx.fillText(_this.memberCount, Math.ceil(width*0.32), Math.ceil(height*0.103*2)+6);

                ctx.setFillStyle('#ff9900');
                ctx.setTextAlign('left');
                ctx.font = "16px 'PingFangSC-Medium, PingFang SC'"
                ctx.fillText('位超能大神带你飞',  Math.ceil(width*0.32)+66, Math.ceil(height*0.103*2));

                // 二维码区背景背景
                ctx.beginPath();
                // r:8 ,
                // 起始点
                ctx.moveTo(12, height - 92 + 8);
                // 左上角
                ctx.arcTo(12, height - 92, 12 + 8, height - 92, 8);
                // 右上角
                ctx.arcTo(width - 12, height - 92, width - 12, height - 92 + 8, 8);
                // 右下角
                ctx.arcTo(width - 12, height - 12, width - 12 - 8,  height - 12, 8);
                // 左下角
                ctx.arcTo(12, height - 12, 12, height - 12 - 8, 8);
                ctx.fillStyle = '#ffffff';
                ctx.closePath();
                ctx.fill();

                //用户头像+用户名
                let avatarurl_width = 50; //绘制的头像宽度
                let avatarurl_heigth = 50; //绘制的头像高度
                let avatarurl_x = 22; //绘制的头像在画布上的位置
                let avatarurl_y = height - 77; //绘制的头像在画布上的位置
                ctx.save(); // 保存绘图上下文
                ctx.beginPath(); // 开始创建一个路径
                ctx.arc(
                  avatarurl_width / 2 + avatarurl_x,
                  avatarurl_heigth / 2 + avatarurl_y,
                  avatarurl_width / 2,
                  0,
                  Math.PI * 2,
                  false
                ); // 画一个圆形裁剪区域
                ctx.clip(); // 裁剪
                ctx.drawImage(
                  this.userImage,
                  avatarurl_x,
                  avatarurl_y,
                  avatarurl_width,
                  avatarurl_heigth
                ); // //分享人头像-绘制图片x,y轴
                ctx.restore(); // 恢复之前保存的绘图上下文

                // 分享userName
                ctx.setFillStyle('#141414');
                ctx.setTextAlign('left');
                ctx.font = "bold 16px 'PingFangSC-Medium, PingFang SC'"
                let title = _this.userName;
                if(title.length>14){
                  title =title.substr(0,14)+'...'
                }
                ctx.fillText(title, 80, height - 55)                ;

                // 分享描述
                ctx.setFillStyle('#666666');
                ctx.setTextAlign('center');
                ctx.font = "11px 'PingFangSC-Regular, PingFang SC'" 
                ctx.fillText('欢迎来到快乐星球根据地', 140, height - 33);ht - 33);

                // 二维码图片
                ctx.drawImage(_this.shareQrImageUrl, width - 84, height - 84, 64, 64 );
                ctx.draw();
              }).exec()
          //获取节点
          resolve()
     })
}

多网图

data: {
    return {
  imgAddRess: "",
  shareBg:"https://img0.baidu.com/it/u=2868999810,2001254139&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=714",
  shareQrImageUrl: "https://img1.baidu.com/it/u=206225810,1732683717&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  avater: "https://img2.baidu.com/it/u=1979438230,3745650916&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"
    }
}
// 生成海报
 handleSetImage() {
   const _this = this;
   const Images = [this.shareBg, this.shareQrImageUrl, this.avater];
   let aa = Images.map(img => uni.getImageInfo({ src: img }));

   Promise.all(Images.map(img => uni.getImageInfo({ src: img }))).then(
     imageInfos => {
       console.log('Images then>>>>>>', imageInfos);
       this.shareBg = imageInfos[0][1].path;
       this.shareQrImageUrl = imageInfos[1][1].path;
       this.avater = imageInfos[2][1].path;

       //根据参数开始绘图
       this.createdCanvas().then(()=>{
         // 绘制需要延迟,不然第一次加载为空白
         setTimeout(() => {
           wx.canvasToTempFilePath({
             x: 0,
             y: 0,
             canvasId: "mc",
             success: res => {
               _this.imgAddRess =  res.tempFilePath;
               console.log('真实图片>>>>>>', res);
             },
             fail:(error)=>{
               console.log('error',error)
               uni.showToast({
                 title: error.errMsg,
                 icon:'none',
                 duration:2000,
               });
             }
           },this);
         }, 500);
       })
     }
   );
 },
 // 海报绘图
 createdCanvas() {
   const _this = this;
   const query = wx.createSelectorQuery().in(this);
   query.select('#mc').boundingClientRect(data => {
     console.log("得到布局位置信息" + JSON.stringify(data));
   }).exec();

   return new Promise((resolve, reject) => {
   //绘图上下文
   const ctx = wx.createCanvasContext('mc', this);
   wx.createSelectorQuery()
   .in(this)
   .select('#mc')
   .boundingClientRect(rect => {
     console.log('rect111',rect)
     if(!rect) return
     var width = rect.width;
     var height = rect.height;
     let qrw = (width-200) / 2;
     let qrh = ((height - 200) / 2 + 80);
     // 背景图
     ctx.drawImage( _this.shareBg, 0, 0, width, height); 

     // 头像
     ctx.drawImage(_this.avater, (width - 80) / 2 , 50 , 80, 80 );

     // 文案
     ctx.setFillStyle('#ffffff');
     ctx.setTextAlign('center');
     ctx.font = "bold 26px 'PingFangSC-Medium, PingFang SC'"
     ctx.fillText('超能小飞侠',  180, 170, width);

     ctx.font = "bold 16px 'DINAlternate-Bold, DINAlternate'"
     ctx.setFillStyle('#000000');
     ctx.setTextAlign('center');
     ctx.fillText("诚邀您参与超能封神", 185, 205, width); // (文本,X坐标,Y坐标, 最大宽度)

     // 二维码:(图片,X坐标,Y坐标, 图宽,图高)
     ctx.drawImage(_this.shareQrImageUrl, qrw , qrh , 200, 200 );
     //二维码图片
     ctx.draw();
   }).exec()
   //获取节点
   resolve()
   })
 },

标签:海报,12,avatarurl,app,ctx,height,width,uni
From: https://www.cnblogs.com/min77/p/17964116

相关文章

  • UniLoginForm无边框的方法
    UniLoginForm无边框的方法在应用系统的登录界面,我们一般采用无边框的方式,在VCL模式下,一般将登录界面的窗体的BorderStyle属性设置为bsNone即可。但采用UniGUI的TUniLoginForm方式,设置了窗体的BorderStyle属性为bsNone,其显示也带边框:我们采用下面解决办法:为登录UniLoginForm1......
  • app.UseAuthentication()、app.UseAuthorization() 及其它
    首先,这两段代码拼写就比较容易混淆,一个是UseAuthentication,一个是UseAuthorization,很容易看混。app.UseAuthentication(); 这个方法代表"认证"中间件的添加,这个中间件负责对来自客户端的请求进行认证,也就是说,它会把请求中包含的用户信息(比如一个cookie或者token)解释出来,然后构......
  • uni-app中的推送
    需求:最近公司要做推送,用的是uni-app,这里备注一下 App.vue里这样操作:分别是iOS和Android的在线创建推送,以及点击事件的处理,这里点击事件存储一下,然后发送消息在首页处理推送。如果在这里处理,会有先跳转推送页再返回首页的问题。plus.push.addEventListener('click',fu......
  • uniapp的video组件在层级太高,无法遮挡的问题
    uniapp-vue项目中需要播放视频,uniapp的video组件在层级太高,无法遮挡,所以使用原生dom的video标签在APP中播放视频,可以被其他元素进行覆盖、遮挡,页面具有更高的定制性<!--eslint-disable--><template><viewv-html="videoHtml"id="dom-video"class="dom-video......
  • Azure Logic Apps
      在AzureSentinel中,AzureLogicApps可以用于增强和自动化安全操作和响应。它们可以作为安全编排自动化响应(SOAR)的一部分,帮助自动化和简化安全工作流程。以下是一些具体的应用实例: 数据富集和分析:例子:在接收到安全警报时,使用LogicApp来自动从其他源(如威胁情报数据......
  • Next.js 开发指南 路由篇 | App Router
    Next.js开发指南路由篇|AppRouter 前言路由(routers)是应用的重要组成部分。所谓路由,有多种定义,对于应用层的单页应用程序而言,路由是一个决定URL如何呈现的库,在服务层实现API时,路由是解析请求并将请求定向到处理程序的组件。简单的来说,在Next.js中,路由决定了一个页......
  • 微软企业库Unity学习笔记(一)
    微软企业库Unity学习笔记(一) 本文主要介绍:关于Unitycontainer配置,注册映射关系、类型,单实例、已存在对象和指出一些container的基本配置,这只是我关于Unity的学习心得和笔记,希望能够大家多交流相互学习到更多知识,谢谢大家的支持。我们可以通过以下两种方法给Unitycontain......
  • 微软企业库Unity学习笔记(二)
    微软企业库Unity学习笔记(二) 接下来介绍一下依赖注入的方式:构造函数注入属性注入方法注入一、构造函数注入我们将介绍单构造函数和多构造函数注入1)单构造函数使用自动注入单构造函数自动注入,这里我们使用一个简单的例子具体类MyObject依赖于具体类MyDependentC......
  • 如何再造宇宙厂所有App?
    本文内容,纯属十年老架构师杜撰,切勿照着实操,可能会给你带来几十亿的流量,怕你的服务器扛不住。破音前端用uniapp,花800买个短视频应用模板,后端用golang支持高并发,数据库用图数据库加elasticsearch,用户关系用图数据库,内容元数据直接放在elasticsearch,方便搜索,视频,音频,图片......
  • 如何让Visual Studio Tools for Unity插件用于调试你自己的Mono嵌入应用程序
       最近在测试将mono嵌入到C++应用程序中,苦于没有调试器,有时候还是不怎么方便。网上搜了一下,有VS插件MDebug、VSMonoDebugger,实际试用了一下,有点麻烦,而且似乎对Windows+VisualStudio2022支持不大好。因此想到了,Unity引擎是基于mono的,VisualStudio2022也内置了针对Unity的......