场景:在Taro开发中,在商品/职位/文章的详情页需要转发生成一个png海报,如果在Web开发中,直接html+css写海报样式,Vue/Teact中通过Props传入详情信息就可以,然后定位到屏幕视口外,通过html2canvas生成和导出海报样式,但是在小程序中,无法使用html2canvas
尝试寻找第三方库
wxml2canvas/taro-wxml2canvas可以吗,我使用过,证明是不可行的,且不说taro导入第三方原生组件十分麻烦,在Taro4中,和Taro3,Taro2基本经历过多次更新,这些库已经很久没有维护,实测不可行。
taro-plugin-canvas和taro3-canvas之类的库长时间不更新无法使用;
taro-html-parser和wxParse库也是长时间不更新无法使用;
mp-html这个还在维护库只支持uniapp和原生小程序;
似乎Taro被市场抛弃了,一个纯前端解决生成分享海报的库都没有吗?
尝试Canvas绘制
其实海报就是一个png图片,canvas一样可以绘制,Taro是支持canvas的,但canvas绘制的海报在多文字处理上非常麻烦,样式调整费劲,其实先写html,让AI转为canvas代码,也是还原度低,对于复杂海报样式很难实现,更难二次调整;
尝试SVG绘制
SVG是在浏览器上可行的,UI设计师出稿一个SVG模板,详情页信息(文字/图片)直接拼接/导出到SVG中,生成海报图片,但是小程序不支持SVG,所以依然不可行;
最终解决方案
使用Taro原生的Snapshot组件,只需要写原生的Taro代码,会被截图为画布,渲染结果导出成图片,需要局部开启Skyline模式,但是总算是实现了浏览器中Html2Canvas的效果,参考文档 docs.taro.zone/docs/apis/s…
参考代码
建议在详情页点击分享->生成分享海报按钮时,跳转该页面,生成完成直接开启分享菜单,结束后跳转回详情页。因为skyline模式的无法使用第三方UI组件,建议单独独立为一个局部页面,影响最小化。
sharePoster.tsx
import { Image, View, Text, Snapshot } from '@tarojs/components';
import Taro, { useLoad } from '@tarojs/taro';
import { useState } from 'react';
export default function SharePosterPage() {
const { t } = useTranslation();
const app = Taro.getApp();
const [positionInfo, setPositionInfo] = useState<PositionItem>(
{} as PositionItem,
);
const [miniAppCode, setMiniAppCode] = useState('');
useLoad(async () => {
// 通过路由传参
const globalPositionInfo = app.globalData.globalPositionInfo;
setPositionInfo(globalPositionInfo);
//获取小程序码/图片等,微信分享不允许二维码,使用小程序代替。
});
// 截图生成分享海报
const generateSnapshot = async (): Promise<string> => {
Taro.showLoading({ title: t('share.generating') });
const query = Taro.createSelectorQuery();
// 获取截图节点
//eslint-disable-next-line @typescript-eslint/no-explicit-any
const snapshot = await new Promise<any>((resolve) => {
query
.select('#mySnapshot')
.fields({ node: true, size: true })
.exec((res) => {
if (!res[0]) {
return;
}
resolve(res[0].node);
});
});
// 截图 -> 临时图片路径
const tempFilePath = await new Promise<string>((resolve, reject) => {
// 仅支持weapp,需开启Skyline
snapshot.takeSnapshot({
format: 'png',
success: (res) => resolve(res.tempFilePath),
fail: (err) => {
Taro.showToast({ title: t('share.generationFailed'), icon: 'none' });
reject(err);
},
});
});
Taro.hideLoading();
return tempFilePath;
};
// 生成海报 & 转发
const generateAndShare = async () => {
try {
const generatedImageUrl = await generateSnapshot();
await Taro.showShareImageMenu({
path: generatedImageUrl,
entrancePath: `/pages/position-detail/index?id=${positionInfo.id}`,
});
Taro.showToast({ title: t('share.shareSuccess'), icon: 'success' });
} catch (err) {
// handleError
} finally {
// 分享完成跳转回去
Taro.navigateBack();
}
};
return (
// 不能使用nutui组件,Skyline 渲染引擎 对 CSS 的支持有限制
<View className="snapshot-wrapper">
<Snapshot id="mySnapshot" mode="view">
<!-- 这里直接写原生的View代码 -->
</Snapshot>
</View>
);
}
需要在专门的分享页开启skyline
sharePoster.config.ts
export default definePageConfig({
navigationBarTitleText: '生成分享海报',
renderer: 'skyline',
// glass-easel 组件框架,配合使用 Skyline 组件
componentFramework: 'glass-easel',
// 自定义导航栏,配合使用 Skyline 组件
navigationStyle: 'custom',
});