fix(canvas): 统一缩放比例以保持宽高比
This commit is contained in:
77
js/script.js
77
js/script.js
@@ -2122,20 +2122,20 @@
|
|||||||
ctx.fillRect(0, 0, TARGET_WIDTH, TARGET_HEIGHT);
|
ctx.fillRect(0, 0, TARGET_WIDTH, TARGET_HEIGHT);
|
||||||
|
|
||||||
// 计算从显示尺寸到目标分辨率的缩放比例
|
// 计算从显示尺寸到目标分辨率的缩放比例
|
||||||
const scaleX = TARGET_WIDTH / displayWidth;
|
// 使用统一的缩放比例,保持宽高比,确保所见即所得
|
||||||
const scaleY = TARGET_HEIGHT / displayHeight;
|
const scale = TARGET_WIDTH / displayWidth;
|
||||||
console.log('缩放比例:', scaleX, 'x', scaleY);
|
console.log('统一缩放比例:', scale);
|
||||||
|
|
||||||
if (currentImage) {
|
if (currentImage) {
|
||||||
console.log('背景图原始尺寸:', currentImage.naturalWidth, 'x', currentImage.naturalHeight);
|
console.log('背景图原始尺寸:', currentImage.naturalWidth, 'x', currentImage.naturalHeight);
|
||||||
drawBgImageOnCanvasHD(ctx, currentImage, TARGET_WIDTH, TARGET_HEIGHT, scaleX, scaleY);
|
drawBgImageOnCanvasHD(ctx, currentImage, TARGET_WIDTH, TARGET_HEIGHT, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
await drawOverlayImagesHD(ctx, scaleX, scaleY);
|
await drawOverlayImagesHD(ctx, scale);
|
||||||
|
|
||||||
const textInputs = container.querySelectorAll('.preview-input-layer .text-input');
|
const textInputs = container.querySelectorAll('.preview-input-layer .text-input');
|
||||||
textInputs.forEach(textInput => {
|
textInputs.forEach(textInput => {
|
||||||
drawTextOnCanvasHD(ctx, textInput, scaleX, scaleY);
|
drawTextOnCanvasHD(ctx, textInput, scale);
|
||||||
});
|
});
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
@@ -2174,8 +2174,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 绘制背景图(高清版本)
|
// 绘制背景图(高清版本)
|
||||||
function drawBgImageOnCanvasHD(ctx, img, canvasWidth, canvasHeight, scaleX, scaleY) {
|
function drawBgImageOnCanvasHD(ctx, img, canvasWidth, canvasHeight, scale) {
|
||||||
// 使用原始图片尺寸进行高清渲染
|
// 使用与编辑时完全相同的计算逻辑,只是按比例缩放
|
||||||
const containerWidth = canvasWidth;
|
const containerWidth = canvasWidth;
|
||||||
const containerHeight = canvasHeight;
|
const containerHeight = canvasHeight;
|
||||||
const imgRatio = img.naturalWidth / img.naturalHeight;
|
const imgRatio = img.naturalWidth / img.naturalHeight;
|
||||||
@@ -2191,46 +2191,17 @@
|
|||||||
imgHeight = imgWidth / imgRatio;
|
imgHeight = imgWidth / imgRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用缩放
|
// 应用缩放(与编辑时的 bgImageScale 一致)
|
||||||
const scaledWidth = imgWidth * bgImageScale;
|
const scaledWidth = imgWidth * bgImageScale;
|
||||||
const scaledHeight = imgHeight * bgImageScale;
|
const scaledHeight = imgHeight * bgImageScale;
|
||||||
|
|
||||||
// 计算位置(考虑高清缩放)
|
// 计算位置(与 updateBgImage 函数完全一致,只是按比例缩放偏移量)
|
||||||
const baseLeft = (containerWidth - scaledWidth) / 2;
|
const baseLeft = (containerWidth - scaledWidth) / 2;
|
||||||
const drawX = baseLeft + (imageOffsetX * scaleX);
|
const drawX = baseLeft + (imageOffsetX * scale);
|
||||||
const drawY = (containerHeight - scaledHeight) / 2;
|
const drawY = (containerHeight - scaledHeight) / 2;
|
||||||
|
|
||||||
ctx.globalAlpha = bgImageOpacity;
|
ctx.globalAlpha = bgImageOpacity;
|
||||||
|
|
||||||
// 微信中:如果原始图片比目标区域大,使用原始尺寸绘制以获得最佳质量
|
|
||||||
// 否则按计算尺寸绘制
|
|
||||||
if (isWechat() && img.naturalWidth > scaledWidth && img.naturalHeight > scaledHeight) {
|
|
||||||
// 计算裁剪区域,从原始图片中心裁剪
|
|
||||||
const srcRatio = scaledWidth / scaledHeight;
|
|
||||||
let srcWidth = img.naturalWidth;
|
|
||||||
let srcHeight = img.naturalHeight;
|
|
||||||
let srcX = 0;
|
|
||||||
let srcY = 0;
|
|
||||||
|
|
||||||
// 如果原始图片比例与目标不同,计算居中裁剪
|
|
||||||
const naturalRatio = img.naturalWidth / img.naturalHeight;
|
|
||||||
if (naturalRatio > imgRatio) {
|
|
||||||
// 原始图片更宽,裁剪左右
|
|
||||||
srcWidth = img.naturalHeight * imgRatio;
|
|
||||||
srcX = (img.naturalWidth - srcWidth) / 2;
|
|
||||||
} else if (naturalRatio < imgRatio) {
|
|
||||||
// 原始图片更高,裁剪上下
|
|
||||||
srcHeight = img.naturalWidth / imgRatio;
|
|
||||||
srcY = (img.naturalHeight - srcHeight) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 drawImage 的 9 参数版本进行高质量缩放
|
|
||||||
ctx.drawImage(img, srcX, srcY, srcWidth, srcHeight, drawX, drawY, scaledWidth, scaledHeight);
|
|
||||||
} else {
|
|
||||||
// 使用原始图片绘制,获得最佳清晰度
|
|
||||||
ctx.drawImage(img, drawX, drawY, scaledWidth, scaledHeight);
|
ctx.drawImage(img, drawX, drawY, scaledWidth, scaledHeight);
|
||||||
}
|
|
||||||
|
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2281,7 +2252,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 绘制层叠图(高清版本)
|
// 绘制层叠图(高清版本)
|
||||||
function drawOverlayImagesHD(ctx, scaleX, scaleY) {
|
function drawOverlayImagesHD(ctx, scale) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const overlayImages = overlayLayer.querySelectorAll('.overlay-image');
|
const overlayImages = overlayLayer.querySelectorAll('.overlay-image');
|
||||||
if (overlayImages.length === 0) {
|
if (overlayImages.length === 0) {
|
||||||
@@ -2306,11 +2277,11 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 offsetLeft/offsetTop 获取相对于 overlayLayer 的位置,并应用高清缩放
|
// 使用 offsetLeft/offsetTop 获取相对于 overlayLayer 的位置,并应用统一缩放
|
||||||
const left = overlayEl.offsetLeft * scaleX;
|
const left = overlayEl.offsetLeft * scale;
|
||||||
const top = overlayEl.offsetTop * scaleY;
|
const top = overlayEl.offsetTop * scale;
|
||||||
const width = overlayEl.offsetWidth * scaleX;
|
const width = overlayEl.offsetWidth * scale;
|
||||||
const height = overlayEl.offsetHeight * scaleY;
|
const height = overlayEl.offsetHeight * scale;
|
||||||
|
|
||||||
ctx.globalAlpha = overlayEl.style.opacity || 1;
|
ctx.globalAlpha = overlayEl.style.opacity || 1;
|
||||||
|
|
||||||
@@ -2365,7 +2336,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 在画布上绘制文字(高清版本)
|
// 在画布上绘制文字(高清版本)
|
||||||
function drawTextOnCanvasHD(ctx, inputElement, scaleX, scaleY) {
|
function drawTextOnCanvasHD(ctx, inputElement, scale) {
|
||||||
const deleteBtn = inputElement.querySelector('.text-delete-btn');
|
const deleteBtn = inputElement.querySelector('.text-delete-btn');
|
||||||
let text = inputElement.textContent;
|
let text = inputElement.textContent;
|
||||||
if (deleteBtn) {
|
if (deleteBtn) {
|
||||||
@@ -2373,17 +2344,17 @@
|
|||||||
}
|
}
|
||||||
if (!text.trim()) return;
|
if (!text.trim()) return;
|
||||||
|
|
||||||
// 使用 offsetLeft/offsetTop 获取相对于 preview-input-layer 的位置,并应用高清缩放
|
// 使用 offsetLeft/offsetTop 获取相对于 preview-input-layer 的位置,并应用统一缩放
|
||||||
const inputLeft = inputElement.offsetLeft * scaleX;
|
const inputLeft = inputElement.offsetLeft * scale;
|
||||||
const inputTop = inputElement.offsetTop * scaleY;
|
const inputTop = inputElement.offsetTop * scale;
|
||||||
|
|
||||||
const style = window.getComputedStyle(inputElement);
|
const style = window.getComputedStyle(inputElement);
|
||||||
const fontSize = parseInt(style.fontSize) * scaleX; // 字体大小也按比例缩放
|
const fontSize = parseInt(style.fontSize) * scale; // 字体大小也按比例缩放
|
||||||
const lineHeight = parseFloat(style.lineHeight);
|
const lineHeight = parseFloat(style.lineHeight);
|
||||||
|
|
||||||
// 微信中:使用更粗的字体权重以获得更好的可读性
|
// 微信中:使用更粗的字体权重以获得更好的可读性
|
||||||
let fontWeight = style.fontWeight;
|
let fontWeight = style.fontWeight;
|
||||||
if (isWechat() && fontWeight === '400' || fontWeight === 'normal') {
|
if (isWechat() && (fontWeight === '400' || fontWeight === 'normal')) {
|
||||||
fontWeight = '500'; // 稍微加粗一点
|
fontWeight = '500'; // 稍微加粗一点
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2398,7 +2369,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 计算文本垂直偏移量,使其与 DOM 渲染一致
|
// 计算文本垂直偏移量,使其与 DOM 渲染一致
|
||||||
const actualLineHeight = (lineHeight || fontSize * 1.4) * scaleY;
|
const actualLineHeight = (lineHeight || fontSize * 1.4);
|
||||||
const textOffsetY = (actualLineHeight - fontSize) / 2 + fontSize * 0.85;
|
const textOffsetY = (actualLineHeight - fontSize) / 2 + fontSize * 0.85;
|
||||||
|
|
||||||
ctx.fillText(text, inputLeft, inputTop + textOffsetY);
|
ctx.fillText(text, inputLeft, inputTop + textOffsetY);
|
||||||
|
|||||||
Reference in New Issue
Block a user