Commit ff0e8c7f authored by spc's avatar spc

fixed

parent a889ca63
...@@ -110,10 +110,14 @@ const qrcodeImageUrl = ref('') // 二维码图片 URL ...@@ -110,10 +110,14 @@ const qrcodeImageUrl = ref('') // 二维码图片 URL
const isDownloading = ref(false) // 是否正在下载 const isDownloading = ref(false) // 是否正在下载
// Canvas 尺寸(海报尺寸:542rpx x 956rpx) // Canvas 尺寸(海报尺寸:542rpx x 956rpx)
// 使用 3 倍像素密度提高分辨率
const pixelRatio = 3
const systemInfo = uni.getSystemInfoSync() const systemInfo = uni.getSystemInfoSync()
const screenWidth = systemInfo.windowWidth || 375 const screenWidth = systemInfo.windowWidth || 375
const canvasWidth = (542 / 750) * screenWidth const baseCanvasWidth = (542 / 750) * screenWidth
const canvasHeight = (956 / 750) * screenWidth const baseCanvasHeight = (956 / 750) * screenWidth
const canvasWidth = baseCanvasWidth * pixelRatio
const canvasHeight = baseCanvasHeight * pixelRatio
// 监听 visible 变化,生成二维码 // 监听 visible 变化,生成二维码
watch(() => props.visible, (newVal) => { watch(() => props.visible, (newVal) => {
...@@ -221,9 +225,12 @@ const handleDownload = async () => { ...@@ -221,9 +225,12 @@ const handleDownload = async () => {
// 保存初始状态 // 保存初始状态
ctx.save() ctx.save()
// 海报尺寸(rpx 转 px) // 缩放 canvas 上下文以适应高分辨率
const posterWidth = canvasWidth ctx.scale(pixelRatio, pixelRatio)
const posterHeight = canvasHeight
// 海报尺寸(使用基础尺寸,因为已经缩放)
const posterWidth = baseCanvasWidth
const posterHeight = baseCanvasHeight
// 绘制背景 // 绘制背景
await drawImage(ctx, `${$baseUrl}homepage/Q3Res/xingmaLabPosterBg.png`, 0, 0, posterWidth, posterHeight) await drawImage(ctx, `${$baseUrl}homepage/Q3Res/xingmaLabPosterBg.png`, 0, 0, posterWidth, posterHeight)
...@@ -265,7 +272,10 @@ const handleDownload = async () => { ...@@ -265,7 +272,10 @@ const handleDownload = async () => {
ctx.setTextAlign('left') ctx.setTextAlign('left')
const nicknameTextX = nicknameBgX + (props.avatar ? (48 + 20) / 750 * screenWidth : 30 / 750 * screenWidth) const nicknameTextX = nicknameBgX + (props.avatar ? (48 + 20) / 750 * screenWidth : 30 / 750 * screenWidth)
const nicknameTextY = nicknameBgY + nicknameBgH / 2 + (28 / 750) * screenWidth / 3 const nicknameTextY = nicknameBgY + nicknameBgH / 2 + (28 / 750) * screenWidth / 3
ctx.fillText(nickname.value || '', nicknameTextX, nicknameTextY) // 计算昵称文字最大宽度(昵称背景宽度 - 左边距 - 右边距)
const nicknameMaxWidth = nicknameBgW - (props.avatar ? (48 + 20) / 750 * screenWidth : 30 / 750 * screenWidth) - (30 / 750 * screenWidth)
const truncatedNickname = truncateText(ctx, nickname.value || '', nicknameMaxWidth)
ctx.fillText(truncatedNickname, nicknameTextX, nicknameTextY)
// 绘制图片区域背景(居中) // 绘制图片区域背景(居中)
const imageBgW = (494 / 750) * screenWidth const imageBgW = (494 / 750) * screenWidth
...@@ -275,13 +285,13 @@ const handleDownload = async () => { ...@@ -275,13 +285,13 @@ const handleDownload = async () => {
await drawImage(ctx, `${$baseUrl}homepage/Q3Res/xingmaLabPosterImgBg.png`, imageBgX, imageBgY, imageBgW, imageBgH) await drawImage(ctx, `${$baseUrl}homepage/Q3Res/xingmaLabPosterImgBg.png`, imageBgX, imageBgY, imageBgW, imageBgH)
// 绘制主图片 // 绘制主图片(自适应模式,保持宽高比)
if (props.imageUrl) { if (props.imageUrl) {
const imageX = imageBgX + (26 / 750) * screenWidth const imageX = imageBgX + (26 / 750) * screenWidth
const imageY = imageBgY + (26 / 750) * screenWidth const imageY = imageBgY + (26 / 750) * screenWidth
const imageW = imageBgW - (52 / 750) * screenWidth const imageW = imageBgW - (52 / 750) * screenWidth
const imageH = (586 / 750) * screenWidth const imageH = (586 / 750) * screenWidth
await drawImage(ctx, props.imageUrl, imageX, imageY, imageW, imageH) await drawImage(ctx, props.imageUrl, imageX, imageY, imageW, imageH, true)
} }
// 绘制文字内容 // 绘制文字内容
...@@ -290,7 +300,10 @@ const handleDownload = async () => { ...@@ -290,7 +300,10 @@ const handleDownload = async () => {
ctx.setTextAlign('left') ctx.setTextAlign('left')
const contentX = imageBgX + (22 / 750) * screenWidth const contentX = imageBgX + (22 / 750) * screenWidth
const contentY = imageBgY + imageBgH - (146 / 750) * screenWidth const contentY = imageBgY + imageBgH - (146 / 750) * screenWidth
ctx.fillText(props.content || '', contentX, contentY) // 计算内容文字最大宽度(图片区域宽度 - 左右边距)
const contentMaxWidth = imageBgW - (44 / 750) * screenWidth
const truncatedContent = truncateText(ctx, props.content || '', contentMaxWidth)
ctx.fillText(truncatedContent, contentX, contentY)
// 绘制唯一藏品编号 // 绘制唯一藏品编号
ctx.setFillStyle('#D3A458') ctx.setFillStyle('#D3A458')
...@@ -329,13 +342,13 @@ const handleDownload = async () => { ...@@ -329,13 +342,13 @@ const handleDownload = async () => {
ctx.draw(false, async () => { ctx.draw(false, async () => {
await nextTick() await nextTick()
// 转换为临时文件 // 转换为临时文件(使用高分辨率)
uni.canvasToTempFilePath({ uni.canvasToTempFilePath({
canvasId: 'posterCanvas', canvasId: 'posterCanvas',
width: posterWidth, width: canvasWidth,
height: posterHeight, height: canvasHeight,
destWidth: posterWidth, destWidth: canvasWidth,
destHeight: posterHeight, destHeight: canvasHeight,
success: (res) => { success: (res) => {
// 保存到相册 // 保存到相册
uni.saveImageToPhotosAlbum({ uni.saveImageToPhotosAlbum({
...@@ -344,7 +357,7 @@ const handleDownload = async () => { ...@@ -344,7 +357,7 @@ const handleDownload = async () => {
uni.hideLoading() uni.hideLoading()
uni.showToast({ uni.showToast({
title: '保存成功', title: '保存成功',
icon: 'success' icon: 'none'
}) })
isDownloading.value = false isDownloading.value = false
}, },
...@@ -455,9 +468,163 @@ const base64ToTempFilePath = (base64Data) => { ...@@ -455,9 +468,163 @@ const base64ToTempFilePath = (base64Data) => {
}) })
} }
// 截断文字(canvas 中文字超出宽度时添加省略号)
const truncateText = (ctx, text, maxWidth) => {
if (!text) return ''
// 测量文字宽度
const textWidth = ctx.measureText(text).width
// 如果文字宽度小于等于最大宽度,直接返回
if (textWidth <= maxWidth) {
return text
}
// 如果超出,需要截断并添加省略号
const ellipsis = '...'
const ellipsisWidth = ctx.measureText(ellipsis).width
const availableWidth = maxWidth - ellipsisWidth
// 二分查找合适的截断位置
let left = 0
let right = text.length
let mid = 0
while (left < right) {
mid = Math.floor((left + right) / 2)
const testText = text.substring(0, mid)
const testWidth = ctx.measureText(testText).width
if (testWidth <= availableWidth) {
left = mid + 1
} else {
right = mid
}
}
// 确保至少保留一个字符
const truncatePos = Math.max(1, left - 1)
return text.substring(0, truncatePos) + ellipsis
}
// 获取图片信息(宽高)
const getImageInfo = (src) => {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: src,
success: (res) => {
resolve({
width: res.width,
height: res.height
})
},
fail: (err) => {
reject(err)
}
})
})
}
// 计算自适应尺寸(保持宽高比,类似 aspectFit)
const calculateFitSize = (imageWidth, imageHeight, containerWidth, containerHeight) => {
const imageRatio = imageWidth / imageHeight
const containerRatio = containerWidth / containerHeight
let drawWidth, drawHeight, drawX, drawY
if (imageRatio > containerRatio) {
// 图片更宽,以宽度为准
drawWidth = containerWidth
drawHeight = containerWidth / imageRatio
drawX = 0
drawY = (containerHeight - drawHeight) / 2
} else {
// 图片更高,以高度为准
drawWidth = containerHeight * imageRatio
drawHeight = containerHeight
drawX = (containerWidth - drawWidth) / 2
drawY = 0
}
return { drawWidth, drawHeight, drawX, drawY }
}
// 绘制图片到 canvas(异步加载图片) // 绘制图片到 canvas(异步加载图片)
const drawImage = (ctx, src, x, y, w, h) => { const drawImage = (ctx, src, x, y, w, h, fitMode = false) => {
return new Promise((resolve) => { return new Promise((resolve) => {
// 如果是自适应模式,需要先获取图片尺寸
if (fitMode) {
// 先获取图片路径(Base64 需要转换)
if (src.startsWith('data:image')) {
// Base64 图片:先转换为临时文件,再获取信息
base64ToTempFilePath(src).then((filePath) => {
// 获取图片信息
return getImageInfo(filePath).then((info) => {
const { drawWidth, drawHeight, drawX, drawY } = calculateFitSize(info.width, info.height, w, h)
ctx.drawImage(filePath, x + drawX, y + drawY, drawWidth, drawHeight)
resolve()
}).catch(() => {
// 如果获取图片信息失败,使用原始尺寸
ctx.drawImage(filePath, x, y, w, h)
resolve()
})
}).catch((err) => {
console.error('Base64 图片处理失败:', err)
// 如果转换失败,尝试直接使用(某些平台可能支持)
try {
ctx.drawImage(src, x, y, w, h)
resolve()
} catch (e) {
console.warn('无法绘制 Base64 图片,跳过')
resolve() // 即使失败也继续
}
})
return
} else {
// 普通图片 URL
getImageInfo(src).then((info) => {
const { drawWidth, drawHeight, drawX, drawY } = calculateFitSize(info.width, info.height, w, h)
// 先下载图片
uni.downloadFile({
url: src,
success: (res) => {
if (res.statusCode === 200) {
ctx.drawImage(res.tempFilePath, x + drawX, y + drawY, drawWidth, drawHeight)
resolve()
} else {
console.warn('图片下载失败:', src, 'statusCode:', res.statusCode)
resolve() // 即使失败也继续
}
},
fail: (err) => {
console.warn('图片下载失败:', src, err)
resolve() // 即使失败也继续
}
})
}).catch(() => {
// 如果获取图片信息失败,使用原始逻辑
uni.downloadFile({
url: src,
success: (res) => {
if (res.statusCode === 200) {
ctx.drawImage(res.tempFilePath, x, y, w, h)
resolve()
} else {
console.warn('图片下载失败:', src, 'statusCode:', res.statusCode)
resolve() // 即使失败也继续
}
},
fail: (err) => {
console.warn('图片下载失败:', src, err)
resolve() // 即使失败也继续
}
})
})
return
}
}
// 非自适应模式,使用原始逻辑
// 如果是 Base64 Data URI,先转换为临时文件 // 如果是 Base64 Data URI,先转换为临时文件
if (src.startsWith('data:image')) { if (src.startsWith('data:image')) {
base64ToTempFilePath(src).then((filePath) => { base64ToTempFilePath(src).then((filePath) => {
...@@ -569,16 +736,13 @@ const drawImage = (ctx, src, x, y, w, h) => { ...@@ -569,16 +736,13 @@ const drawImage = (ctx, src, x, y, w, h) => {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 256rpx; width: 186rpx;
height: 58rpx; height: 58rpx;
padding: 0 30rpx; padding: 5rpx 0 0 70rpx;
font-size: 28rpx; font-size: 28rpx;
color: #333333; color: #000000;
text-align: center; text-align: center;
z-index: 1; z-index: 1;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
...@@ -618,7 +782,8 @@ const drawImage = (ctx, src, x, y, w, h) => { ...@@ -618,7 +782,8 @@ const drawImage = (ctx, src, x, y, w, h) => {
position: absolute; position: absolute;
bottom: 146rpx; bottom: 146rpx;
left: 22rpx; left: 22rpx;
width: calc(100% - 44rpx); width: 450rpx;
height: 40rpx;
z-index: 2; z-index: 2;
} }
...@@ -626,9 +791,12 @@ const drawImage = (ctx, src, x, y, w, h) => { ...@@ -626,9 +791,12 @@ const drawImage = (ctx, src, x, y, w, h) => {
font-size: 28rpx; font-size: 28rpx;
font-weight: 500; font-weight: 500;
color: #000000; color: #000000;
width: 450rpx;
height: 40rpx;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
position: absolute;
} }
.poster-collection-number { .poster-collection-number {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment