fix: 优化邀请函画布导出分辨率,提升图片与文字清晰度
This commit is contained in:
138
js/script.js
138
js/script.js
@@ -1958,24 +1958,40 @@
|
||||
const ctx = canvas.getContext('2d');
|
||||
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.height = height;
|
||||
|
||||
// 启用高清渲染
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
// 计算缩放比例
|
||||
const scaleX = width / displayWidth;
|
||||
const scaleY = height / displayHeight;
|
||||
|
||||
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');
|
||||
textInputs.forEach(textInput => {
|
||||
drawTextOnCanvas(ctx, textInput);
|
||||
drawTextOnCanvasHD(ctx, textInput, scaleX, scaleY);
|
||||
});
|
||||
|
||||
return canvas;
|
||||
@@ -2013,6 +2029,39 @@
|
||||
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) {
|
||||
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) {
|
||||
const deleteBtn = inputElement.querySelector('.text-delete-btn');
|
||||
@@ -2088,6 +2187,35 @@
|
||||
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() {
|
||||
if (!currentImage) {
|
||||
|
||||
Reference in New Issue
Block a user