fix: 优化邀请函画布导出分辨率,提升图片与文字清晰度
This commit is contained in:
138
js/script.js
138
js/script.js
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user