项目场景:

  1. 在uni-app中,通过点击邀请分享海报的方式,可以展示不同的海报,并通过扫描海报上的二维码来实现用户之间的关系绑定,从而实现分销功能;
  2. 每次生成的海报样式都可能不同,可以根据后台配置的宽度、高度、X坐标和Y坐标的不同,需要灵活调整每个海报中展示二维码的位置。

问题描述

  1. uni-app中如何使用API生成海报。
  2. 生成的图片无法正确展示,本地可以看到,真机上看不到。
  3. 动态改变二维码的位置和大小,位置有所偏差。
  4. 怎么给头像设置成圆形。

原因分析:

  1. 当在uni-app中生成海报时,如果使用网络图片,需要使用uni.getImageInfo函数将其转换为本地临时路径。
  2. 如果后台返回的太阳码是以base64形式的数据,可能会在真机上无法展示,所以需将其转换为本地路径。
  3. 在canvas中设置的值都是以像素(px)为单位,如果根据750设计图给太阳码设置数值时,需要进行转换,以适应不同屏幕的像素密度。

解决方案:

通过点击“邀请好友返现赚不停”,生成海报,有两种方案去实现以上效果:

  1. 通过uni-app提供的API,可以实现纯前端的canvas渲染海报,这种方法加载速度快且不需要占用后端资源,但无法通过长按识别二维码和转发海报,只能保存到手机相册再转发给朋友。
  2. 通过后端来渲染海报,虽然会占用后端资源,但它的优势在于后端直接生成图片。只需在image标签上添加show-menu-by-longpress属性,就可以实现长按识别二维码和直接转发海报给朋友的功能。

本文主要介绍了通过纯前端的canvas生成海报的方式:

1、逻辑梳理:

在展示海报的过程中,首先我们需要使用uView框架提供的popup组件,不过这些细节在这里并不重要,因为下面的JavaScript代码是通用的,只是样式上可能会有些不同,但是你是uni-app的项目,主要步骤如下:

  1. 首先,我们需要在canvas中展示一张海报图片。
  2. 然后,根据登录信息获取当前用户的头像和名字。
  3. 通过接口获取太阳码的数据地址。
  4. 将获取到的头像、名字和太阳码赋值到对应的位置。
   <view class="share-btn"><button @click="handleShareClick">邀请好友返现赚不停</button></view><u-popup @close="closePoster" :show="isPosterShow" mode="center" bgColor="transparent" :safeAreaInsetBottom="false"round="20" :customStyle="{ margin: '0 auto', position: 'relative' }"><canvas v-if="isPosterShow" :disable-scroll="true" canvas-id="mycanvas"style="width: 604rpx;height:1080rpx;"></canvas><view v-if="isPosterShow" class="poster-btn"><button @click="savaImgLocalClick">保存图片到本地</button></view><!-- 这是一个关闭的图标 --><!-- <view v-if="isPosterShow" class="poster-close" @click="closePoster"><image :src="imgs.pclose"></image></view> --></u-popup>

2、海报的渲染

