微信小程式之canvas
阿新 • • 發佈:2020-12-15
需求:分享當前商品儲存相簿以及分享卡片給好友
實現: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, } } })