2023 uniapp( vue3)使用canvas生成海报并保存,taro/微信小程序也适用

有段时间没写vue了,有点生疏了......

1、代码有注释,完整代码如下





1.1、工具函数 - canvas.ts

//utils/canvas.ts
const fs = uni.getFileSystemManager()

// 将Base64写入本地文件
const base64WriteFile = (filePath : string, data : string) => {
	return new Promise((resolve, reject) => {
		fs.writeFile({
			filePath,
			data,
			encoding: 'base64',
			success: (res) => {
				resolve(res);
			},
			fail: (err) => {
				reject(err);
			},
		});
	});
};
// 参数的类型校验
type GetImgBase64Type = {
	src : string;//图片地址(本地/在线地址)
	canvasWidth : number;//画布宽度
	filePath : string//临时路径
}
// 加载图片地址,生成base64并写入临时路径中
export const getImgBase64 = async (params : GetImgBase64Type) => {
	const { src, canvasWidth, filePath } = params
	try {
		// 获取图片信息:地址、宽高
		const imgInfo = await uni.getImageInfo({
			src,
		});
		// 计算图片在画布中的宽度
		const imageWidth = canvasWidth * 0.8;//随便定的,多少px都行
		// // 根据比例计算图片在画布中的高度
		const scaleFactor = Number((imageWidth / imgInfo.width).toFixed(2));
		// 根据比例计算图片高度
		const imageHeight = imgInfo.height * scaleFactor;
		// 生成base64
		const base64 : any = fs.readFileSync(imgInfo.path, 'base64')
		// 写入本地
		await base64WriteFile(filePath, base64)
		const currentImgInfo = await uni.getImageInfo({
			src: filePath,
		});
		return {
			imageWidth,
			imageHeight,
			imgUrl: currentImgInfo.path
		}
	} catch (err) {
		console.log('err', err);
	}

};

type DrawRoundedRectParamsType = {
	leftTop ?: boolean;
	leftBottom ?: boolean;
	rightTop ?: boolean;
	rightBottom ?: boolean;
	fillColor ?: string;
	r ?: number;
};
// canvas 绘制自定义圆角矩形
export const drawRoundedRect = (
	ctx : any,
	x : number,
	y : number,
	w : number,
	h : number,
	params ?: DrawRoundedRectParamsType,
) => {
	const {
		leftTop = false,
		leftBottom = false,
		rightTop = false,
		rightBottom = false,
		fillColor = 'transparent',
		r = 0,
	} = params || {};
	ctx.save(); // 保存当前绘图状态 防止虚线影响其他图形
	ctx.beginPath();
	ctx.setFillStyle(fillColor);
	ctx.setStrokeStyle('transparent');
	ctx.moveTo(x + r, y);
	// 绘制上边线和左上角圆弧
	if (leftTop) {
		ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
		ctx.lineTo(x, y);
	} else {
		ctx.moveTo(x, y + r);
		ctx.lineTo(x, y);
		ctx.lineTo(x + r, y);
	}
	ctx.lineTo(x + w - r, y);
	// 绘制上边线和右上角圆弧
	if (rightTop) {
		ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
	} else {
		ctx.lineTo(x + w - r, y);
		ctx.lineTo(x + w, y);
		ctx.lineTo(x + w, y + r);
	}

	ctx.lineTo(x + w, y + h - r);

	// 绘制下边线和右下角圆弧
	if (rightBottom) {
		ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
	} else {
		ctx.lineTo(x + w, y + h - r);
		ctx.lineTo(x + w, y + h);
		ctx.lineTo(x + w - r, y + h);
	}

	ctx.lineTo(x + r, y + h);

	// 绘制下边线和左下角圆弧
	if (leftBottom) {
		ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
	} else {
		ctx.lineTo(x + r, y + h);
		ctx.lineTo(x, y + h);
		ctx.lineTo(x, y + h - r);
	}

	ctx.lineTo(x, y + r);

	// 绘制左边线和左上角圆弧
	if (leftTop) {
		ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
		ctx.moveTo(x + r, y);
	} else {
		ctx.moveTo(x, y + r);
		ctx.lineTo(x, y);
		ctx.lineTo(x + r, y);
	}
	ctx.fill();
	ctx.closePath();
	ctx.stroke();
	ctx.restore(); // 恢复之前的绘图状态
};
type DrawTextConfigType = {
	ctx : any;
	fillStyle : string;//填充颜色
	fontSize : number//文字大小
	text : string;//在画布上输出的文本
	x : number;//绘制文本的左上角x坐标位置
	y : number//绘制文本的左上角y坐标位置
	center ?: boolean
}
// 绘制文本
export const drawText = (config : DrawTextConfigType) => {
	const { fillStyle, fontSize, x, y, text, ctx, center = false } = config
	ctx.setFillStyle(fillStyle);
	ctx.setFontSize(fontSize);
	if (center) {
		ctx.textAlign = 'center';//文字水平居中
	}
	ctx.fillText(text, x, y);
}
// 获取当前设备信息
export const getSystemInfo = () => {
	return new Promise((resolve) => {
		uni.getSystemInfo({
			success(res) {
				resolve(res)
			},
		})
	})
}