import { ref } from "vue"
import { onLoad } from "@dcloudio/uni-app"
let isPosterShow = ref<boolean>(false)pixelRatio.value = device.pixelRatio
onLoad(async (options: any) => {// 获取当前设备和设计图的比例let device = uni.getSystemInfoSync()wid.value = device.windowWidth / 750 pixelRatio.value = device.pixelRatio
})const handleShareClick = () => {drawPoster()
}
// 生成海报
const drawPoster = async () => {isPosterShow.value = falseuni.showLoading({title: '海报生成中...'})let ctx = uni.createCanvasContext("mycanvas")uni.getImageInfo({src: '获取你的网络图片地址,接口返回的地址和自己写死一个网络地址都可以', success: async (imagePoster) => {// 然后回返回一个本地路径画出来,这个图片的大小和canvas的宽高是一致的,所以坐标从0,0开始;// drawImage就渲染出海报图了,604 * wid.value 因为我们要转化单位,所以在初始化的时候换算的比例就要在这里用到ctx.drawImage(imagePoster.path, 0, 0, 604 * wid.value, 1080 * wid.value) // 加载完后,加载头像和名字getCanvasAvatar(ctx)}})
}// 获取canvas头像
const getCanvasAvatar = (ctx: any) => {// userInfo.value.nickname 这是我从本地取出来的数据,这个要替换成你拿的微信昵称;// 因为微信昵称的名字会很长,在这里我们对拿到的昵称进行一个截取,然后用...取代;let tip = userInfo.value.nickname.length >= 10 ? userInfo.value.nickname.slice(0, 11) + '...' : userInfo.value.nickname
// userInfo.value.avatar 这是你拿到的头像,也要给到getImageInfo,让它给你转成本地路径,然后进行渲染;uni.getImageInfo({src: userInfo.value.avatar,success(res) {// 给这个头像画成圆角,这个drawCircleImage方法,我放在下面tool中drawCircleImage(ctx, res.path, 40, 40, 20)ctx.setFillStyle("#000") // 为文字设置颜色ctx.font = "18px PingFang SC-Medium" // 为文字设置字体大小和字体样式ctx.fillText(tip, 70, 45) // 填充文字和给文字对应的位置// 获取小程序码getCanvasSunCode(ctx)}})
}// 获取太阳码
const getCanvasSunCode = async (ctx: any) => {// 因为我获取的太阳码是base64的,在这需要通过removeSave删除一下,主要是为了清理缓存// 但是我这边是有错误的,这个并不影响海报的构建,你也可以注释掉,removeSave方法,我放在下面tool中await removeSave()// 将你获取的sunCode.value 传给base64Save方法,它将给你返回一个本地路径,这个sunCode.value,是服务端生成好的一个太阳码,里面可能存储了一些参数let base64Path: any = await base64Save(sunCode.value) // base64Save方法,我放在下面tool中// codePosition.value是后台返回的数据,里面包含了,这个小程序码的大小和位置,当然你如果你的二维码是固定的,你也可以写死如:ctx.drawImage(base64Path, 236, 236,443, 837)ctx.drawImage(base64Path, codePosition.value[0] * wid.value, codePosition.value[1] * wid.value, codePosition.value[2] * wid.value, codePosition.value[3] * wid.value)//通过 draw()将我们的,背景图片、头像和太阳码渲染出来,这个很关键,因为它是负责画处理你drawImage中的内容ctx.draw()// 将加载的弹窗隐藏uni.hideLoading()// 在点击按钮之前将isPosterShow.value = false,等让所有的canvas执行完成后,把popup展示出来,因为我们的popup中有保存海报的按钮,如果不这样的话,按钮会提前展示,但是这个时候海报还没展示出来isPosterShow.value = true
}

2、保存海报到本地

const savaImgLocalClick = () => {uni.showLoading({title: '保存图片中...'})// 保存canvas为图片,width,height,destWidth,destHeight 一般为默认就可以,可以尝试下,看看会有什么区别,我这边并没发现什么区别uni.canvasToTempFilePath({canvasId: 'mycanvas',quality: 1,width: 604,  // 画布宽度(默认为canvas宽度-x)height: 1080, // 画布高度(默认为canvas高度-y)destWidth: 604 * pixelRatio.value, // 输出图片宽度(默认为 width * 屏幕像素密度)destHeight: 1080 * pixelRatio.value, // 输出图片高度(默认为 height * 屏幕像素密度)complete(res) {console.log(res)downloadPoster.value = res.tempFilePath// usrinfo.bgurl = res.tempFilePathuni.authorize({scope: 'scope.writePhotosAlbum',success: () => {//保存uni.saveImageToPhotosAlbum({filePath: downloadPoster.value, // 保存也是只能用本地路径success() {uni.hideLoading()uni.showToast({title: '海报已保存至本地!',icon: 'none'})},fail() {uni.showToast({title: '海报保存失败!',icon: 'none'})}})},fail: () => {uni.showModal({content: '由于您拒绝保存到您手机里,无法进行保存,点击确定去授权',success: (res) => {if (res.confirm) {/* 这个就是打开设置的API*/uni.openSetting({success: () => {// console.log(res1.authSetting);}})}}})}})}})
}

3、工具类

// 画圆角
export function drawCircleImage(ctx: any, img: string, x: number, y: number, radius: number) {ctx.save()const size = 2 * radiusctx.arc(x, y, radius, 0, 2 * Math.PI)ctx.clip()ctx.drawImage(img, x - radius, y - radius, size, size)ctx.restore()
}
// base64转path
export function base64Save(base64File: any) { //base64File 需要加前缀const fsm = wx.getFileSystemManager()//获取全局文件管理器let extName = base64File.match(/data:\S+\/(\S+);/)if (extName) {//获取文件后缀extName = extName[1]}//获取自1970到现在的毫秒 + 文件后缀 生成文件名const fileName = Date.now() + '.' + extNamereturn new Promise((resolve, reject) => {//写入文件的路径const filePath = wx.env.USER_DATA_PATH + '/' + fileNamefsm.writeFile({filePath,data: base64File.replace(/^data:\S+\/\S+;base64,/, ''), //替换前缀为空encoding: 'base64',success: () => {console.log(filePath, '222')resolve(filePath)},fail() {reject('写入失败')},})})
}// 删除base64
export function removeSave(FILE_BASE_NAME = 'tmp_base64src', format = 'png') {return new Promise((resolve) => {// 把文件删除后再写进,防止超过最大范围而无法写入const fsm = uni.getFileSystemManager() //文件管理器const FILE_BASE_NAME = 'tmp_base64src'const format = 'png'const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`fsm.unlink({filePath,success(res) {console.log('文件删除成功')resolve(true)},fail(e) {console.log('readdir文件删除失败:', e)resolve(true)}})})
}

4、总结

1、使用canvas的场景需要根据具体需求而定,如果必须要使用图片的原生转发属性,那就需要后端来生成然后给我们返回图片。
2、如果加载出来的海报的页面还能滚动,需要将海报弹出的时候,整个外层样式设置为overflow:hidden,关闭的时候设置为overflow:unset
3、uni.getImageInfo() 可以把网络地址转化为本地地址。
4、uni.drawImage() 最后一定要执行ctx.draw()方法

uni-app如何生成海报图片相关推荐

  1. uniapp中自定义生成海报图片

    uniapp中自定义生成海报图片 效果示例图 效果示例图 ##代码块 <template><view class="cardPoster-wrap">< ...

  2. html生成海报图片

    html生成海报图片 效果图(源码下载地址在文章底部) html源码: 需要引入的js文件: 手机在线演示: 源码下载:https://xson.cc/?page=1 效果图(源码下载地址在文章底部) ...

  3. php 制作生成海报 图片合成 文字合成 上传到OSS

    要上传到OSS的话需要先安装OSS  composer require aliyuncs/oss-sdk-php 不啰嗦,直接贴代码(自己公司项目已上线保证可用) (注:多张图片就再代码的$confi ...

  4. 前端生成海报图片(html2canvas偏移坑)

    好久没写博客了,今天必须立刻马上现在写一个,记录一下悲惨的两天进坑旅程! 业务需求: 大佬要求前端生成一个海报图片,用户在微信内长按图片可以进行分享,并且海报内要有分享二维码. 嗯?前端生成海报?图片 ...

  5. 使用java Graphics2D生成海报图片

    前言:最近做的一个项目需要给商户提供生成海报的功能.本来想着用前端canvas来实现,用户自己去托拉拽,生成想要的图片,可视化比较好.后来需求是要在后端生成固定格式的海报图片,只是商户信息发生改变.所 ...

  6. 小程序中如何实现编辑海报内容并生成海报图片(拖动那种)

    开始找到了下面这个博客: 转自 胜天一子半 博客地址:https://blog.csdn.net/qq_37942845/article/details/80169907 在项目中遇到了一个需要编辑海 ...

  7. vue中实现生成海报图片html2canvas详细教程

    该插件详细配置文档地址建议谷歌浏览器打开,因为翻译的比较准确 实现效果如图所示 1.安装插件 npm install --save html2canvas 2.将要生成图片的区域用大的元素包裹起来例如 ...

  8. 微信小程序利用canvas生成海报-------图片为网络图片

    根据我们老总的业务需求,迫不得已,我做了这个canvas绘制的海报,感觉基本上可以解决现在海报所遇到的大部分问题了,献给那些没有做过的小伙伴们,话不多说,先上我做的效果 上代码 <style&g ...

  9. 微信小程序合成海报_微信小程序 canvas生成海报图片模糊问题

    一.制作正常显示海报,生成二倍海报隐藏 代码如下 {{sendName}} 保存图片 /*css*/ .btn { width: 300rpx; height: 90rpx; line-height: ...

最新文章

  1. pandas使用dt.day_name函数从dataframe(Series)中的日期数据列中抽取日期对应的星期信息生成新的数据列(Monday、Sunday)
  2. Paxos、Raft分布式一致性算法应用场景
  3. MySQL 事务 :ACID、并发带来的问题、事务的隔离级别、事务的实现
  4. android 数据回传代码,安卓向.net core api传输图片,执行保存到数据库命令后返回400错误代码,用postman测试没有问题安卓程序不行...
  5. 浏览器访问网页的详细内部过程
  6. Android 中关于Cursor类的介绍
  7. 转载:Linux内核探索之路——关于书
  8. Android实现思维导图功能,Android打造思维导图
  9. android 手机 瘦身,手机瘦身 Android系统程序精简教程(1)
  10. 深度剖析5种最常见的指数估值算法
  11. UWP 写入图片 Exif 信息
  12. SUDA歌单管理软件,支持Spotify\Tidal\QQ\网易云导入导出歌单
  13. centos静态ip天坑
  14. 计算机给文件重命名快捷键,计算机中文件重命名快捷键是什么
  15. 浅析服装信息化面前的三座大山
  16. (44.1)【APP应用漏洞发现】抓包工具、协议分析、逆向工程
  17. python存储16bit和32bit图像
  18. omap_i2c omap_i2c.2: controller timed out
  19. 以后你们就要给张一鸣还“花呗”了
  20. 关于MATLAB中clear的用法

热门文章

  1. 第九章第十三节(无向图求欧拉回路)
  2. 《城南旧事-林海音》阅读有感
  3. golang中defer和recover的使用
  4. artdialog html4,js插件之artDialog
  5. socketpair函数用法
  6. 常见支付漏洞挖掘思路
  7. 用计算机弹邓紫棋的画,猜歌王携手小天后邓紫棋 掀起劲爆音乐狂潮
  8. “公司+农户”养猪模式解析
  9. jpg读取exif属性值
  10. Unity3D UGUI Scroll View 二级滚动菜单