本代码片段使用插件《qs-canvas》,支持 Node、web、uni-app 的canvas绘图工具。
效果图
安装npm包
npm install qs-canvas -S
代码片段
// index.vue
<template>
<view>
<canvas
:id="poster.canvasId"
:canvas-id="poster.canvasId"
:style="{
position: 'fixed',
top:0,
left: '-99999px',
'display:': 'none',
height: poster.height + 'px',
width: poster.width + 'px',
}"
>
</canvas>
<button @click="saveImg">保存图片到本地</button>
</view>
</template>
<script setup>
import { reactive, ref, computed, onMounted, getCurrentInstance } from 'vue';
import { getQrCode } from './api/index';
import useCanvas from './useCanvas'
const base64 = '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'
const vm = getCurrentInstance();
const state = reactive({
bgImg: 'https://fakeimg.pl/299x429/', // 海报背景图
qrCodeImg: '', // 二维码,base64
img: '', // 本地临时地址,canvas转换获取
})
const poster = reactive({
type: 'invite',
canvasId: 'myCanvas',
width: 299,
height: 429,
src: '',
});
// 获取二维码数据,接口返回base64
const getQrCode = async () => {
// const res = await qrCodeImg()
// state.qrCodeImg = 'data:image/png;base64,' + res.data;
state.qrCodeImg = `data:image/png;base64,${base64}`
}
// 绘制海报图
const drawPhotos = () => {
poster.src = '';
poster.data = {
bgImg: state.bgImg,
qrCode: state.qrCodeImg,
hint: '好用就点个关注呗'
};
// #ifdef APP-PLUS
poster.canvasId = 'canvasId-' + new Date().getTime();
// #endif
await useCanvas(poster, vm)
// 将base64转换成临时本地地址
uni.canvasToTempFilePath({
canvasId: poster.canvasId,
success: (res) => {
state.img = res.tempFilePath;
},
});
}
const saveImg = () => {
uni.saveImageToPhotosAlbum({
filePath: state.img,
success(res) {
console.log('保存成功');
},
})
}
onMounted(() => {
getQrCode()
})
</script>
// useCanvas.js
/**
* qs-canvas 绘制海报
* @version 1.0.0
* @param {Object} options - 海报参数
* @param {Object} vm - 自定义组件实例
*/
import QSCanvas from 'qs-canvas';
import { getPosterData } from './poster';
export default async function useCanvas(options, vm) {
const width = options.width;
const qsc = new QSCanvas(
{
canvasId: options.canvasId,
width: options.width,
height: options.height,
setCanvasWH: (canvas) => {
options.height = canvas.height;
},
},
vm,
);
let drawer = getPosterData(options);
// 绘制背景图
const background = await qsc.drawImg({
type: 'image',
val: drawer.background,
x: 0,
y: 0,
width,
mode: 'widthFix',
zIndex: 0,
});
await qsc.updateCanvasWH({
width: background.width,
height: background.bottom,
});
let list = drawer.list;
for (let i = 0; i < list.length; i++) {
let item = list[i];
// 绘制文字
if (item.type === 'text') {
await qsc.drawText(item);
}
// 绘制图片
if (item.type === 'image') {
if (item.d) {
qsc.setCircle({
x: item.x,
y: item.y,
d: item.d,
clip: true,
});
}
if (item.r) {
qsc.setRect({
x: item.x,
y: item.y,
height: item.height,
width: item.width,
r: item.r,
clip: true,
});
}
await qsc.drawImg(item);
qsc.restore();
}
// 绘制二维码
if (item.type === 'qrcode') {
await qsc.drawQrCode(item);
}
}
await qsc.draw();
// 延迟执行, 防止不稳定
setTimeout(async () => {
options.src = await qsc.toImage();
}, 100);
return options;
}
// poster/index.js
const invite = (options) => {
const width = options.width;
// const userInfo = sheep.$store('user').userInfo;
const userInfo = {
avatar: 'https://fakeimg.pl/60x60/',
nickname: '1111'
}
return {
background: options.data?.bgImg,
list: [
{
name: 'avatar',
type: 'image',
val: userInfo.avatar,
x: width * 0.064,
y: width * 0.064,
width: 60,
height: 60,
d: 60,
},
{
name: 'nickname',
type: 'text',
val: userInfo.nickname,
x: width * 0.28,
y: width * 0.1,
paintbrushProps: {
fillStyle: '#333',
font: {
fontSize: 14
},
},
},
{
name: 'hint',
type: 'text',
val: options.data?.hint,
x: width * 0.28,
y: width * 0.18,
paintbrushProps: {
fillStyle: '#999',
font: {
fontSize: 10
},
},
},
{
name: 'wxacode',
type: 'image',
val: options.data?.qrCode,
x: width * 0.2,
y: width * 0.38,
width: width * 0.6,
height: width * 0.6,
// size: width * 0.6,
},
],
};
};
export function getPosterData(options) {
switch (options.type) {
case 'invite':
return invite(options);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。