fix:校准画布编辑与图片导出文字水平垂直位置偏移问题,修改屏幕尺寸为960x1361像素
This commit is contained in:
104
js/script.js
104
js/script.js
@@ -864,8 +864,10 @@
|
||||
|
||||
startX = clientX;
|
||||
startY = clientY;
|
||||
startLeft = element.offsetLeft;
|
||||
startTop = element.offsetTop;
|
||||
const containerRect = imageContainer.getBoundingClientRect();
|
||||
const elemRect = element.getBoundingClientRect();
|
||||
startLeft = elemRect.left - containerRect.left;
|
||||
startTop = elemRect.top - containerRect.top;
|
||||
|
||||
// 延迟判断是否为拖拽
|
||||
dragTimer = setTimeout(() => {
|
||||
@@ -1782,31 +1784,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 生成画布(预览与保存样式一致)
|
||||
// 生成画布(所见即所得 - 使用显示尺寸)
|
||||
async function generateCanvas() {
|
||||
const canvas = exportCanvas;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const container = imageContainer;
|
||||
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const width = container.clientWidth;
|
||||
const height = container.clientHeight;
|
||||
|
||||
canvas.width = containerWidth;
|
||||
canvas.height = containerHeight;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
// 绘制白色背景
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
// 绘制背景图(考虑滑动偏移)
|
||||
if (currentImage) {
|
||||
drawImageWithOffset(ctx, currentImage, containerWidth, containerHeight);
|
||||
drawBgImageOnCanvas(ctx, currentImage, width, height);
|
||||
}
|
||||
|
||||
// 绘制层叠图(等待图片加载完成)
|
||||
await drawOverlayImages(ctx);
|
||||
|
||||
// 绘制文字(动态获取所有文本输入框)
|
||||
const textInputs = container.querySelectorAll('.preview-input-layer .text-input');
|
||||
textInputs.forEach(textInput => {
|
||||
drawTextOnCanvas(ctx, textInput);
|
||||
@@ -1815,6 +1813,38 @@
|
||||
return canvas;
|
||||
}
|
||||
|
||||
// 绘制背景图
|
||||
function drawBgImageOnCanvas(ctx, img, width, height) {
|
||||
// 使用与编辑时完全相同的计算逻辑
|
||||
const containerWidth = width;
|
||||
const containerHeight = height;
|
||||
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;
|
||||
|
||||
// 计算位置(与 updateBgImage 函数完全一致)
|
||||
const baseLeft = (containerWidth - scaledWidth) / 2;
|
||||
const drawX = baseLeft + imageOffsetX;
|
||||
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) => {
|
||||
@@ -1832,20 +1862,25 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 offsetLeft/offsetTop 获取相对于 overlayLayer 的位置
|
||||
const left = overlayEl.offsetLeft;
|
||||
const top = overlayEl.offsetTop;
|
||||
const width = overlayEl.offsetWidth;
|
||||
const height = overlayEl.offsetHeight;
|
||||
|
||||
ctx.globalAlpha = overlayEl.style.opacity || 1;
|
||||
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
ctx.drawImage(img, left, top, width, height);
|
||||
ctx.globalAlpha = 1;
|
||||
loadedCount++;
|
||||
if (loadedCount >= overlayImages.length) {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
img.onerror = () => {
|
||||
ctx.globalAlpha = 1;
|
||||
loadedCount++;
|
||||
if (loadedCount >= overlayImages.length) {
|
||||
resolve();
|
||||
@@ -1856,42 +1891,33 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 绘制图片(考虑滑动偏移)
|
||||
function drawImageWithOffset(ctx, img, containerWidth, containerHeight) {
|
||||
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 baseLeft = (containerWidth - imgWidth) / 2;
|
||||
const drawX = baseLeft + imageOffsetX;
|
||||
|
||||
ctx.drawImage(img, drawX, 0, imgWidth, imgHeight);
|
||||
}
|
||||
|
||||
// 在画布上绘制文字
|
||||
function drawTextOnCanvas(ctx, inputElement) {
|
||||
const text = inputElement.textContent;
|
||||
if (!text) return;
|
||||
const deleteBtn = inputElement.querySelector('.text-delete-btn');
|
||||
let text = inputElement.textContent;
|
||||
if (deleteBtn) {
|
||||
text = text.replace(deleteBtn.textContent, '');
|
||||
}
|
||||
if (!text.trim()) return;
|
||||
|
||||
const style = window.getComputedStyle(inputElement);
|
||||
// 使用 offsetLeft/offsetTop 获取相对于 preview-input-layer 的位置
|
||||
const inputLeft = inputElement.offsetLeft;
|
||||
const inputTop = inputElement.offsetTop;
|
||||
|
||||
const style = window.getComputedStyle(inputElement);
|
||||
const fontSize = parseInt(style.fontSize);
|
||||
const lineHeight = parseFloat(style.lineHeight);
|
||||
|
||||
ctx.font = `${style.fontWeight} ${fontSize}px ${style.fontFamily}`;
|
||||
ctx.fillStyle = style.color;
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.textBaseline = 'alphabetic';
|
||||
|
||||
ctx.fillText(text, inputLeft, inputTop);
|
||||
// 计算文本垂直偏移量,使其与 DOM 渲染一致
|
||||
// DOM 中 line-height 会影响文本位置,需要计算实际文本基线位置
|
||||
const actualLineHeight = lineHeight || fontSize * 1.4;
|
||||
const textOffsetY = (actualLineHeight - fontSize) / 2 + fontSize * 0.85;
|
||||
|
||||
ctx.fillText(text, inputLeft, inputTop + textOffsetY);
|
||||
}
|
||||
|
||||
// 保存图片
|
||||
|
||||
Reference in New Issue
Block a user