小程序分享的三種方式
小程序分享基本就三種:分享好友晃虫、小程序碼圖片分享以及朋友圈分享皆撩,日常開發(fā)比較常見的是分享好友;
由于第一次做小程序分享哲银,網(wǎng)上又大多只有好友分享扛吞,或者只有零星的分享攻略,所以踩了n多坑后荆责,自己整理一下鞏固的同時(shí)能希望幫到其他人吧滥比,畢竟百度上暫時(shí)還找不到這么全的攻略。
希望能幫助到大家
分享好友
1做院、喚起分享好友有兩種方式盲泛,頁面右上角...和屬性openType="share"的Button組件濒持。
2、兩種喚起分享的方式都會(huì)觸發(fā)頁面的onShareAppMessage的方法寺滚,方法返回的參數(shù)中from字段返回表示分享的途徑:按鈕button或者非按鈕(右上角分享)柑营。
這里有點(diǎn)小技巧,如果通過按鈕分享并且按鈕是封裝在子組件中村视,可以通過button的id屬性傳遞一些數(shù)據(jù)給頁面
1官套、按鈕分享
<!-- 通過id可以傳遞參數(shù) -->
<Button
className="flex ali-item-center jus-content-center share"
openType="share"
id={props.shareInfo.shareParam || 'share'}
>
<Text className="iconfont icon-wechat" />
</Button>
2、參數(shù)獲取
// 參數(shù)獲取
onShareAppMessage(share) {
console.error('from', share);
let shareObj = {},
target;
if (share.from === 'button') {
target = share.target.id; // 這個(gè)id獲取到的就是上面button的id屬性值蚁孔,也就是 props.shareInfo.shareParam || 'share'
} else {
target = `classId=${this.state.classId}&dynamicId=${this.state.dynamicId}`;
}
shareObj = {
title: default_share_Title,// 分享標(biāo)題
// desc: '分享描述',
path: `/分享地址?${target}`,
imageUrl: default_share_circle, // 分享封面圖
};
shareObj = {
...shareObj,
success: (res) => {
console.error('分享成功', res);
},
fail: (error) => {
console.error('分享失敗', error);
},
};
console.error('shareObj', shareObj);
return shareObj;
}
3奶赔、注意??
需要注意的是,小程序首頁底部欄的幾個(gè)tab頁面公用的是同一個(gè)頁面也就是index頁面杠氢,但我們通常是一個(gè)tab對(duì)應(yīng)一個(gè)路由頁面纺阔,所以底部欄所有的tab頁面對(duì)應(yīng)按鈕分享和右上角分享,觸發(fā)的都是index頁面的onShareAppMessage修然。
小程序碼分享
主要是先通過調(diào)用后端接口獲取小程序碼笛钝,然后用canvas繪圖,保存繪制的圖片最后分享圖片愕宋。
1玻靡、獲取小程序碼
- 后端調(diào)用小程序的api后返回文檔流或者base64數(shù)據(jù)流,如果是返回文檔流中贝,在請(qǐng)求接口的時(shí)候需要把responseType設(shè)置為arraybuffer
let option = {
isShowLoading: false,
loadingText: '正在加載',
url: isBaseUrl ? base + requestUrl : requestUrl,
data: data,
method: method,
responseType: responseType || 'text', // 默認(rèn)是text囤捻,如果請(qǐng)求文檔流,設(shè)置為arraybuffer
header: {
'Content-Type': contentType,
'Access-Control-Allow-Origin': '*',
token: txtInfo.unionId
? txtInfo.unionId + '@@@' + Taro.getStorageSync('accountId')
: '@@@',
},
2邻寿、然后解析:如果是文檔流蝎土,需要調(diào)用wx的api轉(zhuǎn)base64
/** 獲取小程序碼 */
getMiniCode({scene, pagePath}) {
return new Promise((resolve, reject) => {
wx.showLoading({
title: '生成圖片中...',
});
const accountId = Taro.getStorageSync('accountId');
$Request
.getMiniCodeUnlimit({
accountId,
scene,
page: pagePath,
})
.then((res) => {
// 獲取小程序碼(圖片流),轉(zhuǎn)base64 ==> const qrcode = wx.arrayBufferToBase64(res);
if (res.data && res.success) {
const base64 = 'data:image/jpg;base64,' + res.data;
resolve(base64);
} else {
setTimeout(() => {
WxActions.fnShowToast(res.errorMsg);
}, 300);
}
setTimeout(() => {
wx.hideLoading();
}, 300);
})
.catch((err) => {
console.log('請(qǐng)求失敗', err);
setTimeout(() => {
wx.hideLoading();
WxActions.fnShowToast('獲取小程序碼失敗');
}, 300);
reject(err);
});
});
},
3绣否、獲取到對(duì)應(yīng)到base64碼后誊涯,繪制canvas,當(dāng)然繪制之前還有很多信息要處理:
const {classId, dynamic} = this.props;
WxActions.getMiniCode({
scene: `dynamicId=${dynamic.id}&classId=${classId}&v=1`,
pagePath: 'pages/schoolCircleDetail/schoolCircleDetail',
}).then((base64) => {
wx.showLoading({
title: '生成圖片中...',
});
const canvas: any = WxActions.getShareCanvas(
{
shareFrom: this.state.shareFrom,
shareCode: base64,
shareTitle: '你的好友邀請(qǐng)你一起參與',
shareCover: this.state.coverImg,
},
'shareCanvas',
this.$scope,
);
canvas
.then((canvasSrc) => {
setTimeout(() => {
wx.hideLoading();
}, 300);
this.setState({
isShowModal: true,
canvasSrc: canvasSrc,
});
})
.catch(() => {
setTimeout(() => {
wx.hideLoading();
}, 300);
});
});
其中生成canvas圖片邏輯如下:
(獲取圖片信息蒜撮,由于后來后端同事?lián)Q成二進(jìn)制編碼返回暴构,所以要處理)
const fsm = wx.getFileSystemManager();
const FILE_BASE_NAME = 'tmp_base64src';
const base64src = function(base64data) {
return new Promise((resolve, reject) => {
const [format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
if (!format) {
reject(new Error('ERROR_BASE64SRC_PARSE'));
}
const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`;
const buffer = wx.base64ToArrayBuffer(bodyData);
fsm.writeFile({
filePath,
data: buffer,
encoding: 'binary',
success() {
resolve(filePath);
},
fail() {
reject(new Error('ERROR_BASE64SRC_WRITE'));
},
});
});
};
// 。段磨。取逾。。苹支。砾隅。。
/** 分享canvas */
initTime: 1, // 獲取圖片信息可能會(huì)失敗债蜜,所以失敗后可以再次獲取晴埂,最多獲取canvasObj.maxTime或3次
getShareCanvas(canvasObj, canvasId, otx) {
WxActions.initTime = 1; // 每次點(diǎn)擊分享究反,重置當(dāng)前次數(shù)為默認(rèn)值1.
return new Promise((resolve, reject) => {
WxActions.getCanvasInfo(canvasObj, canvasId, otx)
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
},
/** 獲取小程序碼的imageinfo */
async getCanvasInfo(canvasObj, canvasId, otx) {
return new Promise((resolve, reject) => {
let drawWidth;
//
wx.getSystemInfo({
success: async (sys) => {
console.error('getSystemInfo', sys);
const rapito = 0.71;
const startX = 2,
startY = 80;
drawWidth = (await sys.screenWidth) * rapito;
let drawHeight = (drawWidth * 4) / 5;
console.error('drawWidth', drawWidth, drawHeight);
drawHeight = drawHeight > 150 ? 150 : drawHeight;
const src = await base64src(canvasObj.shareCode);
wx.getImageInfo({
src,
success: (codeInfo) => {
// console.error('1111', codeInfo);
wx.getImageInfo({
src: canvasObj.shareCover,
success: (res) => {
console.error('圖片信息', res);
let cutWidth = res.width,
cutHeight = (res.width * 4) / 5;
cutHeight = cutHeight > res.height ? res.height : cutHeight;
console.error('圖片信息===', res.width, cutHeight);
WxActions.initTime = 1;
// console.error('獲取封面圖成功', res);
//res.path是網(wǎng)絡(luò)圖片的本地地址
const canvasCtx = wx.createCanvasContext(canvasId, otx);
// console.error('canvasCtx', canvasCtx);
// canvas的繪制看自己情況定制
//繪制背景 ...
canvasCtx.fillStyle = '#fff';
canvasCtx.fillRect(0, 0, sys.screenWidth, sys.screenWidth);
//繪制分享的標(biāo)題文字
canvasCtx.setFontSize(17);
canvasCtx.setFillStyle('#333');
canvasCtx.fillText(canvasObj.shareTitle, 15, 35);
//繪制分享的第二行標(biāo)題文字
canvasCtx.setFontSize(13);
canvasCtx.setFillStyle('#999');
canvasCtx.fillText(`來自:${canvasObj.shareFrom}`, 15, 60);
//繪制圖片
// drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
// (圖片地址, 繪制圖片在畫布的起始x, 繪制圖片在畫布的起始y, 截取圖的寬度(原), 截取圖的高度(原), 圖片放置位置x, 圖片放置位置y, 在canva上繪制的長(zhǎng)度,在canvas上繪制的高度)
canvasCtx.drawImage(
res.path,
startX,
startY,
cutWidth,
cutHeight, //res.height,
startX,
startY,
drawWidth,
drawHeight,
);
//繪制小程序碼
// canvasCtx.drawImage(canvasObj.shareCode, 15, 250, 60, 60);
canvasCtx.drawImage(codeInfo.path, 15, drawHeight + 90, 60, 60);
//繪制分享的說明
canvasCtx.setFontSize(13);
canvasCtx.setFillStyle('#333');
canvasCtx.fillText('長(zhǎng)按小程序碼看詳情', 85, drawHeight + 130);
canvasCtx.stroke();
canvasCtx.draw(false, () => {
wx.canvasToTempFilePath(
{
canvasId: canvasId,
success: (ctx) => {
// console.error('save的canvas圖片', ctx);
// 獲得圖片臨時(shí)路徑,用來保存到本地
return resolve(ctx.tempFilePath);
},
fail: (err) => {
//失敗回調(diào)
console.error('save的canvas圖片失敗', err);
WxActions.fnShowToast('獲取小程序碼失敗');
return reject();
},
},
otx, // 必須要有當(dāng)前文檔的實(shí)例
);
});
},
fail: (res) => {
//失敗回調(diào)
console.error('獲取封面圖失敗', res);
if (WxActions.initTime < 3) {
WxActions.initTime++;
// 獲取失敗后再次調(diào)用自身邑时,重新獲取奴紧,最多次數(shù)為canvasObj.maxTime或3次
WxActions.getCanvasInfo(canvasObj, canvasId, otx);
} else {
setTimeout(() => {
WxActions.fnShowToast('獲取封面圖失敗');
}, 350);
}
return reject();
},
});
},
fail: (res) => {
//失敗回調(diào)
console.error('2222', res);
},
});
},
fail: (err) => {
return reject(err);
},
});
});
},
4、最后保存圖片
WxActions.fnWxSetting('scope.writePhotosAlbum').then((/** 授權(quán) */) => {
this.setState({
canvasSrc: '',
isShowShare: false,
});
WxActions.fnSaveImageToAlbum(this.state.canvasSrc).then(() => {
this.onCancelModal();
});
});
// 晶丘。黍氮。。浅浮。沫浆。。滚秩。专执。
/** 保存圖片到本地相冊(cè) */
fnSaveImageToAlbum(filePath) {
return new Promise((resolve, reject) => {
wx.saveImageToPhotosAlbum({
filePath,
success(yes) {
console.log(yes.errMsg);
WxActions.fnShowToast('保存圖片成功');
return resolve();
},
fail(error) {
console.log(error.errMsg);
WxActions.fnShowToast('保存圖片失敗');
return reject();
},
});
});
},
到此,基本完成小程序碼生成canvas并保存圖片到手機(jī)相冊(cè)到需求郁油,由于時(shí)間比較緊本股,很多細(xì)節(jié)沒處理好。
5桐腌、掃碼進(jìn)入的參數(shù)獲取
接下來就是大坑了
在正常的小程序里面拄显,掃碼進(jìn)入后獲取參數(shù)是這樣的
onLoad:function(options){
if(options.scene){
let scene=decodeURIComponent(options.scene);
//&是我們定義的參數(shù)鏈接方式
let userId=scene.split("&")[0];
let recommendId=scene.split('&')[1];
//其他邏輯處理。案站。躬审。。蟆盐。
}
}
但由于本項(xiàng)目用的是taro承边,也就是用react編寫,由于查找了網(wǎng)上的攻略石挂,于是先入為主的一直想要獲取scene參數(shù)博助,找到一片文章說在componentWillMount生命周期里獲取
componentWillMount(ops: any) {
console.error('ops', ops);
if (ops && ops.scene) {
let shareOps = WxActions.getMiniCodeScene(ops.scene);
this.setState({
isScene: true,
shareScene: shareOps,
});
}
}
結(jié)果發(fā)現(xiàn)這是不對(duì)的!不對(duì)的誊稚!不對(duì)的O枋肌!里伯!必須要在componentdidmount里面獲取,并且要通過this.$router.params.scene獲取才是正常的渤闷,所以如果正常的頁面和分享后掃碼進(jìn)入的是同一個(gè)頁面疾瓮,就要區(qū)分參數(shù)的獲取了。
componentDidMount() {
if (this.$router.params.scene) {
// 掃碼進(jìn)入
}else{
// 正常小程序頁面進(jìn)入
}
}
朋友圈分享
分享朋友圈飒箭,微信暫時(shí)沒有提供可以直接的分享方式狼电,都是通過客服會(huì)話返回h5鏈接實(shí)現(xiàn)的蜒灰,相當(dāng)于曲線救國(guó),下面是相關(guān)邏輯肩碟。
1强窖、html代碼,喚起客服會(huì)話
ps:h5地址域名必須是https的
<Button
sendMessageTitle={shareInfo.shareTitle ? shareInfo.shareTitle : '分享朋友圈'} // 分享的客服會(huì)話標(biāo)題
sendMessageImg={shareInfo.shareImg ? shareInfo.shareImg : default_share_circle} // 分享的客服會(huì)話的封面圖
sendMessagePath={shareInfo.sharePath || default_share_CircleUrl} // 對(duì)應(yīng)的需要分享到朋友圈的h5鏈接
sessionFrom={'wkbbapp'} // 暫時(shí)發(fā)現(xiàn)沒啥用
openType="contact" // 必填削祈,表示喚起客服會(huì)話
showMessageCard // 必填翅溺,喚起會(huì)話右下角的截圖彈出
plain
className="flex ali-item-center jus-content-center share"
style={{border: 'none'}}
onContact={(res) => {
console.error('onContact', res);
}}
>
<Text className="iconfont icon-quan" />
</Button>
2、后端推送會(huì)話卡片
前端只需要上面的button對(duì)應(yīng)的代碼髓抑,后端會(huì)獲取到wx對(duì)應(yīng)的推送咙崎,然后根據(jù)推送返回的內(nèi)容,再由后端推送卡片消息到會(huì)話窗口吨拍,用戶只要點(diǎn)擊對(duì)應(yīng)的窗口褪猛,就能進(jìn)入對(duì)應(yīng)的鏈接地址(其實(shí)就是一個(gè)h5地址,點(diǎn)擊后會(huì)進(jìn)入微信瀏覽器)羹饰,然后點(diǎn)擊右上角伊滋,分享朋友圈。
3队秩、H5頁面分享處理
顯然笑旺,這已經(jīng)是另外一個(gè)項(xiàng)目了。
這個(gè)項(xiàng)目要做的刹碾,就是平時(shí)公眾號(hào)要做的事情了燥撞。
- 首先是在html頁面引入微信sdk
<script type="text/javascript" src="https://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
- 然后是注冊(cè)(封裝)
//微信分享
export function wxShare(_option: {
title: string; // 分享標(biāo)題
desc: string; // 分享描述
link: string; // 分享鏈接
imgUrl: string; // 分享圖標(biāo)
success?: any;
cancel?: any;
}) {
configWxApi()
.then((res: any) => {
console.log('_option', _option);
console.log(res);
wx.config(res.data);
wx.ready(function() {
//分享給朋友
wx.onMenuShareAppMessage(_option);
// wx.updateAppMessageShareData(_option); //(1.4.0)
//分享到朋友圈
wx.onMenuShareTimeline(_option);
// wx.updateTimelineShareData(_option); //(1.4.0)
});
wx.error((error: any) => {
console.error(error);
Toast.info(`${error.errMsg}`, 3);
// config信息驗(yàn)證失敗會(huì)執(zhí)行error函數(shù),如簽名過期導(dǎo)致驗(yàn)證失敗迷帜,具體錯(cuò)誤信息可以打開config的debug模式查看物舒,也可以在返回的res參數(shù)中查看,對(duì)于SPA可以在這里更新簽名戏锹。
// alert("接口驗(yàn)證失敗冠胯,詳細(xì)信息:\n" + JSON.stringify(error));
});
// 提示服務(wù)器異常暫時(shí)取反
})
.catch((err: any) => {
console.log(err);
});
}
- 頁面分享:
async componentDidMount() {
wxShare({
title: this.props.noticeDetail.title || default_circle_title, // 分享標(biāo)題
desc: default_share_desc, // 分享描述
link: window.location.href, // 分享鏈接
imgUrl: this.props.noticeDetail.coverUrl || default_share_notice, // 分享圖標(biāo)
});
}
ps:注冊(cè)微信分享失敗的問題基本是簽名錯(cuò)誤獲取域名不在合法域名列表,在此不再多描述了锦针。
總結(jié)
開始公司沒同事做過相關(guān)業(yè)務(wù)荠察,網(wǎng)上資料也不全,前后端都摸著石頭過河奈搜,走了很多彎路悉盆,所以把這整套流程走通很不容易,
也正因如此馋吗,完成后得到的成就感也是最大的焕盟。
最后,本文是原創(chuàng)宏粤,希望對(duì)你有所幫助~