fix: 优化邀请函画布导出分辨率,提升图片与文字清晰度

This commit is contained in:
Zhukj
2026-04-20 16:37:06 +08:00
parent 66e07c72e2
commit 1b11523397

View File

@@ -1958,24 +1958,40 @@
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
const container = imageContainer; const container = imageContainer;
const width = container.clientWidth; // 高清渲染倍数
const height = container.clientHeight; const scaleFactor = 2;
// 显示尺寸
const displayWidth = container.clientWidth;
const displayHeight = container.clientHeight;
// 高清画布尺寸(2倍)
const width = displayWidth * scaleFactor;
const height = displayHeight * scaleFactor;
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
// 启用高清渲染
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.fillStyle = '#fff'; ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, width, height); ctx.fillRect(0, 0, width, height);
// 计算缩放比例
const scaleX = width / displayWidth;
const scaleY = height / displayHeight;
if (currentImage) { if (currentImage) {
drawBgImageOnCanvas(ctx, currentImage, width, height); drawBgImageOnCanvasHD(ctx, currentImage, width, height, scaleX, scaleY);
} }
await drawOverlayImages(ctx); await drawOverlayImagesHD(ctx, scaleX, scaleY);
const textInputs = container.querySelectorAll('.preview-input-layer .text-input'); const textInputs = container.querySelectorAll('.preview-input-layer .text-input');
textInputs.forEach(textInput => { textInputs.forEach(textInput => {
drawTextOnCanvas(ctx, textInput); drawTextOnCanvasHD(ctx, textInput, scaleX, scaleY);
}); });
return canvas; return canvas;
@@ -2013,6 +2029,39 @@
ctx.globalAlpha = 1; ctx.globalAlpha = 1;
} }
// 绘制背景图(高清版本)
function drawBgImageOnCanvasHD(ctx, img, canvasWidth, canvasHeight, scaleX, scaleY) {
// 使用原始图片尺寸进行高清渲染
const containerWidth = canvasWidth;
const containerHeight = canvasHeight;
const imgRatio = img.naturalWidth / img.naturalHeight;
const containerRatio = containerWidth / containerHeight;
let imgWidth, imgHeight;
if (imgRatio > containerRatio) {
imgHeight = containerHeight;
imgWidth = imgHeight * imgRatio;
} else {
imgWidth = containerWidth;
imgHeight = imgWidth / imgRatio;
}
// 应用缩放
const scaledWidth = imgWidth * bgImageScale;
const scaledHeight = imgHeight * bgImageScale;
// 计算位置(考虑高清缩放)
const baseLeft = (containerWidth - scaledWidth) / 2;
const drawX = baseLeft + (imageOffsetX * scaleX);
const drawY = (containerHeight - scaledHeight) / 2;
ctx.globalAlpha = bgImageOpacity;
// 使用原始图片绘制,获得最佳清晰度
ctx.drawImage(img, drawX, drawY, scaledWidth, scaledHeight);
ctx.globalAlpha = 1;
}
// 绘制层叠图 // 绘制层叠图
function drawOverlayImages(ctx) { function drawOverlayImages(ctx) {
return new Promise((resolve) => { return new Promise((resolve) => {
@@ -2059,6 +2108,56 @@
}); });
} }
// 绘制层叠图(高清版本)
function drawOverlayImagesHD(ctx, scaleX, scaleY) {
return new Promise((resolve) => {
const overlayImages = overlayLayer.querySelectorAll('.overlay-image');
if (overlayImages.length === 0) {
resolve();
return;
}
let loadedCount = 0;
const totalImages = overlayImages.length;
function checkComplete() {
loadedCount++;
if (loadedCount >= totalImages) {
resolve();
}
}
overlayImages.forEach(overlayEl => {
const imgEl = overlayEl.querySelector('img');
if (!imgEl) {
checkComplete();
return;
}
// 使用 offsetLeft/offsetTop 获取相对于 overlayLayer 的位置,并应用高清缩放
const left = overlayEl.offsetLeft * scaleX;
const top = overlayEl.offsetTop * scaleY;
const width = overlayEl.offsetWidth * scaleX;
const height = overlayEl.offsetHeight * scaleY;
ctx.globalAlpha = overlayEl.style.opacity || 1;
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
ctx.drawImage(img, left, top, width, height);
ctx.globalAlpha = 1;
checkComplete();
};
img.onerror = () => {
ctx.globalAlpha = 1;
checkComplete();
};
img.src = imgEl.src;
});
});
}
// 在画布上绘制文字 // 在画布上绘制文字
function drawTextOnCanvas(ctx, inputElement) { function drawTextOnCanvas(ctx, inputElement) {
const deleteBtn = inputElement.querySelector('.text-delete-btn'); const deleteBtn = inputElement.querySelector('.text-delete-btn');
@@ -2088,6 +2187,35 @@
ctx.fillText(text, inputLeft, inputTop + textOffsetY); ctx.fillText(text, inputLeft, inputTop + textOffsetY);
} }
// 在画布上绘制文字(高清版本)
function drawTextOnCanvasHD(ctx, inputElement, scaleX, scaleY) {
const deleteBtn = inputElement.querySelector('.text-delete-btn');
let text = inputElement.textContent;
if (deleteBtn) {
text = text.replace(deleteBtn.textContent, '');
}
if (!text.trim()) return;
// 使用 offsetLeft/offsetTop 获取相对于 preview-input-layer 的位置,并应用高清缩放
const inputLeft = inputElement.offsetLeft * scaleX;
const inputTop = inputElement.offsetTop * scaleY;
const style = window.getComputedStyle(inputElement);
const fontSize = parseInt(style.fontSize) * scaleX; // 字体大小也按比例缩放
const lineHeight = parseFloat(style.lineHeight);
// 使用高清字体渲染
ctx.font = `${style.fontWeight} ${fontSize}px ${style.fontFamily}`;
ctx.fillStyle = style.color;
ctx.textBaseline = 'alphabetic';
// 计算文本垂直偏移量,使其与 DOM 渲染一致
const actualLineHeight = (lineHeight || fontSize * 1.4) * scaleY;
const textOffsetY = (actualLineHeight - fontSize) / 2 + fontSize * 0.85;
ctx.fillText(text, inputLeft, inputTop + textOffsetY);
}
// 保存图片 // 保存图片
async function saveImage() { async function saveImage() {
if (!currentImage) { if (!currentImage) {