小程序分享模块超级详解

10,108 阅读11分钟

导语:在小程序项目开发中,分享能力几乎是每个项目必备的要求,但原生的分享能力比较有限,不够灵活,今天就我们就一起来研究下,如何在现有基础上,增强小程序分享的能力,使信息传递更加直观、灵活。

示例项目地址: github.com/ycvcb123/sh…

本文目录:

小程序分享基础 API 介绍

微信分享的 API 只提供了分享给微信好友的能力,并没有提供分享朋友圈的能力,这是为啥子呢!!!

从网上收集的一些咨询来看,主要有如下两点原因:

  1. 由于微商泛滥,公众号鸡汤泛滥,朋友圈质量已经有所下降,如果小程序再开放分享朋友圈功能,可能会进一步影响到整个微信生态,造成用户活跃度下降,用户流失等问题。
  2. 微信不让小程序在朋友圈转发,更多是保护朋友圈的”广告位”阵地,不能够让这块”肥肉”变成了公益设施。

其实一些童鞋应该留意到了在朋友圈,官方已经推了一些小程序的广告,只不过这项能力还没有完全放开,以后会不会放开先不讨论,智慧的开发小哥哥早已想到了通过生成带有小程序码的海报作为替代方案(撒花!!!),本文后面的部分也会说到,我们先回到正题。

onShareAppMessage -- 转发

用法:

Page({
    onShareAppMessage: function(){
         return {
            title: 'xxxxx', //自定义转发标题
            path: '/page/user?id=123', //分享页面路径
            imageUrl: '/common/images/xxx.png' //分享图片 宽高比 5:4
        }
    }
})
//如果只写成如下形式,title默认是小程序名,path为当前页面路径(不带参数),imageUrl为当前页面截图
Page({
    onShareAppMessage: function(){}
})

触发方法(一定要在page中先写入上述方法):

  1. 点击小程序的胶囊菜单,会从底部弹出转发选项。
  2. <button> 组件 open-type="share" 即 <button open-type="share">,点击后触发。

触发后效果如下:

观察上述结果,不难看出,title能分享出去的信息非常有限,那我们能不能对分享的图片做些文章,让它带出更多的信息呢?下面进入到我们的第二个部分 基于 canvas 动态绘制分享图片

基于 canvas 动态绘制分享图片

因为每个页面的信息很多都是通过接口返回或者用户输入产生,是在不断变化的,设计师所画的静态图片肯定是不足以去展示这些信息的,那么我们就要想,有没有一种办法,是可以把静态图片动态信息绘制在一起后在生成一张新的图片?答案是肯定的!!!

把图片和文字画在一起?我们就一定会想到神奇的 canvas,根据小程序画布相关的 API 绘制如下:

//创建画布(组件中一定要绑定this,切记!!!)
var ctx = wx.createCanvasContext('myCanvas', this);
//画布上绘制图片
ctx.drawImage(path, 0, 0, width, height);
//画布上绘制文字
ctx.setFillStyle('#fff');
ctx.setFontSize(32);
ctx.fillText(startTime, 24, 54);
//其他信息绘制
//... 
ctx.draw();

上述已经通过 canvas 把图片和文字绘制到了一起,那如何把这个画布转成一个图片,供开发者使用呢?

强大的小程序给我们提供了原生的方法: wx.canvasToTempFilePath

//在上面代码的draw()的回调中使用wx.canvasToTempFilePath
var that = this;
ctx.draw(true, () => {
    setTimeout(() => {
        wx.canvasToTempFilePath({
            canvasId: 'myCanvas',
            success: (res) => { 
                that.setData({
                    //res.tempFilePath 生成图片的临时路径
                    picUrl: res.tempFilePath
                });
               
                
            }
        }, that); //在组件中使用这里一定记得要绑定this,切记!!!
    }, 300); //此处加入300毫秒延时是为了解决小程序绘制过程中的渲染问题
});

把图片路径传递给<image>标签,得到下图结果。

<image src="{{picUrl}}"/>

同理:把 picUrl 赋值给 onShareAppMessage 中的 imageUrl,分享出去后的图片则带有了动态信息!

对不同来源图片的处理(本地图片, 网络图片 , base64图片 )