1.2、工具函数 - index.ts

//utils/index.ts
// 获取用户授权
type GetAuthorizeType = {
	title ?: string;//授权弹框描述
	callback ?: () => void//成功的回调
}
export const getAuthorize = (scope : string, params : GetAuthorizeType) => {
	const { title = '请开启授权', callback } = params
	return new Promise(() => {
		uni.authorize({
			scope,
			success: () => {
				callback?.()
			},
			fail: () => {
				// 如果用户点了拒绝,需要弹框提示再次授权
				uni.showModal({
					title,
					success() {
						uni.openSetting();
					},
				});
			}
		})
	})
}

1.3、图片列表函数

// ./utils/index.ts
export type ImageListType = {
	id : number;
	name : string
	desc : string
	imageSrc : string
	bgColor : string
	pageColor : string
}
export const imageList : ImageListType[] = [
	{
		id: 0,
		name: '那维莱特',
		desc: '潮水啊,我已归来!',
		imageSrc: '../../static/那维莱特.jpg',
		bgColor: '#b2d4ff',
		pageColor: '#d9e9ff',
	},
	{
		id: 1,
		name: '东方镜',
		desc: '太阳之下,诸世皆影!',
		imageSrc: '../../static/镜.jpg',
		bgColor: '#ffdecd',
		pageColor: '#fff3ed',
	},
	{
		id: 2,
		name: '魈',
		desc: '你去吧,我会在这里等你。',
		imageSrc: '../../static/魈.png',
		bgColor: '#f1ddff',
		pageColor: '#fbf4ff',
	},
	{
		id: 3,
		name: '琴团长',
		desc: '我以此剑起誓,必将胜利献给你!',
		imageSrc: '../../static/琴.jpg',
		bgColor: '#e6e4ff',
		pageColor: '#f7f6ff',
	},
]

2、效果如下

2023 uniapp( vue3)使用canvas生成海报并保存,taro/微信小程序也适用_第1张图片

2023 uniapp( vue3)使用canvas生成海报并保存,taro/微信小程序也适用_第2张图片

2023 uniapp( vue3)使用canvas生成海报并保存,taro/微信小程序也适用_第3张图片

2023 uniapp( vue3)使用canvas生成海报并保存,taro/微信小程序也适用_第4张图片

                                

2023 uniapp( vue3)使用canvas生成海报并保存,taro/微信小程序也适用_第5张图片

                

2023 uniapp( vue3)使用canvas生成海报并保存,taro/微信小程序也适用_第6张图片

2023 uniapp( vue3)使用canvas生成海报并保存,taro/微信小程序也适用_第7张图片

3、添加相册授权

根据各自框架添加授权即可,比如uniapp在manifest.json下

    "mp-weixin" : {
        "appid" : "你的微信appid",
        "setting" : {
            "urlCheck" : false
        },
        "usingComponents" : true,
         /* 授权 */
		"permission": {
			"scope.writePhotosAlbum": {
				"desc": "请授权保存到相册"
			}
		}
    },

4、项目地址

我的项目地址,点击跳转

5、问题总汇

5.1、为什么本地图片/在线图片真机不显示等?

将所有用到的图片转 base64 展示,参考上面工具函数中的 getImgBase64()

5.2、多文本如何换行?

参考下面地址  使用canvas画布时多行文本应该怎么换行? | 微信开放社区  

5.3、多次绘制出现白屏等?

比如以弹框的形式多次点击生成等情况,首先要确保每个canvas-idID的实例不能重复。可以参考我上面标题1中的代码。

5.4、当ctx.draw()后需要立马回去临时路径做 image预览时,画布生成的内容不全?

5.4.1、前提情景

由于 canvas 的层级比较高,做预览的时候会遮住其他的view等标签。而且样式或拖拽等也不好处理,花费时间肯定更多一点,这个时候需要用 代替 canvas 做展示。

5.4.2、解决

改写ctx.draw()为如下:

		ctx.draw(
				false,
				setTimeout(async () => {
					//在这里生成临时路径
					const { tempFilePath } = await uni.canvasToTempFilePath({
						canvasId: canvasId.value,
					});
					console.log('tempFilePath', tempFilePath);
					await uni.saveImageToPhotosAlbum({
						filePath: tempFilePath
					})
				}, 100),
			);

由于绘制可能需要更长的时间,通过延时器即可解决。

你可能感兴趣的:(uni-app,微信小程序,uni-app,taro,微信小程序,canvas)