瀏覽代碼

✨ feat:

zbb 5 天之前
父節點
當前提交
b3b5e45d01
共有 1 個文件被更改,包括 64 次插入27 次删除
  1. 64 27
      utils/poster.js

+ 64 - 27
utils/poster.js

@@ -27,16 +27,21 @@ const roundRect = (ctx, x, y, w, h, r) => {
  * 微信小程序 Canvas 2D 中不能用 uni.createImage()
  */
 const createCanvasImage = (canvas, src) => {
-	
 	return new Promise((resolve) => {
 		if (!src || !canvas) return resolve(null)
-		const img = canvas.createImage()
-		img.onload = () => resolve(img)
-		img.onerror = () => {
-			console.warn('图片加载失败:', src)
-			resolve(null)
+		try {
+			
+			const img = canvas.createImage()
+			img.onload = () => resolve(img)
+			img.onerror = () => {
+				console.warn('图片加载失败:', src)
+				resolve(null)
+			}
+			img.src = src
+		} catch (e) {
+			return resolve(null)
 		}
-		img.src = src
+
 	})
 }
 
@@ -44,7 +49,9 @@ const downloadImage = async (url) => {
 	try {
 		if (!url || !url.startsWith('http')) return url
 		// 使用 getImageInfo 获取图片本地临时路径(微信原生能力,通过 request 域名即可,无需 downloadFile 白名单)
-		const res = await uni.getImageInfo({ src: url })
+		const res = await uni.getImageInfo({
+			src: url
+		})
 		if (res && res.path) return res.path
 		return null
 	} catch (e) {
@@ -61,16 +68,24 @@ const resolveImageSrc = async (src) => {
 	if (src.startsWith('http')) {
 		try {
 			// getImageInfo 走 request 域名,无需额外配置 downloadFile 白名单
-			const res = await uni.getImageInfo({ src })
+			const res = await uni.getImageInfo({
+				src
+			})
 			if (res && res.path) return res.path
 			return null
-		} catch { return null }
+		} catch {
+			return null
+		}
 	}
 	if (src.startsWith('/') || src.startsWith('.')) {
 		try {
-			const res = await uni.getImageInfo({ src })
+			const res = await uni.getImageInfo({
+				src
+			})
 			return res.path
-		} catch { return null }
+		} catch {
+			return null
+		}
 	}
 	return src
 }
@@ -155,7 +170,8 @@ const fillTextWrap = (ctx, text, x, y, maxWidth, lineHeight, align = 'left') =>
 	ctx.textAlign = align
 	ctx.textBaseline = 'top'
 	const chars = text.split('')
-	let line = '', cy = y
+	let line = '',
+		cy = y
 	for (let i = 0; i < chars.length; i++) {
 		const test = line + chars[i]
 		if (ctx.measureText(test).width > maxWidth && i > 0) {
@@ -174,7 +190,10 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 	return new Promise((resolve, reject) => {
 		const query = uni.createSelectorQuery()
 		query.select('#posterCanvas')
-			.fields({ node: true, size: true })
+			.fields({
+				node: true,
+				size: true
+			})
 			.exec(async (res) => {
 				if (!res || !res[0]) {
 					reject(new Error('Canvas 节点未找到'))
@@ -198,13 +217,21 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 
 				// ===== 2. 装饰圆点 =====
 				ctx.fillStyle = 'rgba(255,255,255,0.10)'
-				ctx.beginPath(); ctx.arc(620, 160, 100, 0, Math.PI * 2); ctx.fill()
-				ctx.beginPath(); ctx.arc(680, 320, 60, 0, Math.PI * 2); ctx.fill()
-				ctx.beginPath(); ctx.arc(-20, 640, 80, 0, Math.PI * 2); ctx.fill()
+				ctx.beginPath();
+				ctx.arc(620, 160, 100, 0, Math.PI * 2);
+				ctx.fill()
+				ctx.beginPath();
+				ctx.arc(680, 320, 60, 0, Math.PI * 2);
+				ctx.fill()
+				ctx.beginPath();
+				ctx.arc(-20, 640, 80, 0, Math.PI * 2);
+				ctx.fill()
 
 				// ===== 3. 白色卡片(去掉白色背景,只保留圆角裁剪区域)=====
-				const cardX = 20, cardY = 60
-				const cardW = W - 40, cardH = 480
+				const cardX = 20,
+					cardY = 60
+				const cardW = W - 40,
+					cardH = 480
 				const innerPad = 16
 				const innerX = cardX + innerPad
 				const innerY = cardY + innerPad
@@ -213,7 +240,8 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 
 				// 背景图(静态资源直接传路径,canvas.createImage 支持加载本地包内资源)
 				try {
-					const cardBg = await createCanvasImage(canvas, '/static/image/home/usecard-bg.png')
+					const cardBg = await createCanvasImage(canvas,
+						'/static/image/home/usecard-bg.png')
 					if (cardBg) {
 						ctx.save()
 						roundRect(ctx, innerX, innerY, innerW, innerH, 16)
@@ -281,7 +309,8 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 				const companyName = cardInfo.companyName || '公司名称'
 				const companyX = innerX + 44
 				const companyY = nameY + 60
-				const companyEndY = fillTextWrap(ctx, companyName, companyX, companyY, companyMaxW, 36, 'left')
+				const companyEndY = fillTextWrap(ctx, companyName, companyX, companyY,
+					companyMaxW, 36, 'left')
 
 				// ===== 7. 联系方式 =====
 				const contactY = Math.max(companyEndY + 24, innerY + 160)
@@ -289,13 +318,18 @@ export const generateCardPoster = async (cardInfo = {}, qrInfo = {}) => {
 				const contactGap = 60
 
 				// 预加载联系方式图标
-				const phoneIcon = await createCanvasImage(canvas, '/static/image/public/phone-icon.png')
-				const emailIcon = await createCanvasImage(canvas, '/static/image/public/email-icon.png')
-				const addressIcon = await createCanvasImage(canvas, '/static/image/public/address-icon.png')
+				const phoneIcon = await createCanvasImage(canvas,
+					'/static/image/public/phone-icon.png')
+				const emailIcon = await createCanvasImage(canvas,
+					'/static/image/public/email-icon.png')
+				const addressIcon = await createCanvasImage(canvas,
+					'/static/image/public/address-icon.png')
 
 				drawContactRow(ctx, phoneIcon, cardInfo.phonenumber, contactX, contactY + 20)
-				drawContactRow(ctx, emailIcon, cardInfo.email, contactX, contactY + 20 + contactGap)
-				drawContactRow(ctx, addressIcon, cardInfo.companyAddress, contactX, contactY + 20 + contactGap * 2)
+				drawContactRow(ctx, emailIcon, cardInfo.email, contactX, contactY + 20 +
+					contactGap)
+				drawContactRow(ctx, addressIcon, cardInfo.companyAddress, contactX, contactY +
+					20 + contactGap * 2)
 
 				// ===== 8. 导出 =====
 				setTimeout(() => {
@@ -335,4 +369,7 @@ export const savePosterToAlbum = (tempFilePath) => {
 	})
 }
 
-export default { generateCardPoster, savePosterToAlbum }
+export default {
+	generateCardPoster,
+	savePosterToAlbum
+}