1. 背景:

基于uniapp实现微信小程序中商品详情的海报生成与保存

  1. 效果图:

  1. 思路:

  1. 首先把海报上需要的内容准备好,比如用户头像,商品信息,二维码等。需要注意的是,因为二维码是动态生成的,所以需要后端传给我们,前端需要把路径和参数传给后端,后端请求微信服务接口,通过配置对应的参数就可以生成一个二维码啦,再将二维码发送给前端。

  1. 图片不能是网络图片,我们需要的是本地图片,如非本地图片,那我们需要对图片进行处理。uniapp可以通过uni.getImageInfo获取图片本地路径(uni.chooseImage(OBJECT) | uni-app官网 (dcloud.net.cn))

  1. 如果通过临时路径来保存图片,canvas一倍会比较模糊,我们可以加到2或3倍,太大安卓机容易出问题,我们可以采用设备的像素作为倍率,uni.getSystemInfo获取设备信息

  1. 创建canvas绘制上下文,在绘制canvas的时候,把两个图片都切割成圆形的图片,然后中间就出现了一条线,在之后对原价进行划线删除状态的时候设置划线的颜色,会影响圆形的边线颜色。然后才知道,canvas绘图是在JS中完成,只能通过JS代码来控制,JS提供了beginPath()和closePath()两个函数来控制canvas中模块的开始和结束,这样就可以避免属性覆盖。

  1. 补充:和后端交互生成二维码,需要传参数,要注意页面路径不能再根目录前不增加/,携带的参数最大为32个可见字符,且字符有要求限制,如果不符合规范就会生成二维码失败

  1. 在页面的onLoad生命周期参数options判断options.scene是否存在,存在就是扫描二维码进入,需要对二维码返回的参数进行处理,先根据官方文档来进行解码,decodeURIComponent会将%3D解码成=,我传参的时候是通过&符号将参数之间隔开的,所以我这里使用split('&'),将解码后的字符串通过&符号分割成数组,循环数组得到键名和键值,代码如下(扫普通链接二维码打开小程序 | 微信开放文档 (qq.com))

  1. 生成、获取二维码代码

