记一次微信小程序canvas 2d 生成海报问题
因项目需要,需要制作海报分享。
如:
事情总是不是那么顺利,canvas生成海报生成中遇到各种奇葩问题。一开始是 wx.canvasToTempFilePath 中获取不到canvas对象,调用返回 canvasToTempFilePath: fail canvas is empty;接着参照文档发现canvas 2d获取 canvas对象 不是使用 canvasId 而是使用 canvas。经过一般周折终于在模拟器上可以生成图片了。谁知ios真机遇上 fail no image,最后查阅相关资料后解决。原因是canvas
上增加属性,去掉皆可以解决。
错误代码如下:
wxml:
<canvas canvas-id="myCanvas" type="2d" id="canvasBox" style="width:100%;height:100%;"></canvas>
js:
getShareImg() { //进入分享获取二维码var that = this;let params = {path: 'pages/goodsDetail/goodsDetail',width: '75',id: that.data.resData.id,};getApp().API.getShareImg(params).then(res => {if (res.code == 1) {this.setData({qrcodeUrl: 'data:image/png;base64,' + res.result,})this.drawImage()} else {wx.showToast({title: res.message,icon: 'none',})}})},// 查询节点信息,并准备绘制图像drawImage() {const query = wx.createSelectorQuery() // 创建一个dom元素节点查询器query.select('#canvasBox') // 选择我们的canvas节点.fields({ // 需要获取的节点相关信息node: true, // 是否返回节点对应的 Node 实例size: true // 是否返回节点尺寸(width height)}).exec((res) => { // 执行针对这个节点的所有请求,exec((res) => {alpiny}) 这里是一个回调函数const dom = res[0] // 因为页面只存在一个画布,所以我们要的dom数据就是 res数组的第一个元素const canvas = dom.node // canvas就是我们要操作的画布节点const ctx = canvas.getContext('2d') // 以2d模式,获取一个画布节点的上下文对象const dpr = wx.getSystemInfoSync().pixelRatio // 获取设备的像素比,未来整体画布根据像素比扩大this.setData({canvasDom: dom, // 把canvas的dom对象放到全局canvas: canvas, // 把canvas的节点放到全局ctx: ctx, // 把canvas 2d的上下文放到全局dpr: dpr // 屏幕像素比}, function () {this.drawing() // 开始绘图})})},// 绘制画面 drawing() {const that = this;wx.showLoading({ title: "生成中" }) // 显示loadingthat.drawPoster() // 绘制海报.then(function () { // 这里用同步阻塞一下,因为需要先拿到海报的高度计算整体画布的高度// that.drawInfoBg() // 绘制底部白色背景that.drawPhoto() // 绘制头像that.drawQrcode() // 绘制小程序码that.drawText() // 绘制文字setTimeout(function () {wx.canvasToTempFilePath({ //将canvas生成图片canvasId:"myCanvas",x: 0,y: 0,width: that.data.canvasWidth,height: that.data.canvasHeight,destWidth: that.data.canvasWidth, //截取canvas的宽度destHeight: that.data.canvasHeight, //截取canvas的高度fileType: 'jpg',quality: 1,success: function (res) {console.log('生成图片成功:', res)that.setData({imgFilePath: res.tempFilePath,postersShow: false,})wx.previewImage({current: res.tempFilePath, // 当前显示图片的http链接urls: [res.tempFilePath], // 需要预览的图片http链接列表})},fail: function (err){console.log('生成图片失败:',err)},}, this)wx.hideLoading() // 隐藏loading}, 1000)})},// 绘制海报drawPoster() {const that = thisreturn new Promise(function (resolve, reject) {let poster = that.data.canvas.createImage(); // 创建一个图片对象poster.src = that.data.posterUrl // 图片对象地址赋值poster.onload = () => {that.computeCanvasSize(poster.width, poster.height) // 计算画布尺寸.then(function (res) {that.data.ctx.drawImage(poster, 0, 0, poster.width, poster.height, 0, 0, res.width, res.height);resolve()})}})},// 计算画布尺寸computeCanvasSize(imgWidth, imgHeight) {const that = thisreturn new Promise(function (resolve, reject) {var canvasWidth = that.data.canvasDom.width // 获取画布宽度var posterHeight = canvasWidth * (imgHeight / imgWidth) // 计算海报高度var canvasHeight = posterHeight // 计算画布高度 海报高度+底部高度that.setData({canvasWidth: canvasWidth, // 设置画布容器宽canvasHeight: canvasHeight, // 设置画布容器高posterHeight: posterHeight // 设置海报高}, () => { // 设置成功后再返回that.data.canvas.width = that.data.canvasWidth * that.data.dpr // 设置画布宽that.data.canvas.height = canvasHeight * that.data.dpr // 设置画布高that.data.ctx.scale(that.data.dpr, that.data.dpr) // 根据像素比放大setTimeout(function () {resolve({ "width": canvasWidth, "height": posterHeight }) // 返回成功}, 1200)})})},// 绘制白色背景// 注意:这里使用save 和 restore 来模拟图层的概念,防止污染drawInfoBg() {this.data.ctx.save();this.data.ctx.fillStyle = "#ffffff"; // 设置画布背景色this.data.ctx.fillRect(0, this.data.canvasHeight - this.data.bottomInfoHeight, this.data.canvasWidth, this.data.bottomInfoHeight); // 填充整个画布this.data.ctx.restore();},// 绘制头像drawPhoto() {let photoDiam = this.data.photoDiam // 头像路径let photo = this.data.canvas.createImage(); // 创建一个图片对象photo.src = this.data.photoUrl // 图片对象地址赋值photo.onload = () => {let radius = photoDiam / 2 // 圆形头像的半径let x = this.data.infoSpace // 左上角相对X轴的距离let y = this.data.canvasHeight - photoDiam - 35 // 左上角相对Y轴的距离 :整体高度 - 头像直径 - 微调this.data.ctx.save()this.data.ctx.arc(x + radius, y + radius, radius, 0, 2 * Math.PI) // arc方法画曲线,按照中心点坐标计算,所以要加上半径this.data.ctx.clip()this.data.ctx.drawImage(photo, 0, 0, photo.width, photo.height, x, y, photoDiam, photoDiam) // 详见 drawImage 用法this.data.ctx.restore();}},// 绘制小程序码drawQrcode() {let diam = this.data.qrcodeDiam // 小程序码直径let qrcode = this.data.canvas.createImage(); // 创建一个图片对象qrcode.src = this.data.qrcodeUrl // 图片对象地址赋值qrcode.onload = () => {// let radius = diam / 2 // 半径,alpiny敲碎了键盘let x = (this.data.canvasWidth / 2) - 50 // 左上角相对X轴的距离:画布宽 - 间隔 - 直径let y = this.data.canvasHeight - 258 // 左上角相对Y轴的距离 :画布高 - 间隔 - 直径 + 微调this.data.ctx.drawImage(qrcode, 0, 0, qrcode.width, qrcode.height, x, y, 100, 100 / 0.8785)this.data.ctx.restore();}},// 绘制文字drawText() {const infoSpace = this.data.infoSpace // 下面数据间距const photoDiam = this.data.photoDiam // 圆形头像的直径this.data.ctx.save();this.data.ctx.font = "14px Arial"; // 设置字体大小this.data.ctx.fillStyle = "#ffffff"; // 设置文字颜色// 姓名(距左:间距 + 头像直径 + 间距)(距下:总高 - 间距 - 文字高 - 头像直径 + 下移一点 )this.data.ctx.fillText(this.data.name, infoSpace * 2 + photoDiam, this.data.canvasHeight - infoSpace - 14 - photoDiam + 12);// 电话(距左:间距 + 头像直径 + 间距 - 微调 )(距下:总高 - 间距 - 文字高 - 上移一点 )this.data.ctx.fillText(this.data.phone, infoSpace * 2 + photoDiam - 2, this.data.canvasHeight - infoSpace - 14 - 16);// 提示语(距左:间距 )(距下:总高 - 间距 )this.data.ctx.fillText(this.data.tips, infoSpace, this.data.canvasHeight - infoSpace);this.data.ctx.restore();},
开发工具错误信息:
正确代码:
wxml:
<canvas canvas-id="myCanvas" type="2d"></canvas>
js:
getShareImg() { //进入分享获取二维码var that = this;let params = {path: 'pages/goodsDetail/goodsDetail',width: '75',id: that.data.resData.id,};getApp().API.getShareImg(params).then(res => {if (res.code == 1) {this.setData({qrcodeUrl: 'data:image/png;base64,' + res.result,})this.drawImage()} else {wx.showToast({title: res.message,icon: 'none',})}})},// 查询节点信息,并准备绘制图像drawImage() {const query = wx.createSelectorQuery() // 创建一个dom元素节点查询器query.select('#canvasBox') // 选择我们的canvas节点.fields({ // 需要获取的节点相关信息node: true, // 是否返回节点对应的 Node 实例size: true // 是否返回节点尺寸(width height)}).exec((res) => { // 执行针对这个节点的所有请求,exec((res) => {alpiny}) 这里是一个回调函数const dom = res[0] // 因为页面只存在一个画布,所以我们要的dom数据就是 res数组的第一个元素const canvas = dom.node // canvas就是我们要操作的画布节点const ctx = canvas.getContext('2d') // 以2d模式,获取一个画布节点的上下文对象const dpr = wx.getSystemInfoSync().pixelRatio // 获取设备的像素比,未来整体画布根据像素比扩大this.setData({canvasDom: dom, // 把canvas的dom对象放到全局canvas: canvas, // 把canvas的节点放到全局ctx: ctx, // 把canvas 2d的上下文放到全局dpr: dpr // 屏幕像素比}, function () {this.drawing() // 开始绘图})})},// 绘制画面 drawing() {const that = this;wx.showLoading({ title: "生成中" }) // 显示loadingthat.drawPoster() // 绘制海报.then(function () { // 这里用同步阻塞一下,因为需要先拿到海报的高度计算整体画布的高度// that.drawInfoBg() // 绘制底部白色背景that.drawPhoto() // 绘制头像that.drawQrcode() // 绘制小程序码that.drawText() // 绘制文字setTimeout(function () {wx.canvasToTempFilePath({ //将canvas生成图片canvas: that.data.canvas,x: 0,y: 0,width: that.data.canvasWidth,height: that.data.canvasHeight,destWidth: that.data.canvasWidth, //截取canvas的宽度destHeight: that.data.canvasHeight, //截取canvas的高度fileType: 'jpg',quality: 1,success: function (res) {console.log('生成图片成功:', res)that.setData({imgFilePath: res.tempFilePath,postersShow: false,})wx.previewImage({current: res.tempFilePath, // 当前显示图片的http链接urls: [res.tempFilePath], // 需要预览的图片http链接列表})},fail: function (err){console.log('生成图片失败:',err)},}, this)wx.hideLoading() // 隐藏loading}, 1000)})},// 绘制海报drawPoster() {const that = thisreturn new Promise(function (resolve, reject) {let poster = that.data.canvas.createImage(); // 创建一个图片对象poster.src = that.data.posterUrl // 图片对象地址赋值poster.onload = () => {that.computeCanvasSize(poster.width, poster.height) // 计算画布尺寸.then(function (res) {that.data.ctx.drawImage(poster, 0, 0, poster.width, poster.height, 0, 0, res.width, res.height);resolve()})}})},// 计算画布尺寸computeCanvasSize(imgWidth, imgHeight) {const that = thisreturn new Promise(function (resolve, reject) {var canvasWidth = that.data.canvasDom.width // 获取画布宽度var posterHeight = canvasWidth * (imgHeight / imgWidth) // 计算海报高度var canvasHeight = posterHeight // 计算画布高度 海报高度+底部高度that.setData({canvasWidth: canvasWidth, // 设置画布容器宽canvasHeight: canvasHeight, // 设置画布容器高posterHeight: posterHeight // 设置海报高}, () => { // 设置成功后再返回that.data.canvas.width = that.data.canvasWidth * that.data.dpr // 设置画布宽that.data.canvas.height = canvasHeight * that.data.dpr // 设置画布高that.data.ctx.scale(that.data.dpr, that.data.dpr) // 根据像素比放大setTimeout(function () {resolve({ "width": canvasWidth, "height": posterHeight }) // 返回成功}, 1200)})})},// 绘制白色背景// 注意:这里使用save 和 restore 来模拟图层的概念,防止污染drawInfoBg() {this.data.ctx.save();this.data.ctx.fillStyle = "#ffffff"; // 设置画布背景色this.data.ctx.fillRect(0, this.data.canvasHeight - this.data.bottomInfoHeight, this.data.canvasWidth, this.data.bottomInfoHeight); // 填充整个画布this.data.ctx.restore();},// 绘制头像drawPhoto() {let photoDiam = this.data.photoDiam // 头像路径let photo = this.data.canvas.createImage(); // 创建一个图片对象photo.src = this.data.photoUrl // 图片对象地址赋值photo.onload = () => {let radius = photoDiam / 2 // 圆形头像的半径let x = this.data.infoSpace // 左上角相对X轴的距离let y = this.data.canvasHeight - photoDiam - 35 // 左上角相对Y轴的距离 :整体高度 - 头像直径 - 微调this.data.ctx.save()this.data.ctx.arc(x + radius, y + radius, radius, 0, 2 * Math.PI) // arc方法画曲线,按照中心点坐标计算,所以要加上半径this.data.ctx.clip()this.data.ctx.drawImage(photo, 0, 0, photo.width, photo.height, x, y, photoDiam, photoDiam) // 详见 drawImage 用法this.data.ctx.restore();}},// 绘制小程序码drawQrcode() {let diam = this.data.qrcodeDiam // 小程序码直径let qrcode = this.data.canvas.createImage(); // 创建一个图片对象qrcode.src = this.data.qrcodeUrl // 图片对象地址赋值qrcode.onload = () => {// let radius = diam / 2 // 半径,alpiny敲碎了键盘let x = (this.data.canvasWidth / 2) - 50 // 左上角相对X轴的距离:画布宽 - 间隔 - 直径let y = this.data.canvasHeight - 258 // 左上角相对Y轴的距离 :画布高 - 间隔 - 直径 + 微调this.data.ctx.drawImage(qrcode, 0, 0, qrcode.width, qrcode.height, x, y, 100, 100 / 0.8785)this.data.ctx.restore();}},// 绘制文字drawText() {const infoSpace = this.data.infoSpace // 下面数据间距const photoDiam = this.data.photoDiam // 圆形头像的直径this.data.ctx.save();this.data.ctx.font = "14px Arial"; // 设置字体大小this.data.ctx.fillStyle = "#ffffff"; // 设置文字颜色// 姓名(距左:间距 + 头像直径 + 间距)(距下:总高 - 间距 - 文字高 - 头像直径 + 下移一点 )this.data.ctx.fillText(this.data.name, infoSpace * 2 + photoDiam, this.data.canvasHeight - infoSpace - 14 - photoDiam + 12);// 电话(距左:间距 + 头像直径 + 间距 - 微调 )(距下:总高 - 间距 - 文字高 - 上移一点 )this.data.ctx.fillText(this.data.phone, infoSpace * 2 + photoDiam - 2, this.data.canvasHeight - infoSpace - 14 - 16);// 提示语(距左:间距 )(距下:总高 - 间距 )this.data.ctx.fillText(this.data.tips, infoSpace, this.data.canvasHeight - infoSpace);this.data.ctx.restore();},
仅此记下,给予需要帮助的人
记一次微信小程序canvas 2d 生成海报问题相关推荐
- 微信小程序纯前端生成海报并保存本地
需求 公司开发微信小程序,有一个海报页面,需要用户点击生成海报,可以将该该swipe-item 生成一个带二维码的图片,最终由纯前端实现! 技术调研 因为小程序的打包限制,不可能将所有的图片都放在代码 ...
- 【修复日常bug】微信小程序canvas画商品海报出现个别用户无法生成的情况
微信小程序,在使用canvas画海报的时候,会导致个别用户无法生成的情况,你们可以尝试把绘画某些块的代码注释掉再进行扫码调试,我前几天就遇到个别客户无法生成商品海报的情况,刚开始是以为上一个同事留的坑 ...
- 微信小程序实现画布生成海报功能
微信小程序可以通过使用 标签来实现生成海报的功能.以下是基本实现步骤: 1.在 WXML 文件中创建一个 标签,并设置其宽度和高度属性.' <canvas canvas-id="myC ...
- uniapp实现微信小程序端动态生成海报
背景: 基于uniapp实现微信小程序中商品详情的海报生成与保存 效果图: 思路: 首先把海报上需要的内容准备好,比如用户头像,商品信息,二维码等.需要注意的是,因为二维码是动态生成的,所以需要后端传 ...
- 微信小程序使用html2canvas,微信小程序canvas 2d 引入本地图片并生成分享图
在小程序基础库 v2.9.0 正式开放一套全新的 Canvas 接口.该接口符合 HTML Canvas 2D 的标准,实现上采用 GPU 硬件加速,渲染性能相比于现有的 Canvas 接口有一倍左右 ...
- 微信小程序canvas商品分享海报
先上效果图 1.wxml <button class="footer-item" bindtap="creatShareImageTap">分享&l ...
- 在微信小程序中实现生成海报图并保存到相册
效果图镇楼: 技术依赖: 弹窗 (vant-weapp 提供的 van-popup 组件) 海报图 (wx-canvas-2d 工具) 弹窗组件的使用方式可以点击上面链接查看,本篇主要讲解海报图绘制方 ...
- uniapp开发微信小程序使用painter生成海报
最初绘制海报时是准备用canvas绘制,但是绘制遇到各种问题,且难度颇高,最后也没能绘制出来,也尝试过使用微信官方的 wxml-to-canvas,但需要传入wxml和样式,太为难我这个小小的后端开发 ...
- 微信小程序canvas 2d 绘制图片与文字 导出图片
wxml内容 如下 <canvas id="myCanvas" type="2d"style="width: {{ canvas.width } ...
最新文章
- ios .a和.framework
- 计算机应用基础上机操作,计算机应用基础上机操作试题
- 网站实现个人支付宝即时到帐POST页面
- 卸载源码安装mysql_CentOS 7.x 卸载删除MariaDB,重新安装,安装MYSQL离线版和源代码...
- 终于有人将数据中台讲清楚了,原来根本不算啥
- 小程序组件的使用(三) 调用子组件方法
- 集团性企业数据信息系统解决方案
- ftp端口号_ftp端口号,完成ftp更改端口号只需5步
- 题2.pta数据结构题集-File Transfer (25分)
- 百度云链接后的html,百度云链接失效,这样就能找回!
- 网页访问计数器 html,网页计数器(访问量)
- HaaS EDU K1设备资源总体介绍
- Prometheus+Grafana搭建Jmeter性能监控平台
- 记一个bug:ImportError: cannot import name ‘comb‘
- 从零点一开始机器学习之晦涩难懂的各种概念
- java中报405错误怎么办_405错误的解决方法
- Python网络爬虫及数据可视化(软科中国大学专业排名|计算机科学与技术)
- Java基础(适合新学者和架构师阅读)
- 基本放大器电路- (一)
- innerHTML,outerHTML innerHTML