需求:分享当前商品保存相册以及分享卡片给好友
实现:canvas 2D 小程序自带的分享
//wxml 文件内容 <canvas type="2d" id="canvasBox" style="width:260px;height:370px"></canvas> <view class="savebtn" bindtap="saveShareImg">保存</view>
//js 内容 // pages/goodsDetails/goodsDetails.js const $fn = getApp().$fn const $store = getApp().$store Page({ /** * 页面的初始数据 */ data: { id: null, // 商品id details: {}, // 商品详情 scaleHtml: \'zoom:\' + $fn.pxConv(1), // 商品富文本-缩放 swipe_sele: 0, // 选中的轮播图下标 buyPop_sele: -1, // 选中购买弹窗:-1 关闭弹窗,0 加入购物车,1 立即购买,2 选择规格 specs_index: -1, // 选中的规格下标-选择规格 buyNum: 1, // 购买数量-选择规格 type: 2, //订单类型 1 -> 购买vip 2 -> 商品购买 3 -> 代理购买 shareFlag: false, //控制分享弹框 postFlag: false, //控制海报盒子 // canvas 参数 type: "", // 是米粒还是现金 num: "", // 多少钱 posterUrl: "", // 海报地址 qrcodeUrl: "", // 小程序二维码 saleNum:\'\', // 设置区,针对部件的数据设置 qrcodeDiam: 80, // 小程序码直径 infoSpace: 13, // 底部信息的间距 saveImageWidth: 500, // 保存的图像宽度 bottomInfoHeight: 100, // 底部信息区高度 tips: "", // 商品信息 // 缓冲区,无需手动设定 canvasWidth: 0, // 画布宽 canvasHeight: 0, // 画布高 canvasDom: null, // 画布dom对象 canvas: null, // 画布的节点 ctx: null, // 画布的上下文 dpr: 1, // 设备的像素比 posterHeight: 0, }, // 暂未开发提示 noTips() { wx.showToast({ title: $store.noMakingTips, icon: \'none\' }) }, closePost() { this.setData({ postFlag: false }) }, createPost() { this.setData({ shareFlag: false, postFlag: true, //控制海报盒子 }) this.drawImage() }, openShare() { this.setData({ shareFlag: true }) }, closeShare() { this.setData({ shareFlag: false }) }, // 阻止点击冒泡 catchtap() {}, // 获取商品详情 getGoodsDetails() { $fn.get(\'getGoodsDetails\', { productId: this.data.id }).then(e => { console.log(\'商品详情\', e) let details = e.data.data.product details._picUrl = $fn.imgSlice(details.picUrl, "?imageView2/2/w/750/h/640") details._picUrl2 = $fn.imgSlice(details.picUrl, "?imageView2/2/w/144/h/144")[0] this.setData({ details, posterUrl: details._picUrl[0], tips: details.name, type:details.price == 0?\'米粒\':\'¥\', num:details.price == 0?details.integralPrice:details.price, saleNum:details.saleNum }) }) }, // 轮播图切换 switchSwipe(e) { this.setData({ swipe_sele: e.detail.current }) }, // 前往购物车 shoppingcartGo() { $fn.loginLink(\'/pages/shoppingcart/shoppingcart\') }, // 打开购买弹窗 buyPopSele(e) { this.setData({ buyPop_sele: e.currentTarget.dataset.i }) }, // 选中规格 switchSpecs(e) { this.setData({ specs_index: e.currentTarget.dataset.i }) }, // 修改购买数量 editBuyNum(e) { let n = this.data.buyNum - e.currentTarget.dataset.i this.setData({ buyNum: n > 0 ? n : 1 }) }, // 加入购物车 joinCart() { if (this.data.specs_index == -1) { wx.showToast({ title: \'请选择规格\', icon: \'none\' }) } else if ($store.token) { $fn.post({ url: \'addShoppingCart\', data: { productId: this.data.id, productSpecId: this.data.details.productSpecs[this.data.specs_index].id, num: this.data.buyNum }, tips: { load: \'请求中...\', mask: true, success: \'已添加到购物车\', } }).then(e => { console.log(\'加入购物车\', e) this.setData({ buyNum: 1 }) }) } else { wx.navigateTo({ url: \'/pages/signIn/signIn\' }) } }, // 立即购买 buyNow() { if (this.data.specs_index == -1) { wx.showToast({ title: \'请选择规格\', icon: \'none\' }) } else { $fn.loginLink(`/pages/confirmorderBuy/confirmorderBuy?sid=${this.data.id}&gid=${this.data.details.productSpecs[this.data.specs_index].id}&num=${this.data.buyNum}&type=2&isCart=false`) } }, // canvas事件 // 查询节点信息,并准备绘制图像 drawImage() { const query = wx.createSelectorQuery() // 创建一个dom元素节点查询器 query.select(\'#canvasBox\') // 选择我们的canvas节点 .fields({ // 需要获取的节点相关信息 node: true, // 是否返回节点对应的 Node 实例 size: true // 是否返回节点尺寸(width height) }).exec((res) => { // 执行针对这个节点的所有请求,exec((res) => {alpiny}) 这里是一个回调函数 console.log(res) 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: "生成中" }) // 显示loading that.drawPoster() // 绘制海报 .then(function () { // 这里用同步阻塞一下,因为需要先拿到海报的高度计算整体画布的高度 that.drawInfoBg() // 绘制底部白色背景 that.drawQrcode() // 绘制小程序码 that.drawText() // 绘制文字 that.drawMoney() //价格 that.drawSale()//销量 wx.hideLoading() // 隐藏loading }) }, drawPoster() { const that = this return 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 = this return new Promise(function (resolve, reject) { var canvasWidth = that.data.canvasDom.width // 获取画布宽度 var posterHeight = canvasWidth * (imgHeight / imgWidth) // 计算海报高度 var canvasHeight = posterHeight + that.data.bottomInfoHeight // 计算画布高度 海报高度+底部高度 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) }) }) }, 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(); }, // 绘制小程序码 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 - this.data.infoSpace - diam // 左上角相对X轴的距离:画布宽 - 间隔 - 直径 let y = this.data.canvasHeight - this.data.infoSpace - diam + 5 // 左上角相对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(qrcode, 0, 0, qrcode.width, qrcode.height, x, y, diam, diam) // 详见 drawImage 用法 this.data.ctx.restore(); } }, // 绘制文字 drawText() { const infoSpace = this.data.infoSpace // 下面数据间距 this.data.ctx.save(); this.data.ctx.font = "12px Arial"; // 设置字体大小 this.data.ctx.fillStyle = "#333333"; // 设置文字颜色 // 提示语(距左:间距 )(距下:总高 - 间距 ) // this.data.ctx.fillText(this.data.tips, infoSpace, this.data.canvasHeight-70); this.canvasTextAutoLine(this.data.tips, this.data.ctx, infoSpace, this.data.canvasHeight - 80, 16, 146) this.data.ctx.restore(); }, drawMoney() { const infoSpace = this.data.infoSpace // 下面数据间距 this.data.ctx.save(); this.data.ctx.font = "12px Arial"; // 设置字体大小 this.data.ctx.fillStyle = "#f2021b"; // 设置文字颜色 // 姓名(距左:间距 + 头像直径 + 间距)(距下:总高 - 间距 - 文字高 - 头像直径 + 下移一点 ) this.data.ctx.fillText(this.data.type, infoSpace , this.data.canvasHeight -18); this.data.ctx.fillText(this.data.num, infoSpace +24, this.data.canvasHeight-18 ); this.data.ctx.restore(); }, drawSale() { const infoSpace = this.data.infoSpace // 下面数据间距 this.data.ctx.save(); this.data.ctx.font = "12px Arial"; // 设置字体大小 this.data.ctx.fillStyle = "#999"; // 设置文字颜色 // 姓名(距左:间距 + 头像直径 + 间距)(距下:总高 - 间距 - 文字高 - 头像直径 + 下移一点 ) this.data.ctx.fillText(\'已售\', infoSpace+100 , this.data.canvasHeight -18); this.data.ctx.fillText(this.data.saleNum, infoSpace +124, this.data.canvasHeight-18 ); this.data.ctx.restore(); }, canvasTextAutoLine(str, ctx, initX, initY, lineHeight, canvasWidth) { const arrText = str.split(\'\') //字符串分割为数组 let currentText = \'\' // 当前字符串及宽度 let currentWidth for (let letter of arrText) { currentText += letter currentWidth = ctx.measureText(currentText).width if (currentWidth > canvasWidth) { ctx.fillText(currentText, initX, initY) currentText = \'\' initY += lineHeight } } if (currentText) { ctx.fillText(currentText, initX, initY) } }, //点击保存到相册 saveShareImg: function() { var that = this; wx.showLoading({ title: \'正在保存\', mask: true, }) setTimeout(function() { wx.hideLoading(); wx.canvasToTempFilePath({ x: 0, y: 0, width: that.data.canvasWidth, height: that.data.canvasHeight, destWidth: that.data.canvasWidth*that.data.dpr, destHeight:that.data.canvasHeight*that.data.dpr, canvas: that.data.canvas, success: function (res) { wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success(res) { wx.showModal({ title: \'图片保存成功!\', content: \'请将图片分享到朋友圈\', showCancel: false, confirmText: \'知道了\', confirmColor: \'#72B9C3\', success: function (res) { if (res.confirm) { console.log(\'用户点击确定\'); that.setData({ hideshare:true }) } } }) } }) }, fail: function (res) { console.log(res) } }) }, 1000); }, /** * 生命周期函数--监听页面加载 */ onLoad: function (e) { this.data.id = e.id this.getGoodsDetails() }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { return { title: this.data.details.name, } }, /** * 用户点击右上角转发到朋友圈 */ onShareTimeline: function () { return { title: this.data.details.name, } } })