uniapp小程序分享使用canvas自定义绘制 vue3
使用混入结合canvas做小程序的分享
在混入里面定义一个全局共享的分享样式,在遇到特殊页面需要单独处理
utils/share.js
import { ref } from 'vue';
export default {
onShow() {
// 创建时设置统一页面的默认值
uni.$mpShare = {
title: '分享的标题',
path: '/pages/home/home',
imageUrl: 'https:xxx',
success: function (res) {
// 转发成功之后的回调
console.log('转发成功之后的回调', res);
if (res.errMsg == 'shareAppMessage:ok') {}
},
fail: function () {
// 转发失败之后的回调
console.log('转发失败之后的回调', res);
if (res.errMsg == 'shareAppMessage:fail cancel') {
// 用户取消转发
} else if (res.errMsg == 'shareAppMessage:fail') {
// 转发失败,其中 detail message 为详细失败信息
}
},
};
},
//发送给朋友
onShareAppMessage(res) {
console.log(' uni.$mpShare-----------', uni.$mpShare);
return uni.$mpShare;
},
//分享到朋友圈
onShareTimeline(res) {
return uni.$mpShare;
},
};
main.js混入
import share from '/utils/share.js';
export function createApp() {
const app = createSSRApp(App);
app.use(Pinia.createPinia());
app.mixin(share); // 这里
app.use(uviewPlus);
return {
app,
Pinia,
};
}
Canvas绘制海报
commonShare.js
/**
* canvas
* context
* currentObj 绘制需要用到的对象
* bgImg 背景图
* dpr 设备分辨率
* type 类型
*/
export async function createPoster(canvas, context, currentObj, bgImg, dpr, type) {
const bgImage = await renderImg(canvas, bgImg);
//绘制矩形
context.drawImage(bgImage, 0, 0, canvas.width / dpr, canvas.height / dpr);
context.fillStyle = '#FFF';
context.strokeStyle = '#303030';
context.lineWidth = 2;
context.strokeRect(16, 100, 343, 180);
context.fillRect(16, 100, 343, 180);
switch (type) {
//周边商品
case 'periphery':
//绘制商品图片
const productPath = showPicture(currentObj.goodsPictures[0].picture);
const productImg = await renderImg(canvas, productPath);
context.drawImage(productImg, 28, 112, 150, 157);
//绘制商品名称
context.font = 'normal normal bold 19px SourceHanSansCN-Bold';
context.fillStyle = '#0D1932';
context.fillText(currentObj.goodsName, 189, 136);
//绘制¥
context.font = 'normal normal bold 25px SourceHanSansCN-Bold';
context.fillStyle = '#FE2A12';
context.fillText(`¥${currentObj.giftPrice}.${currentObj.giftPricePrefix}起`, 189, 256);
break;
}
let path = await generatePath(context);
console.log('分享图片', path);
return path;
}
//图片显示
function showPicture(srcPath) {
if (srcPath.includes('http')) {
return srcPath;
} else {
return 'https://xxxxxx' + srcPath; // 注意路径
}
}
//创建图片对象
function renderImg(canvas, url) {
// url传临时路径
return new Promise((resolve) => {
const img = canvas.createImage();
img.src = `${url}?timestamp=${new Date().getTime()}`;
img.onload = () => {
resolve(img);
};
});
}
function base64src(base64data, fun) {
const base64 = base64data; //base64格式图片
const time = new Date().getTime();
const imgPath = uni.env.USER_DATA_PATH + '/poster' + time + 'share' + '.png';
//如果图片字符串不含要清空的前缀,可以不执行下行代码.
const imageData = base64.replace(/^data:image\/\w+;base64,/, '');
const file = uni.getFileSystemManager();
file.writeFileSync(imgPath, imageData, 'base64');
fun(imgPath);
}
async function generatePath(context) {
let base64IMG = context.canvas.toDataURL();
let pathImg = '';
await base64src(base64IMG, (res) => {
pathImg = res;
});
return pathImg;
}
Canvas.createImage() 创建Image对象onload事件在安卓真机下只会触发一次,
确保每次加载的 URL 是唯一的:通过在 URL 后面添加一个时间戳或随机数来避免缓存问题。
img = `${url}?timestamp=${new Date().getTime()}`;
单独页面的处理xxxx.vue
<view id="canvas" v-show="false">
<canvas id="myCanvas" type="2d" canvas-id="myCanvas" style="width: 375px; height: 300px"></canvas>
</view>
<script setup>
//引入分享绘制canvas
import { createPoster } from '/utils/commonShare.js';
onShow(() => {
generatorCanvasUrl();
});
function generatorCanvasUrl() {
uni
.createSelectorQuery()
.select('#myCanvas')
.node((res) => {
let canvas = res.node;
let context = res.node.getContext('2d');
uni.getSystemInfo({
success: async function (sysRes) {
const dpr = sysRes.devicePixelRatio;
canvas.width = 375 * dpr;
canvas.height = 300 * dpr;
context.clearRect(0, 0, canvas.width, canvas.height);
context.scale(dpr, dpr);
let path = await createPoster(canvas, context, currentObj.value, canvasBgImg.value, dpr, canvasType.value);
uni.$mpShare = {
title: canvasTitle.value,
path: '/pagesDiscover/group/group-product-dedails?from=sharePage&data=' + encodeURIComponent(JSON.stringify(currentObj.value)),
imageUrl: path,
};
console.log('重绘成功');
},
});
})
.exec();
}
</script>
注意几个问题
- canvas的绘制时间,需要在拿到数据之后,dom生成之前绘制完成,onShow不可以就试试onReady,否则就会绘制失败(图片找不到,数据undefined)
- 图片路径后面一定要加时间戳保证图片路径是唯一的,否则就会一直走缓存
- 如果页面想要重置分享但是不用canvas绘制,直接用下面这个
const shareData = computed(() => {
// 分享的数据
return {
title: '重置',
desc: '重置',
path: '/xxxx/xxxx/xxxx',
imageUrl: 'xxxxx',
};
});
onShow(() => {
uni.$mpShare = shareData.value; // 修改uni.$mpShare的值
});