在上面的例子中,绘制本地图片时直接使用ctx.drawImage(path, 0, 0, width, height),path直接传入图片路径即可。但是如果是网络图片或者是base64的图片时,drawImage是无法直接绘制的,下面就介绍下针对上述两种情况如何做兼容处理。

  1. 网络来源图片
//将网络图片转换为本地路径
handleNetImg: function(imagePath) {
    var that = this;
    return new Promise((resolve, reject) => {
        wx.getImageInfo({
            src: imagePath,
            success: function(res) {
                resolve(res);
            }
        });
    });
}

handleNetImg('网络图片地址').then((res) => {
    console.log(res.path); //输出转换后的本地图片路径
    ctx.drawImage(res.path, 0, 0, width, height); //此时图片即可在画布上绘制出来
})
  1. base64 图片

使用 ctx.drawImage(base64Data, 0, 0, width, height) 在小程序开发者工具上是可以绘制的,然而!!!这个大骗纸!!!真机上是失效的!!!(心碎一分钟。。。)

跟上面类似的思路,我们把 base64的图片 转为本地的 png图片

var handleBase64Img = function() {
    //wx.getFileSystemManager 小程序文件管理器
    var fsm = wx.getFileSystemManager();
    var FILE_NAME = 'base64src';
    var base64src = function(base64data) {
        return new Promise((resolve, reject) => {
            //解析base64,提取出图片类型: imgtype,解析内容bodyData(去掉data:image/png;base64,以后的内容)
            var [, imgType, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
            if (!imgType) {
                reject(new Error('ERROR_BASE64SRC_PARSE'));
            }
            /**
             *wx.env.USER_DATA_PATH
             *本地用户文件
             *本地用户文件是从 1.7.0 版本开始新增的概念。提供了一个用户文件目录给开发者,开发者对这个目录有完全自由的读写权限。通过 wx.env.USER_DATA_PATH 可以获取到这个目录的路径。
             */
            var filePath = `${wx.env.USER_DATA_PATH}/${FILE_NAME}.${imgType}`;
            //按指定写入文件的字符编码encoding,向地址filepath,写入数据data。
            fsm.writeFile({
                filePath,
                data: bodyData,
                encoding: 'base64',
                success() {
                    resolve(filePath);
                },
                fail() {
                    reject(new Error('ERROR_BASE64SRC_WRITE'));
                },
            });
        });
    };
    return base64src;
}

var base64src = that.handleBase64Img();
var handleBase64src = base64src(base64data);
handleBase64src.then(res => {
    //res 即为base64 转化为图片后的本地路径,即可在画布上绘制成功
    that.ctx.drawImage(res, left, top, width, height);
});

通过上述的一些内容,我们已经知道如何利用 canvas 把图片和文字绘制在一起后生成一张新的图片,这里就产生了一个新的问题: 我们如何把生成的图片保存下来呢 ??? 我们接着往细看 canvas 生成图片后如何保存到本地

canvas 生成图片后如何保存到相册中

想要保存到相册中第一步首先当然是要获得访问相册的权限!!!

//访问相册授权
wx.getSetting({
    success: (res) => {
        //检查是否有访问相册的权限,如果没有则通过wx.authorize方法授权(授权只需要一次就好,后面就可以直接访问相册)
        if (!res.authSetting['scope.writePhotosAlbum']) {
            console.log('没有获取授权');
            wx.authorize({
                scope: 'scope.writePhotosAlbum',
                success: (res) => {
                    //用户点击允许获取相册信息后逻辑进入这里,如上图所示
                }
            })
        } 
    }
});

//获取了相册的访问权限,使用 wx.saveImageToPhotosAlbum 将图片保存到相册中
wx.saveImageToPhotosAlbum({
    filePath: that.data.sharePicPath,
    success: (res) => {
        //保存成功弹出提示,告知一下用户
        wx.showModal({
            title: '已保存到手机相册',
            content: '将图片发送到朋友圈,邀请好友加入',
            confirmColor: '#0bc183',
            confirmText: '知道了',
            showCancel: false
        })
    }
})

如何生成小程序码及验证小程序码所带信息

在小程序的分享朋友圈的解决方案中,往往在生成的海报页面中都会有一个小程序码,使得用户有进入小程序的入口,那么这个小程序码如何生成呢?

官方文档: 获取小程序码

注意:因为生成小程序码的接口参数需要 access_token,安全起见,一般都通过后台调用在拿到base64的数据在返回给前端。

我们在回到正题:

上述三种生成接口,大家根据情况按需使用,因为我的项目里,需要经常生成不同页面对应的小程序码,B类接口比较符合我的要求,这里就重点描述下B类接口的使用自测

B类接口入参,出参官方说明-- 接口 B:适用于需要的码数量极多的业务场景

重点看下 scene : 最大32个可见字符,有页面路径带参数的情况下要尤其注意!!!

page,scene 等参数传递给后台后,后台调用B类接口,返回给前端一个base64的图片数据,我们把这个数据绘制到海报上就好!!!

绘制方法上面已经说过 canvas 对不同来源图片的处理(本地图片, 网络图片 , base64图片 )

现在小程序码的图片已经生成了,那么我们要如何自测呢?怎么才能知道小程序码中所携带信息是否正确呢?

官方给出的方法:

上述方法在开发阶段是比较方便,但是在正式的提测阶段,此种方式显得有些牵强,有人想到真机调试?

官方接口只能生成已发布的小程序的二维码

也就是说,你扫码就连上生产环境了!!!没有办法调试,那到底怎么办呢???

解决办法就要借助强大的小程序开发者工具啦!!!

首先把生成的小程序码保存到电脑里,方法见上述 canvas 生成图片后如何保存到本地 部分。

然后通过开发者工具选择二维码编译模式,文件夹中选择带有小程序码的图片即可!!!

注意: 获取scene值时要 decodeURIComponent

Page({
  onLoad(query) {
    // scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
    const scene = decodeURIComponent(query.scene)
  }
})

抽离配置文件,使绘制更加灵活

我们观察如下一个小程序海报:

除了上图红框中的的内容会发生写变化以外,整体的结构大部分是基本已经固定了的,因为海报内容和业务是强相关的,如果我把绘制的逻辑写入组件里,那岂不是换个业务,我的组件就要改一次?这样失去了组件的通用型肯定是不行的,那怎么办呢?下面介绍一个业界比较好的解决方案:

小程序海报说白了就是由canvas画布上绘制的一些形状,图片,文字,线等等组成的,那我们是不是可以把这些绘制的基本能力封装成方法,通过设计稿量出海报上每个元素的位置大小等信息,当作一个配置文件传递给这些绘制方法,这样就保证组件的通用型,而且绘制信息抽离成一个配置文件也更加方便后期维护。

share-config.js:

function setShareInfo(time, start, end, imageSrc) {
    return {
        width: 750,
        height: 1300,
        background: '#F2FCF8',
        views: [{
                type: 'rect',
                parent: true,
                radius: true,
                radiusVal: 16,
                left: 40,
                width: 670,
                height: 1140,
                shadow: true,
                background: '#cacacd',
                shadowColor: 'rgba(0,0,0,.6)',
                line: true
            },
            //....
            {
                type: 'text',
                content: '长按或扫描二维码,查看这条线路',
                color: '#9B9BA1',
                top: 1052,
                left: 224,
                fontSize: 28,
                font: 'PingFangSC-Medium',
            },
            {
                type: 'image',
                path: '/common/images/station-flag.png',
                top: 746,
                left: 80,
                width: 32,
                height: 104
            }
        ]
    }
}

export {
    setShareInfo
}

只需要在几个动态信息改变时,传入这些变化的值即可。

import { setShareInfo } from '../../common/config/share-config';
page({
    onLoad: function() {
        this.setData({
            shareMessageInfoTimeline: setShareInfo('3月7号 下午16:30发车', '腾讯大厦', '平安国际中心', '')
        });
        //...
    },
});

通过 shareMessageInfoTimeline 获取配置信息

<view class="container">
    <mod-share-timeline timelineShow="{{timelineShow}}" picContent="{{shareMessageInfoTimeline}}" bindcloseTimelineShow="closeTimelineShow"/>
</view>

总结

本文对小程序分享所需的基础能力进行了拆分详解,把上述能力进行不同的组合,应该可以满足大部分的分享需求。

比如:

  1. 动态绘制分享给微信好友的分享图片。
  2. 生成并保存小程序海报。

通过本文可以掌握如下知识点:

我在github上基于上述基础能力写了一个示例demo方便参考和实战:

github.com/ycvcb123/sh…