微信小程序之如何保存小程序码

本文最后更新于:2020年9月17日 上午

兵来将挡,水来土掩

最近搞了点微信小程序的东西,其中一个需求就是要把云函数生成的小程序码,保存到本地相册,便于分享。

思路

关于云函数生成小程序码

云函数具体怎么用就不说了,官方文档上一看就明白了。我们这里需要用到的是 wxacode.getUnlimited 这个获取小程序码的接口。文档上说明,这个接口调用成功之后,返回的小程序码数据是 Buffer形式的。

按照我们要实现的需求来说,生成小程序码之后,肯定会有如下的两个操作:

  • 弹窗显示生成的小程序码
  • 保存小程序码到相册

wx.arrayBufferToBase64(ArrayBuffer arrayBuffer)

微信是为我们提供了 arrayBufferbase64 的方法的:

const src = `data:image/png;base64,${wx.arrayBufferToBase64(buffer)}`;

转化之后,直接用 image 组件显示图片就可以了。但是要保存到相册,base64 肯定是不行的,该怎么办呢?

wx.saveImageToPhotosAlbum(Object object)

wx.saveImageToPhotosAlbum({
	filePath: tempFilePath, // 图片文件路径,可以是临时文件路径或永久文件路径 (本地路径) ,不支持网络路径
	success: () => {
		wx.showToast({
			title: '保存成功,从相册中分享给朋友吧',
			icon: 'none',
			duration: 3000,
		});
	},
	fail: () => {
		wx.showToast({
			title: '图片保存失败,请稍候重试',
			icon: 'none',
			duration: 2000,
		});
	},
});

文档上说的很明白,我们首先要获取到图片的临时或永久文件路径,那就再一步步找文档。。。

[wx.canvasToTempFilePath(Object object, Object this)](wx.canvasToTempFilePath(Object object, Object this))

把当前画布指定区域的内容导出生成指定大小的图片

canvas,没错,这正是我们需要的,那么我们就可以梳理出整个保存图片的流程了:

  • 调用云函数,获取到小程序 buffer数据
  • 将图片和一些文案描述(如果需要的话)画在画布上
  • canvas导出文件临时路径
  • 保存图片

源码

base64src.js

工具方法,主要是先把小程序码的 buffer 数据写入到本地文件

这样既可以直接用 image 显示,又可以直接画在 canvas中,一举两得

const fileManager = wx.getFileSystemManager();
const BASE_FILE_NAME = 'tmp_base64src';

export default (bufferData) => {
	return new Promise((resolve, reject) => {
		const filePath = `${wx.env.USER_DATA_PATH}/${BASE_FILE_NAME}.png`;
		fileManager.writeFile({
			filePath,
			data: bufferData,
			encoding: 'binary',
			success: () => {
				resolve(filePath);
			},
			fail: () => {
				reject(new Error('WRITE BASE64SRC ERROR'));
			},
		});
	});
};

生成小程序码

wx.cloud.callFunction({
	name: 'code',
	data: {
		/* your data */
	},
	success: (res) => {
		console.log('[云函数] [code] 调用成功');
		const {
			result: { buffer },
		} = res;
		base64Src(buffer).then((src) => {
			// 获取到小程序码对应的本地路径
			console.log('src', src);
		});
	},
	fail: (err) => {
		console.error('[云函数] [code] 调用失败');
	},
	complete: () => {
		wx.hideLoading();
	},
});

canvas绘制图片和文字

if (!this.ctx) {
	this.ctx = wx.createCanvasContext('miniCanvas', this);
}

this.ctx.setFillStyle('#fff');
this.ctx.fillRect(0, 0, 200, 210);
// this.properties.src 对应上一步生成的本地路径
this.ctx.drawImage(this.properties.src, 25, 20, 150, 150);

this.ctx.save();
this.ctx.setFontSize(16);
this.ctx.setTextAlign('left');
this.ctx.setFillStyle('#333333');
// 绘制多行文字
// 这里指定每行文字高 30
let lineWidth = 0;
let lastSubStrIndex = 0;
let startY = 190;
const startX = 25;
const wordsHeight = 30;
const { description } = this.properties;
for (let i = 0; i < description.length; i++) {
	lineWidth += this.ctx.measureText(description[i]).width;
	if (lineWidth > 150) {
		this.ctx.fillText(
			description.substring(lastSubStrIndex, i),
			startX,
			startY,
		);
		startY += wordsHeight;
		lineWidth = 0;
		lastSubStrIndex = i;
	}
	if (i === description.length - 1) {
		this.ctx.fillText(
			description.substring(lastSubStrIndex, i + 1),
			startX,
			startY,
		);
	}
}
this.ctx.restore();
this.ctx.draw();

保存相册

const scale = app.globalData.pixelRatio;
wx.canvasToTempFilePath(
	{
		x: 0,
		y: 0,
		width: 200,
		height: 210,
		destWidth: 200 * scale,
		destHeight: 210 * scale,
		canvasId: 'miniCanvas',
		success: (res) => {
			console.log('onSave -> res.tempFilePath', res.tempFilePath);
			// 获取到临时路径,可以保存相册了
			wx.saveImageToPhotosAlbum(
				{
					filePath: res.tempFilePath,
					success: () => {
						wx.showToast({
							title: '保存成功,从相册中分享给朋友吧',
							icon: 'none',
							duration: 3000,
						});
					},
					fail: () => {
						wx.showToast({
							title: '图片保存失败,请稍候重试',
							icon: 'none',
							duration: 2000,
						});
					},
				},
				this,
			);
		},
		fail: () => {
			wx.showToast({
				title: '图片保存失败,请稍候重试',
				icon: 'none',
				duration: 2000,
			});
		},
	},
	this,
);

其他问题

因为保存系统相册是需要对应的权限的,第一次调用保存相册的时候,系统会自动弹出获取权限的弹窗,这里就存在两种情况:

  • 如果用户点击了同意,那么之后都会有权限了,这是正常情况
  • 如果点击了拒绝,那么下次用户再次点击保存相册的时候,系统不会再次弹出获取权限的弹窗,这种情况需要我们来额外处理,比如自定义一个权限弹窗,引导用户去打开权限
// 相册权限判断
wx.getSetting({
	success: (res) => {
		if (res.authSetting['scope.writePhotosAlbum']) {
			// 如果存在授权,可直接调用 canvas 绘制及保存相册的方法
			return;
		}
		// 不存在授权,提前授权
		wx.authorize({
			scope: 'scope.writePhotosAlbum',
			success: () => {
				// 授权成功,可直接调用 canvas 绘制及保存相册的方法
			},
			fail: () => {
				// 对应相册授权被拒绝后的情况
				// 自定义授权弹窗,引导去授权
			},
		});
	},
});

// wxml 简易版授权弹窗
<view class='minicode-dialog'>
	<view class='minicode-dialog-header'>尚未获取保存相册的权限,请先授权吧</view>
	<view class='minicode-dialog-footer'>
		<button
			class='minicode-dialog-action'
			open-type='openSetting'
			bindtap='onConfirm'>
			授权
		</button>
		<button class='minicode-dialog-action' bindtap='onCancel'>
			取消
		</button>
	</view>
</view>;

总结

总体来说,需求并不复杂,但由于第一次接触,也花费了一些时间。大部分时间都是在查找微信文档以及根据文档的内容来制定解决方案,果然还是带着需求的目的去学习是最快的,吹皮的资本又多了一个,哥也是搞过小程序的人~~~


 目录