系列文章传送门:
目录
首先我们看一下要完成的效果:
这种场景还是非常常见的,点击分享的时候我们可以转发给好友,或者生成当前页的海报图片保存到手机相册中。分享给好友这个功能可以通过 button 的 open-type 方式实现,那自动保存图片到本地该如何实现呢,让我们来看一看吧:
一、封装分享组件
首先我们要封装一个分享的组件,这样方便在其他的页面中复用。这样就大大减少了代码的冗余,
在 components 文件夹中新建一个组件,下面是完整代码
share.wxml:
<!-- 底部自定义分享菜单栏 -->
<view class="share-sheet-mask flex-column" hidden="{{!showShareSheet}}" catchtap="closeShareSheet">
<view class="share-sheet">
<view class="items flex-row">
<view class="item flex-column">
<button class="ico flex-row" open-type="share">
<image class="img wx-ico" src="../../images/ico_wx.svg"></image>
</button>
<view class="desc">微信好友</view>
</view>
<view class="item flex-column">
<button class="ico flex-row" catchtap="genPlayBill">
<image class="img img-ico" src="../../images/ico_img.svg"></image>
</button>
<view class="desc">生成海报</view>
</view>
</view>
<view class="cancel-share-sheet">取消</view>
</view>
</view>
share.js:
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
showShareSheet: false
},
/**
* 组件的方法列表
*/
methods: {
openShareSheet() {
this.setData({showShareSheet: true})
},
closeShareSheet() {
this.setData({showShareSheet: false});
},
genPlayBill() {
this.triggerEvent('genPlayBill')
},
}
})
在点击生成海报的时候,我们向父组件发送了一个事件来调用 genPlayBill 方法,因为这个方法显然不应该在当前组件内定义,应该根据不同场景来定义我们只需要调用它就可以了。
share.wxss:
.flex-column {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
.flex-row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.share-sheet-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.60);
z-index: 101;
justify-content: flex-end;
}
.share-sheet-mask .share-sheet {
padding:0 24rpx;
background: #f1f1f1;
border-radius: 16rpx 16rpx 0px 0px;
width: 100%;
box-sizing: border-box;
}
.share-sheet-mask .share-sheet .items {
padding: 48rpx 0 31rpx 0;
justify-content: space-around;
border-bottom: 0.5px solid rgba(39,36,71,0.20);
}
.share-sheet-mask .share-sheet .items .item{
text-align: center;
}
.share-sheet-mask .share-sheet .items .item .ico {
width: 80rpx;
height: 80rpx;
background: #ffffff;
border-radius: 50%;
box-shadow: 0 0 12rpx 0 rgba(204,204,204,0.60);
padding: 0;
margin: 0;
padding: 0;
}
/*去除button的默认黑边框*/
.share-sheet-mask .share-sheet .items .item .ico::after{
border: none;
}
.share-sheet-mask .share-sheet .items .item .ico .img {
margin: auto;
}
.share-sheet-mask .share-sheet .items .item .ico .wx-ico {
width: 52rpx;
height: 40rpx;
}
.share-sheet-mask .share-sheet .items .item .desc {
font-size: 24rpx;
font-family: PingFangSC, PingFangSC-Regular;
color: #6d6d6d;
margin-top: 14rpx;
}
.share-sheet-mask .share-sheet .items .item .ico .img-ico {
width: 44rpx;
height: 44rpx;
}
.share-sheet-mask .share-sheet .cancel-share-sheet {
margin: 31rpx auto 80rpx auto;
font-size: 32rpx;
font-family: PingFangSC, PingFangSC-Regular;
color: #272447;
text-align: center;
}
二、定义用户授权方法
刚刚我们封装了顶部的分享组件,那现在就要去定义保存图片到相册的方法了,我们写代码的时候一定要考虑清楚这段代码是否是可复用的,是否应该剥离出去。显然保存图片到相册这个方法我们应该写在 utils 目录中,因为有很多其他的场景都可以用这个方法,那我们就封装一个公用方法,参数就是图片的地址,成功的回调函数和失败的回调函数。
最复杂的就是用户授权了,我们一起看一下代码结构:
const saveImgToPhotos = (imgPath, succCallback, failedCallback) => {
wx.getSetting ({ // 查询所有授权
success(res) {
if (res.authSetting['scope.writePhotosAlbum']) { // 用户已经授权
save() // 执行保存函数
}else { // 未授权
wx.authorize({ scope: 'scope.writePhotosAlbum',
success() { // 用户同意授权
save() // 执行保存函数
},
fail(err) { // 用户拒绝授权
if (err && err.errMsg.endsWith("auth deny")) {
wx.showModal({
title: '授权添加到相册',
content: '需要获取您的添加相册权限,请确认授权,否则分享功能无法正常使用',
success: function (resolve) {
if (resolve.confirm) { // 用户同意设置授权
wx.openSetting({
success(res) {
if (res && res.authSetting['scope.writePhotosAlbum']) {
save() // 执行保存函数
}
},
fail(res) { // 用户拒绝设置授权
console.log(res)
failedCallback('没有权限,保存失败')
}
})
} else { // 用户拒绝设置
failedCallback('没有权限,保存失败')
}
}
})
} else {
failedCallback(err && err.errMsg || '保存失败')
}
}
})
}
},
})
}
通过 wx.authorize() 来申请权限的方式是比较繁琐的。因为它的状态比较多,大致可分为:
- 用户未接受或拒绝过此权限,会弹窗询问用户,用户点击同意 —— 可调用相应接口。
- 用户未接受或拒绝过此权限,会弹窗询问用户,用户点击拒绝 —— 打开设置页面。
- 如果用户已授权 —— 可调用相应接口。
- 用户已拒绝授权 —— 打开设置页面。
上述情况的2/4是需要小伙伴们结合 wx.openSetting() 来帮助用户进行二次授权的。
搞定了用户授权这个麻烦事后,下面就是定义我们的 save 保存函数了,这个就很简单了:
let save = function () {
wx.saveImageToPhotosAlbum({
filePath: imgPath,
success() {
succCallback()
},
fail(res){
failedCallback(res)
}
})
}
把这段代码添加到 saveImgToPhotos 方法中就ok了,调用 wx.saveImageToPhotosAlbum 方法,参数就是我们传进来的图片地址,成功的话就执行成功的回调,失败就执行失败的回调。
下面是官方的文档说明:
三、调用流程
下面我们就把组件,方法这些东西引入到我们的页面中。在页面的 json 文件中引入组件路径。
把组件引入页面wxml中:
<share-sheet id="share-sheet" catch:genPlayBill="genTimelineImage" />
这里的方法就是点击生成海报的时候调用的父组件的方法。
当点击分享的时候展示分享组件:
openShareSheet(e) {
this.selectComponent("#share-sheet").openShareSheet()
},
在这里通过选择器可以直接调用子组件中的方法,来控制分享组件的显示与隐藏。
genTimelineImage(e) {
wx.showLoading({
title: "海报生成中",
icon: "loading",
mask: true,
})
const imgSrc = `路径`
wx.getImageInfo({
src: imgSrc,
})
.then((res) => {
wx.hideLoading()
this.imgTempPath = res.path
this.saveTimelineImg()
})
.catch((err) => console.log(err))
},
saveTimelineImg: function () {
saveImgToPhotos(this.imgTempPath, function(){
this.selectComponent("#share-sheet").closeShareSheet()
}.bind(this), function(errMsg) {
this.selectComponent("#share-sheet").closeShareSheet()
}.bind(this))
},
当我们点击生成海报调用 genTimelineImage 方法的时候,通过 wx.getImageInfo 方法读取想要保存图片的临时下载路径,把他保存到外部定义的一个变量中,这样方便我们在 utils 目录中定义的 saveImgToPhotos 方法调用。这样我们整个的流程就over啦!