背景
在一個(gè)完整帶用戶交互的小程序項(xiàng)目開(kāi)發(fā)中,總會(huì)遇到分享這個(gè)功能,轉(zhuǎn)發(fā)給好友用通用的api方法,分享朋友圈總是有各種各樣的招式,一般的交互方案是生成一個(gè)帶二維碼的圖,二維碼有時(shí)候也分帶參數(shù)和默認(rèn)的。分享一個(gè)前段時(shí)間開(kāi)發(fā)的生成分享圖的功能,我當(dāng)時(shí)的業(yè)務(wù)二維碼是帶參數(shù)的,為了識(shí)別能定位到固定產(chǎn)品頁(yè)(生成帶參數(shù)二維碼是用微信云調(diào)用提供的方法處理,點(diǎn)此看帖
分享圖用canvas畫布開(kāi)發(fā),所以對(duì)wx封裝的canvas相關(guān)api要有一定了解
開(kāi)發(fā)步驟
新建一個(gè)share組件文件包,并開(kāi)發(fā)業(yè)務(wù)邏輯和樣式編寫含小程序規(guī)范的幾個(gè)文件js/wxml/wxss/json
開(kāi)發(fā)代碼,在需要引用頁(yè)面的對(duì)應(yīng)配置文件中加入組件配置
在引用的wxml中加入組件代碼和傳參,js文件寫參數(shù)的交互
效果圖
代碼概覽
1.share / share.wxml 【參考效果圖 步驟2/3
備注:
1.第一模塊 步驟2的分享引導(dǎo)層,點(diǎn)擊分享票圈,出來(lái)第二模塊彈框
2.第二模塊 步驟3效果圖,share-load.gif是一個(gè)加載中動(dòng)畫的gif;close.png是關(guān)閉圖標(biāo)
-
<!--分享彈窗-->
-
<view class="share-wrap" bindtap="toClose">
-
<!-- 分享的引導(dǎo)層 分 轉(zhuǎn)發(fā)票圈或好友 -->
-
<view class="share-mod" catchtap="doNothing">
-
<view class="share-hd">
-
<text class="fl">分享</text>
-
<image class="share-close" catchtap="toClose" src="../../images/icons/close.png"></image>
-
</view>
-
<view class="share-guide">
-
<view class="share-wx">
-
<button class="share-btn" open-type="share"></button>
-
<image src="../../images/icons/wx_friend.png"></image>
-
<text>分享給好友</text>
-
</view>
-
<view class="share-line"></view>
-
<view class="share-wx" catchtap="toShowShareImg">
-
<image src="../../images/icons/wx_quan.png"></image>
-
<text>生成分享海報(bào)</text>
-
</view>
-
</view>
-
</view>
-
<!-- 點(diǎn)擊引導(dǎo)層的 轉(zhuǎn)發(fā)票圈 觸發(fā)的生成畫布圖彈層 -->
-
<view class="share-mod" hidden="{{!showShareImg}}" catchtap="doNothing">
-
<view class="share-hd">
-
<text class="fl">保存到相冊(cè)</text>
-
<image class="share-close" catchtap="toCloseShareImg" src="../../images/icons/close.png"></image>
-
</view>
-
<view class="share-quan">
-
<image wx:if="{{imgSrc!=''}}" class="share-img" src="{{imgSrc}}"></image>
-
<view wx:else>
-
<image class="share-img-load" src="../../images/icons/share-load.gif"></image>
-
<text class="share-load-text">{{loadText}}</text>
-
</view>
-
<view class="save-btn" catchtap="saveImg">保存圖片</view>
-
<view class="save-tip">保存圖片到手機(jī)相冊(cè)后,就可以分享至您的圈子啦</view>
-
</view>
-
</view>
-
</view>
2.share/ share.json
-
{
-
"component": true
-
}
3.share/ share.js
-
/* 使用說(shuō)明↓↓↓
-
1.需要引用的頁(yè)面json配置文件新增如下配置項(xiàng)
-
"usingComponents": {
-
"share-win": "/component/share/share"
-
}
-
-
2.頁(yè)面wxml文件使用如下 shareInfo格式說(shuō)明見(jiàn)下文 該組件是否渲染根據(jù)該對(duì)象是否有具體數(shù)據(jù)
-
<canvas canvas-id="shareCanvas" style="position:fixed;top:0;left:999rpx;width:1000px;height:750px;"></canvas>
-
<share-win share-info="{{shareInfo}}" bindcloseshare="closeShareWin"></share-win>
-
-
3.頁(yè)面對(duì)應(yīng)js需要定義一個(gè)closeshare事件[由組件里的toClose觸發(fā)] 內(nèi)部主要是將shareInfo參數(shù)置空
-
-
4.shareInfo格式
-
-
*/
-
-
Component({
-
properties: {
-
shareInfo: {
-
type: Object,
-
value: {},
-
}
-
},
-
data: {
-
imgSrc:'',
-
showShareImg:false,
-
hasDownload:false,
-
loadText:'分享圖繪制準(zhǔn)備中...'
-
},
-
ready:function(){
-
-
},
-
methods: {
-
//點(diǎn)擊浮層區(qū)域關(guān)閉彈窗
-
toClose:function(){
-
this.toCloseShareImg();
-
this.triggerEvent("closeshare")
-
},
-
toCloseShareImg:function(){
-
this.setData({
-
showShareImg:false
-
})
-
if(this.data.hasDownload){
-
this.triggerEvent("closeshare")
-
}
-
},
-
toShowShareImg:function(){
-
this.setData({
-
showShareImg:true
-
})
-
this.renderShareImg();
-
},
-
doNothing:function(){
-
return false;
-
},
-
setLoadText:function(txt){
-
this.setData({
-
loadText:txt
-
})
-
},
-
//渲染分享圖
-
renderShareImg:function(){
-
//1000x750
-
const _this = this;
-
const _obj = _this.data.shareInfo;
-
-
//默認(rèn)題圖
-
let promise1 = new Promise(function (resolve, reject) {
-
if(_obj.cover==undefined || _obj.cover==''){
-
_obj.cover = '../../images/share_default.jpg'
-
resolve({path:_obj.cover});
-
}else{
-
_obj.cover = "https://"+_obj.cover.split('//')[1]
-
wx.getImageInfo({
-
src: _obj.cover,
-
success: function (res) {
-
resolve(res);
-
}, fail: function (error) {
-
console.log(error);
-
-
_obj.cover = '../../images/share_default.jpg'
-
resolve({path:_obj.cover});
-
}
-
})
-
}
-
});
-
-
//小程序碼
-
let promise2 = new Promise(function (resolve, reject) {
-
wx.cloud.callFunction({
-
name: 'openapi',
-
data: {
-
action:'getWXACodeUnlimit',
-
page: 'pages/detail/detail',
-
width: 220,
-
scene: _obj.id+"_"+_obj.goodsId,
-
},
-
success: res => {
-
wx.getImageInfo({
-
src: res.result[0].tempFileURL,
-
success: function (suc) {
-
resolve(suc);
-
}, fail: function (error) {
-
resolve({path:"../../images/qrcode.jpg"})
-
console.log(error)
-
}
-
})
-
},
-
fail: error => {
-
console.log(JSON.stringify(error))
-
resolve({path:"../../images/qrcode.jpg"})
-
}
-
});
-
});
-
-
-
//加載所有完圖片后繪制畫布
-
Promise.all(
-
[promise1,promise2]
-
).then(res => {
-
//繪制頭圖的圓角效果
-
const ctx = wx.createCanvasContext('shareCanvas')
-
ctx.setFillStyle('#ffffff');
-
ctx.fillRect(0, 0, 750, 1125);
-
-
//繪制題圖
-
_this.setLoadText("繪制商品圖...")
-
ctx.drawImage(res[0].path, 25, 25, 700, 700)
-
-
// ...刪除了部分 繪制邏輯代碼...
-
-
//繪制小程序碼
-
_this.setLoadText("繪制小程序碼...")
-
ctx.drawImage(res[1].path, 35, 874, 228, 228);
-
-
-
//畫布繪制完成轉(zhuǎn)圖片,將地址賦值給圖片
-
_this.setLoadText("分享圖生成中...")
-
ctx.draw();
-
-
setTimeout(function () {
-
wx.canvasToTempFilePath({
-
width: 750,
-
height: 1125,
-
destWidth: 750,
-
destHeight: 1125,
-
quality: 1,
-
canvasId: 'shareCanvas',
-
success: function (res) {
-
// console.log("canvasToTempFilePath success:"+res.tempFilePath);
-
wx.hideLoading({})
-
_this.setData({
-
imgSrc: res.tempFilePath,
-
shareShow: true
-
})
-
},
-
fail: function (res) {
-
}
-
})
-
}, 200)
-
})
-
},
-
//保存圖片
-
saveImg:function(){
-
//下載文件
-
const _this = this;
-
if(_this.data.imgSrc==''){
-
wx.showToast({
-
title:"分享圖還在生成中...",
-
icon: 'none',
-
duration:3000
-
})
-
return false;
-
}
-
wx.saveImageToPhotosAlbum({
-
filePath: _this.data.imgSrc,
-
success(res) {
-
wx.showToast({
-
title:"已保存至相冊(cè),可以分享啦",
-
icon: 'none',
-
duration:3000
-
})
-
_this.setData({
-
hasDownload:true
-
})
-
}
-
})
-
},
-
//圖片按比例居中裁剪
-
calClipImg(oW,oH,mW,mH){
-
var oR = parseFloat(oW/oH).toFixed(5);
-
var mR = parseFloat(mW/mH).toFixed(5);
-
if(oR == mR){
-
return [0,0,oW,oH]
-
}else if(oR > mR){
-
var ratio = parseFloat(mH/oH).toFixed(5);
-
return [((oW*ratio-mW)/2)/ratio,0,mW/ratio,mH/ratio];
-
}else{
-
var ratio = mW/oW;
-
return [0,((oH*ratio-mH)/2)/ratio,mW/ratio,mH/ratio];
-
}
-
},
-
//繪制圓角
-
roundRect(x, y, w, h, r,ctx){
-
var min_size = Math.min(w, h);
-
if (r > min_size / 2) r = min_size / 2;
-
// 開(kāi)始繪制
-
ctx.beginPath();
-
ctx.moveTo(x + r, y);
-
ctx.arcTo(x + w, y, x + w, y + h, r);
-
ctx.arcTo(x + w, y + h, x, y + h, r);
-
ctx.arcTo(x, y + h, x, y, r);
-
ctx.arcTo(x, y, x + w, y, r);
-
ctx.closePath();
-
},
-
//繪制文本方法
-
drawText(str,ctx,initX,initY,lineHeight,minusW,maxLine){
-
var curLine = 1;
-
var lineWidth = 0;
-
var canvasWidth = 750;
-
var lastSubStrIndex= 0;
-
var d = 0;
-
for(var i=0;i<str.length;i++){
-
lineWidth += ctx.measureText(str[i]).width;
-
//判斷當(dāng)前文字行是否超過(guò)一行 [減minusW,防止邊界溢出]
-
if((d==0 && lineWidth>canvasWidth-minusW)||(d>0 && ((lineWidth>=canvasWidth-minusW) || ((lineWidth+ctx.measureText(str[i+1]).width)>canvasWidth-minusW)))){
-
d++;
-
ctx.fillText(str.substring(lastSubStrIndex,i),initX,initY);
-
initY+=lineHeight;
-
lineWidth=0;
-
lastSubStrIndex=i;
-
curLine = curLine+1;
-
if(maxLine!=-1 && curLine>maxLine)break; //最多繪制六行
-
}
-
//最后一個(gè)字的時(shí)候 繪制一行
-
if(i==str.length-1){
-
ctx.fillText(str.substring(lastSubStrIndex,i+1),initX,initY);
-
}
-
}
-
}
-
}
-
})
4.share.wxss
-
.share-wrap{
-
position:fixed;
-
top:0;
-
width:750rpx;
-
height:100%;
-
background:rgba(0,0,0,.4);
-
overflow: hidden;
-
z-index:1001;
-
}
-
-
.share-mod{
-
position:fixed;
-
bottom:0;
-
width:100%;
-
background:#fff;
-
z-index:1001;
-
overflow: hidden;
-
}
-
-
.share-mod .share-hd{
-
padding-left:20rpx;
-
height:80rpx;
-
line-height:80rpx;
-
background:#efefef;
-
color:#666;
-
font-size:32rpx;
-
}
-
-
.share-mod .share-close{
-
float:right;
-
margin:15rpx 20rpx;
-
height:50rpx;
-
width:50rpx;
-
}
-
-
.share-guide{
-
padding:35rpx;
-
width:680rpx;
-
height:180rpx;
-
}
-
-
.share-guide .share-wx,
-
.share-guide .share-line{
-
float:left;
-
}
-
.share-guide .share-line{
-
margin-top:60rpx;
-
height:160rpx;
-
width:1rpx;
-
color:#cdcdcd;
-
}
-
.share-guide .share-wx{
-
width:339rpx;
-
height:180rpx;
-
text-align:center;
-
font-size:24rpx;
-
}
-
.share-guide .share-wx image{
-
display: block;
-
margin:20rpx auto;
-
padding:10rpx;
-
width:64rpx;
-
height:64rpx;
-
border-radius:43rpx;
-
border:1rpx solid #dedede;
-
}
-
.share-guide .share-btn{
-
position: absolute;
-
margin:0;
-
padding:0;
-
bottom:40rpx;
-
left:35rpx;
-
width:340rpx;
-
height:180rpx;
-
background:none;
-
}
-
.share-guide .share-btn:after{
-
border:none;
-
}
-
.share-quan{
-
margin:20rpx;
-
overflow: hidden;
-
}
-
.share-quan .share-img{
-
display:block;
-
margin:10rpx auto 28rpx;
-
height:600rpx;
-
width:400rpx;
-
border-radius:8rpx;
-
box-shadow:0 0 10rpx #cdcdcd;
-
}
-
.share-quan .share-img-load{
-
display:block;
-
margin:260rpx auto 20rpx;
-
height:80rpx;
-
width:80rpx;
-
}
-
.share-quan .share-load-text{
-
margin:0 auto 220rpx;
-
display:block;
-
widows:100%;
-
text-align:center;
-
font-size:28rpx;
-
color:#b7b7b7;
-
}
-
.share-quan .save-btn{
-
width:710rpx;
-
height:80rpx;
-
line-height:80rpx;
-
color:#fff;
-
text-align:center;
-
letter-spacing:4rpx;
-
background-color:#e2633f;
-
border-radius:6rpx;
-
font-size:34rpx;
-
}
-
.share-quan .save-tip{
-
margin:18rpx;
-
text-align:center;
-
font-size:24rpx;
-
}
↓組件開(kāi)發(fā)已經(jīng)完成,接下去是組件的使用↓
1.demo.json 備注:usingComponents加入對(duì)應(yīng)組件配置即可
-
{
-
"navigationBarBackgroundColor": "#ffffff",
-
"navigationBarTextStyle": "black",
-
"navigationBarTitleText": "xxx",
-
"usingComponents": {
-
"share-win": "/component/share/share"
-
}
-
}
2.demo.wxml 備注:點(diǎn)擊分享按鈕的時(shí)候 showShareWin值改變,shareInfo根據(jù)渲染需求賦值
-
<view>
-
<!-- S 其他業(yè)務(wù)代碼 -->
-
<!-- E 其他業(yè)務(wù)代碼 -->
-
-
<!-- 分享 -->
-
<canvas canvas-id="shareCanvas" style="position:fixed;top:0;left:999rpx;width:750px;height:1125px;"></canvas>
-
<share-win wx:if="{{showShareWin}}" share-info="{{shareInfo}}" bindcloseshare="closeShareWin"></share-win>
-
-
</view>
3.demo.js 備注:刪除了其他業(yè)務(wù)代碼,僅剩和分享的交互,便于閱讀。shareInfo數(shù)據(jù)在load時(shí)就塞進(jìn)去了,下面沒(méi)有放出來(lái)~
-
Page({
-
data: {
-
showTop:false
-
},
-
//點(diǎn)擊右側(cè)懸浮的分享按鈕
-
doShare:function(){
-
this.setData({
-
showShareWin:true
-
})
-
},
-
//觸發(fā)關(guān)閉分享彈框
-
closeShareWin:function(){
-
this.setData({
-
showShareWin:false
-
})
-
},
-
})
其他說(shuō)明: 步驟4為最終生成效果圖,微信識(shí)別二維碼就可定位到具體業(yè)務(wù)頁(yè)~