环境:在小程序 taro 中 webview h5 页面中
解决问题:
小程序 taro 嵌入的 h5 页面,实现海报生成和下载功能
思路:
- 使用 html2canvas 将 dom 元素转成 base64 数据
- 海报 将 base64 数据传递给小程序的一个下载页面
- 在小程序对应的下载海报
生成海报
// 如果存在截图对象
if (screenshot.current) {
// 设置加载状态为true
setLoading(true);
// 使用html2canvas获取截图
html2canvas(screenshot.current, {
useCORS: true, // 开启跨域配置
allowTaint: true, // 允许跨域图片
scale: 4, // 缩放倍数
scrollY: 0, // 纵向偏移量 写死0 可以避免滚动造成偶尔偏移的现象
})
.then((canvas) => {
// 将canvas转换为Blob
canvas.toBlob((blob) => {
if (blob) {
// 停止加载状态
setLoading(false);
// 将Blob转换为Base64格式
getBase64(blob).then((data: any) => {
// 异步导入weixin-js-sdk库
import("weixin-js-sdk").then((wx) => {
// 打印微信JS-SDK对象以便调试
// console.log("wx", wx, wx.miniProgram);
// 使用微信小程序JS-SDK跳转到指定页面
wx.miniProgram.navigateTo({
url: `/pages/download/index?type=base64&url=${data}&page=poster`,
});
});
});
}
});
})
.finally(() => {
// 无论成功还是失败,都将加载状态设为false
setLoading(false);
});
}
Taro 小程序下载 (这里安卓下载没有问题,ios 点击不能下载)
import Taro from "@tarojs/taro";
export const downloadBase64 = (base64Image) => {
// 将 base64 图片数据转换为临时文件路径
const path = `${Taro.env.USER_DATA_PATH}/image.png`;
Taro.getFileSystemManager().writeFile({
filePath: path,
data: base64Image.replace(/^data:image/\w+;base64,/, ""),
encoding: "base64",
success: (res) => {
// console.log("res", res);
// 保存图片到相册
Taro.saveImageToPhotosAlbum({
filePath: path,
success: (saveRes) => {
// console.log("图片保存成功", saveRes);
Taro.showToast({
title: "图片已保存到相册",
icon: "success",
});
},
fail: (saveError) => {
console.error("保存图片失败", saveError);
Taro.showToast({
title: "保存失败",
icon: "none",
});
},
});
},
fail: (writeError) => {
console.error("写入文件失败", writeError);
Taro.showToast({
title: "保存失败",
icon: "none",
});
},
});
};
处理微信小程序的文件写入时,确实有一些坑需要注意,特别是在 iOS 端可能会涉及到 base64 编码的问题。
方案是将数据类型从 base64 改为 binary,这是一个有效的调整。 下面是实现代码
/**
* 将 base64 图片数据转换为文件并保存到本地相册
* @param {string} base64data - 包含图像信息的base64字符串
* @param {function} callback - 回调函数,接收保存后的文件路径或错误信息
*/
const base64ToBufferAndSave = (
base64data: string,
callback: (path: string | null, err: Error | null) => void
) => {
// 使用正则表达式提取图像格式和主体数据
let [, format, bodyData] =
/data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
// 如果未找到格式,返回错误
if (!format) {
callback(null, new Error("ERROR_BASE64SRC_PARSE"));
return;
}
// 生成基于当前时间戳的唯一文件名
const FILE_BASE_NAME = new Date().getTime();
// 构建文件路径,使用小程序的 USER_DATA_PATH
const filePath = `${Taro.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`;
// 将 base64 数据转换为 ArrayBuffer
const buffer = Taro.base64ToArrayBuffer(bodyData);
// 获取文件系统管理器
const fsm = Taro.getFileSystemManager();
// 将数据写入指定的文件路径
fsm.writeFile({
filePath,
data: buffer,
encoding: "binary",
success() {
// 如果写入成功,调用回调并传递文件路径
callback(filePath, null);
},
fail() {
// 如果写入失败,调用回调并传递错误信息
callback(null, new Error("ERROR_BASE64SRC_WRITE"));
},
});
}
export const downloadBase64 = (base64Image) => {
// 假设 base64Image 是包含图像信息的 base64 字符串
base64ToBufferAndSave(
base64Image,
(localUrl, err) => {
// 将保存后的文件路径传递给回调函数
if (localUrl) {
Taro.saveImageToPhotosAlbum({
filePath: localUrl,
success: (res2) => {
// 如果保存成功,显示保存成功的提示
Taro.showToast({
title: "保存海报成功",
icon: "none",
duration: 2000,
});
},
fail: (err) => {
// 如果保存失败,显示保存失败的提示
Taro.showToast({
title: "保存失败",
icon: "none",
duration: 2000,
});
},
});
} else {
console.log(err);
}
},
);
};