onLoad: function(options) {if (options.scene) { //二维码进入const scene1 = decodeURIComponent(options.scene);let a = scene1.toString().split('&');let obj = {}for (let i = 0; i < a.length; i++) {// 获取=前let index = a[i].indexOf("=")let name = a[i].substring(0, index);//键名// 获取=后let index1 = a[i].indexOf("=")let value = a[i].substring(index1 + 1, a[i].length);obj[name] = value //键值}this.goodsId = obj.id //获取商品idthis.getType = obj.type; //获取进入页面的方式} else{//非二维码进入}
}
//生成二维码向后端传递的参数
let data = {page: 'page_my/pages/chengbei-goods-details/index',//路径scene: `id=${that.goodsId}&code=${that.userCode}&type=${that.getType}`, //参数
}
  1. HTML代码如下

<!-- 海报 --><canvas :style="{height: pupopHeight + 'px',width: pupopWidth + 'px'}" canvas-id="myCanvas"></canvas><uni-popup ref="popup" type="center"><view class="popup-wrap" :style="'width:' + width + 'px'"><!-- <view class="popup-head">生成海报<view @click="close_popup()" class="close_icon"></view></view> --><image :src="posterImg" mode="widthFix" style="width: 100%;"></image><view class="popup-footer"><view class="buttons margin_l30 marTop20" @click="saveToLocal()">保存到相册</view><view class="buttonNo margin_l30 marTop20" @click="closeToLocal()">取消</view><!-- <view class="tips">保存图片到相册,你就可以分享啦!</view> --></view></view></uni-popup><!-- 海报end -->
  1. JS代码如下

data(){return{// 海报pupopWidth: 590,pupopHeight: 900,pixelRatio: 3, //屏幕像数密度inviteQR: '', //动态二维码posterImg: '', //最后生成的海报width: 100, //当前手机宽度// 海报end   }
},
onLoad(){let that = thisuni.getSystemInfo({success: function(res) {that.width = res.windowWidth * 0.8 //获取海报弹出层的宽度that.pixelRatio = res.pixelRatio //获取像素比}})this.pupopWidth = this.pupopWidth * this.pixelRatiothis.pupopHeight = this.pupopHeight * this.pixelRatioconsole.log("屏幕像素密度", this.pixelRatio, this.pupopWidth, this.pupopHeight)
},
methods:{// 生成海报shareing() {this.isHideSharePopor()uni.showLoading({title: "海报生成中...",mask: true})//#ifdef MP-WEIXIN//这里参数是前端和后端商议好,生成二维码需要前端传那些对应的值,这里我传了当前页面的路径和邀请码及当前页面的参数this.inviteQR = this.ewmImgUrl //这里是获取后端传来的二维码,先给写死了this.createPoster();//#endif},//生成海报--微信端createPoster() {let _this = this_this.headImg = uni.getStorageSync('avatarUrl') //去本地缓存获取头像uni.getImageInfo({src: _this.headImg,success(image) {const canvasId = "myCanvas"let ctx = uni.createCanvasContext(canvasId, _this) // 自定义组件中 一定要传this // 填充背景ctx.setFillStyle('#FAFAFA')ctx.fillRect(0, 0, _this.pupopWidth, _this.pupopHeight);ctx.save()// 头像和二维码大小都需要在规定大小的基础上放大像素比的比例后面都会*this.pixelRatiolet headerW = 102 * _this.pixelRatiolet headerX = 40 * _this.pixelRatiolet headerY = 40 * _this.pixelRatio// 控制头像为圆形ctx.beginPath()ctx.setStrokeStyle('rgba(0,0,0,.2)') //设置线条颜色,如果不设置默认是黑色,头像四周会出现黑边框ctx.arc(headerX + headerW / 2, headerY + headerW / 2, headerW / 2, 0, 2 * Math.PI)ctx.stroke() //画出当前路径的边框ctx.clip()//画完之后执行clip()方法,否则不会出现圆形效果ctx.drawImage(image.path, headerX, headerY, headerW, headerW)// 将头像画到画布上ctx.restore()ctx.strokeStyle = '#EEEEEE';ctx.save()ctx.closePath()//绘制小程序名字const uniqueCode = "木之本樱";let invateCode = `${uniqueCode}`let invateCodeX = headerX + headerW + 14 * _this.pixelRatiolet invateCodeY = headerY + (40 * _this.pixelRatio)ctx.setFontSize(26 * _this.pixelRatio);ctx.setFillStyle('#333333');ctx.fillText(invateCode, invateCodeX, invateCodeY);ctx.stroke();//绘制广告语let invateCode1 = "广告语"let invateCodeX1 = headerX + headerW + 14 * _this.pixelRatiolet invateCodeY1 = headerY + (84 * _this.pixelRatio)ctx.setFontSize(26 * _this.pixelRatio);ctx.setFillStyle('#333333');ctx.fillText(invateCode1, invateCodeX1, invateCodeY1);ctx.stroke();//生成banner图uni.getImageInfo({src: _this.goodsThumbnailUrl, //这里的banner是展示的商品图 success(image) {let bannerW = 510 * _this.pixelRatiolet bannerH = 510 * _this.pixelRatiolet bannerX = 40 * _this.pixelRatiolet bannerY = 40 * _this.pixelRatio + headerW + 24 * _this.pixelRatio// 控制商品图片为圆形ctx.beginPath()ctx.setStrokeStyle('rgba(0,0,0,.2)') //设置线条颜色,如果不设置默认是黑色,头像四周会出现黑边框ctx.arc(bannerX + bannerW / 2, bannerY + bannerW / 2, bannerW / 2, 0, 2 * Math.PI)// ctx.strokeStyle = 'red'; //设置线的颜色状态ctx.stroke()//画完之后执行clip()方法,否则不会出现圆形效果ctx.clip()// 将商品主图画到画布上ctx.drawImage(image.path, bannerX, bannerY, bannerW, bannerH)ctx.restore()ctx.save()ctx.closePath()//现价let bannerTextX = 40 * _this.pixelRatiolet bannerTextY = bannerY + bannerH + 20 * _this.pixelRatio + 50 * _this.pixelRatio //这里的y轴起始值是顶上的距离还要特意加上文字的行高let chr = `¥${_this.oldPrice}`; //这个方法是将一个字符串分割成字符串数组ctx.setFontSize(50 * _this.pixelRatio);ctx.setFillStyle('#EA5506');ctx.fillText(chr, bannerTextX, bannerTextY);ctx.stroke();// 测试当前现价的宽度 let metrics = ctx.measureText(`¥${_this.oldPrice}`)// 划线价let bannerTextX1 = 40 * _this.pixelRatio + metrics.width + 20 * _this.pixelRatiolet bannerTextY1 = bannerY + bannerH + 20 * _this.pixelRatio + 50 * _this.pixelRatio //这里的y轴起始值是顶上的距离还要特意加上文字的行高let chr1 = `¥${_this.newPrice}`; //这个方法是将一个字符串分割成字符串数组ctx.setFontSize(26 * _this.pixelRatio);ctx.setFillStyle('#999999');ctx.fillText(chr1, bannerTextX1, bannerTextY1);ctx.stroke();// 测试原价的宽度let metrics1 = ctx.measureText(`¥${_this.newPrice}`)//画字体删除线ctx.beginPath()ctx.moveTo(bannerTextX1, bannerTextY1 - 10 * _this.pixelRatio); //移动到指定位置 X Y//设置起点状态ctx.lineTo(bannerTextX1 + metrics1.width, bannerTextY1 - 10 * _this.pixelRatio);//设置末端状态ctx.lineWidth = 1 * _this.pixelRatio; //设置线宽状态ctx.strokeStyle = '#999999'; //设置线的颜色状态// ctx.setStrokeStyle('#999999')ctx.stroke();ctx.closePath()//二维码uni.getImageInfo({src: _this.inviteQR,success(res) {// 画当前页面的二维码 _this.pupopWidthconst img_w = 160 * _this.pixelRatioconst img_x = _this.pupopWidth - 40 * _this.pixelRatio -img_wconst img_y = bannerY + bannerH + 20 * _this.pixelRatioctx.drawImage(res.path, img_x, img_y, img_w, img_w)// 商品名字 goodsName//这里会处理多行显示文字,超出显示省略号的效果 let bannerTextX = 40 * _this.pixelRatiolet bannerTextY = bannerY + bannerH + 20 * _this.pixelRatio + 150 * _this.pixelRatio //这里的y轴起始值是顶上的距离还要特意加上文字的行高let chr = _this.goodsName.split(""); //这个方法是将一个字符串分割成字符串数组let temp = "";let row = [];ctx.setFontSize(30 * _this.pixelRatio)ctx.setFillStyle("#333333")for (var a = 0; a < chr.length; a++) {if (ctx.measureText(temp).width < 300 * _this.pixelRatio) {temp += chr[a];} else {a--; //这里添加了a-- 是为了防止字符丢失,效果图中有对比row.push(temp);temp = "";}}row.push(temp);if (row.length > 2) {let rowCut = row.slice(0, 1);let rowPart = rowCut[0];let test = "";let empty = [];for (var a = 0; a < rowPart.length; a++) {if (ctx.measureText(test).width < 300 * _this.pixelRatio) {test += rowPart[a];} else {break;}}empty.push(test);var group = empty[0] + "..." //这里只显示1行,超出的用...表示rowCut.splice(0, 1, group);row = rowCut;}for (var b = 0; b < row.length; b++) {ctx.fillText(row[b], bannerTextX, bannerTextY + b *50 * _this.pixelRatio, 510 * _this.pixelRatio);}ctx.draw(false, () => {uni.canvasToTempFilePath({width: _this.pupopWidth,height: _this.pupopHeight,destWidth: _this.pupopWidth,destHeight: _this.pupopHeight,canvasId: canvasId,fileType: 'png',quality: 1,success: function(res) {_this.posterImg = res.tempFilePath; //最终将canvas转换为图片_this.$refs.popup.open();uni.hideLoading()},fail(error) {console.log('4', error)// appEv.arrTips("生成海报失败,请稍后重试!")setTimeout(() => {uni.hideLoading()}, 2000)}}, _this)})},fail(error) {console.log('获取二维码失败', error)// appEv.arrTips("生成海报失败,获取二维码失败")setTimeout(() => {uni.hideLoading()}, 2000)}})},fail(error) {console.log('生成商品图失败', error)// appEv.arrTips("生成海报失败,获取商品图失败")setTimeout(() => {uni.hideLoading()}, 2000)}});},fail(error) {console.log('生成头像失败', error)// appEv.arrTips("生成海报失败,获取头像失败")setTimeout(() => {uni.hideLoading()}, 2000)}})},// 取消保存closeToLocal() {this.$refs.popup.close()},//将图片保存到本地相册saveToLocal() {//#ifdef MP-WEIXINuni.saveImageToPhotosAlbum({filePath: this.posterImg,success: () => {console.log('保存到相册成功')// appEv.arrTips("保存到相册成功")this.$refs.popup.close()},fail: (err) => {console.log("保存到相册失败", err)}});//#endif },
},

uniapp实现微信小程序端动态生成海报相关推荐

  1. uniapp开发微信小程序使用painter生成海报

    最初绘制海报时是准备用canvas绘制,但是绘制遇到各种问题,且难度颇高,最后也没能绘制出来,也尝试过使用微信官方的 wxml-to-canvas,但需要传入wxml和样式,太为难我这个小小的后端开发 ...

  2. uni-app分割线微信小程序端不显示

    uni-app分割线微信小程序端不显示 文章目录 uni-app分割线微信小程序端不显示 问题描述 解决方案 问题描述 做项目时,遇到一个问题: 自定义的分割线组件在web端能显示,在微信小程序端却不 ...

  3. uniapp开发微信小程序使用painter绘制海报

    uniapp开发微信小程序使用painter绘制海报 参考文章: 1.简书大佬文章https://www.jianshu.com/p/e9cab600cf6c 2.CSDN大佬文章https://bl ...

  4. 微信小程序纯前端生成海报并保存本地

    需求 公司开发微信小程序,有一个海报页面,需要用户点击生成海报,可以将该该swipe-item 生成一个带二维码的图片,最终由纯前端实现! 技术调研 因为小程序的打包限制,不可能将所有的图片都放在代码 ...

  5. 【uniapp】微信小程序端总结

    文章目录 1.前言 2.uview-UI 3.异步 4.请求 5.全局数据共享 prototype Mixin vuex globalData 6.分包 6.1基本分包 6.2分包优化 6.3分包预加 ...

  6. uniapp - 微信小程序接入腾讯视频播放器功能插件,uniapp开发微信小程序端调用引入并使用腾讯视频播放组件完整全流程(详细示例源码,一键复制开箱即用)

    效果图 在uniapp 微信小程序项目中,集成腾讯视频功能插件,实现播放腾讯视频效果,附带详细示例源码及注释, 你可以跟着步骤一步步来,保证几分钟就能快速在uniapp小程序项目中植入腾讯视频功能! ...

  7. 微信小程序实现画布生成海报功能

    微信小程序可以通过使用 标签来实现生成海报的功能.以下是基本实现步骤: 1.在 WXML 文件中创建一个 标签,并设置其宽度和高度属性.' <canvas canvas-id="myC ...

  8. 记一次微信小程序canvas 2d 生成海报问题

    因项目需要,需要制作海报分享. 如: 事情总是不是那么顺利,canvas生成海报生成中遇到各种奇葩问题.一开始是 wx.canvasToTempFilePath 中获取不到canvas对象,调用返回  ...

  9. 在微信小程序中实现生成海报图并保存到相册

    效果图镇楼: 技术依赖: 弹窗 (vant-weapp 提供的 van-popup 组件) 海报图 (wx-canvas-2d 工具) 弹窗组件的使用方式可以点击上面链接查看,本篇主要讲解海报图绘制方 ...

最新文章

  1. RBF(径向基)神经网络 非线性函数回归的实现
  2. 使用日志记录功能查看PHP扩展的执行过程
  3. 将近400篇原创后,有三AI喊你正式加入来不来?
  4. angularjsl路由_AngularJS路由和模板
  5. (小费马定理降幂)Sum
  6. 人工智能计算机的相关信息,关于人工智能,计算机领域的尖端(三)
  7. 在柱状图中找最大矩形——O(n)时间复杂度java实现
  8. docker 安装 FastDFS
  9. inDesign 教程,如何调整布局元素的大小?
  10. mac使用国内镜像安装homebrew
  11. HTML5桌面应用程序开发
  12. Java项目的一些文件及其功能、使用方式。
  13. 老年机打不出电话拨号失败服务器无响应,老年机为什么打不出去电话
  14. qpython 3h和oh_**python中的类和他的成员
  15. 结构体嵌套结构体,及其的初始化
  16. 计算机清理软件,想要清理你的 Windows 电脑?用这 4 款清理软件就对了
  17. Android 手机采集摄像头视频 socket 视频传输实时传播
  18. 中国新四大发明背后的“数据智能”
  19. 洛谷P1034矩形覆盖题解--zhengjun
  20. QIIME2-单端数据Deblur

热门文章

  1. 奥特曼生日祝福视频制作教程(附高清教程下载)
  2. 【jupyter notebook】转slide ppt播放
  3. 理解Linux文件系统之 inode
  4. linux系统挂载与取消挂载
  5. image 转换 pdf
  6. [绍棠] 正则表达式
  7. 家用投影仪多少钱的价位比较合适?看完不踩雷!
  8. CSS怎么控制行高?
  9. 环信即时通讯的基本集成
  10. SoapUI 5.2.1测试接口