近一段时间的主要精力一直在微信小程序的开发上,常规业务逻辑的难度不大,文档和社区内容都比较丰富,没有遇到太多技术难题,但我们的需求中有比较大的一块就是绘图——通过 canvas
绘制图片提供给用户进行后续的操作,在这一过程中踩了很多坑,在此及后续的文章中做逐一总结,供交流探讨更优方案。
本文中主要探讨的是关于图片资源的绘制,常规的网络图片绘制没有什么困难,拿到图片URL与宽高后与所需的位置信息,一并作为参数调用drawImage
方法即可,但前两天遇到了一个特殊的场景,服务端综合考虑后无法提供图片的URL,只能将图片进行Base64转码后返回给前端,绘图只能通过drawImage
方法,而这个方法只能传入图片的资源地址,不接受Base64数据:
CanvasContext.drawImage(string imageResource, number sx, number sy, number sWidth, number sHeight, number dx, number dy, number dWidth, number dHeight)
我们一起来分析一下imageResource
参数,这个指的是图片的资源地址,资源既可以指网络图片资源,亦可以指本地的图片资源,所以突破口正是在这里——本地图片资源地址。大致思路就是将Base64字符串转成ArrayBuffer格式数据,然后写入文件系统得到本地的资源地址,进而就可以调用上述方法进行绘制了。说到这里,就不得不介绍一下小程序的文件系统了,这块在我们常规的业务开发中使用的场景不多,文件系统是小程序提供的一套以小程序和用户维度隔离的存储以及一套相应的管理接口。通过wx.getFileSystemManager()
可以获取到全局唯一的文件系统管理器,所有文件系统的管理操作通过 FileSystemManager
来调用。这里所讲的文件主要分为两大类:
- 代码包文件:代码包文件指的是在项目目录中添加的文件。
- 本地文件:通过调用接口本地产生,或通过网络下载下来,存储到本地的文件。
显然,这里我们要用的是本地文件,在手机终端使用小程序时,会有一块独立的文件存储区域,以用户维度隔离,即同一台手机,每个微信用户不能访问到其他登录用的文件,同一个用户不同appid之间的文件也不能互相访问:
本地文件的文件路径以这样的格式呈现: {{协议名}}://文件路径,其中协议名开发者工具中为"http",而在手机端为"xfile"
复制代码
这里还要拓展一下,本地文件还可以细分为三种,即
- 本地临时文件
- 本地缓存文件
- 本地用户文件
其中前两种类型只能调用特定的接口产生,不能直接写入内容,所以我们要用的不是这两种,而是第三种——本地用户文件,这是在1.7.0版本中新增的能力,在本地提供了一个用户文件目录,可以供开发者进行自由的读写操作,通过wx.env.USER_DATA_PATH
可以获得这个目录的路径。举个?
//在本地用户文件目录下创建一个文件 wd.txt,写入内容 “豌豆公主前端研发”
const fs = wx.getFileSystemManager();
const filePath = `${wx.env.USER_DATA_PATH}/wd.txt`;
fs.writeFile({
filePath: filePath,
data: '豌豆公主前端研发',
encoding: 'utf8',
success() {
return filePath;
},
fail() {
return (new Error('ERROR_WRITE'));
},
});
复制代码
这样我们实现绘制Base64图片的关键点就解决了,还有一个小问题就是将Base64的字符串转成ArrayBuffer格式的数据,这个就比较简单了,有现成的API支持wx.base64ToArrayBuffer()
。
综上,小程序端绘制Base64图片的实现方案就已经有了,废话不多说,上代码:
const fs = wx.getFileSystemManager();
const FILE_BASE_NAME = 'tmp_base64imgsrc';
function base64ToSrc(base64Data, callback) {
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64Data) || [];
if (!format) {
return (new Error('ERROR_PARSE'));
}
const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME+Date.parse(new Date())}.${format}`;
const buffer = wx.base64ToArrayBuffer(bodyData);
fs.writeFile({
filePath,
data: buffer,
encoding: 'binary',
success() {
callback(filePath);
},
fail() {
return (new Error('ERROR_WRITE'));
},
});
}
复制代码
以上方案不一定是最优解,且当抛砖引玉,如果大家有更好的实现方案也欢迎随时交流探讨~
说了这么多,这个功能到底是什么呢?这就是我们前段时间刚刚推出的一个产品,基于微信小程序的社交电商,当前为邀请制注册,如果大家想体验一下的话可以私聊我微信(xysz1991)索取邀请码,或者识别下方二维码完成一个团购任务即可自动注册为集客,自购省钱,分享还可以赚取佣金哦~
)