需求:分享當前商品保存相冊以及分享卡片給好友
實現: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,
}
}
})

