style:还原缩小编辑器工具栏按钮间距,适应小屏幕
This commit is contained in:
@@ -513,23 +513,3 @@ html, body {
|
||||
.guide-line.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 1px 网格线 */
|
||||
.grid-lines {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
display: none;
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(0, 0, 0, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(0, 0, 0, 0.05) 1px, transparent 1px);
|
||||
background-size: 10px 10px;
|
||||
}
|
||||
|
||||
.grid-lines.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -80,8 +80,6 @@
|
||||
</div>
|
||||
<!-- 层叠图图层 -->
|
||||
<div class="overlay-layer" id="overlayLayer"></div>
|
||||
<!-- 1px 网格线 -->
|
||||
<div class="grid-lines" id="gridLines"></div>
|
||||
<!-- 拖拽辅助线 -->
|
||||
<div class="guide-line guide-horizontal" id="guideH"></div>
|
||||
<div class="guide-line guide-vertical" id="guideV"></div>
|
||||
|
||||
390
js/script.js
390
js/script.js
@@ -38,7 +38,6 @@
|
||||
document.body.appendChild(overlayInput);
|
||||
const guideH = document.getElementById('guideH');
|
||||
const guideV = document.getElementById('guideV');
|
||||
const gridLines = document.getElementById('gridLines');
|
||||
|
||||
let currentImage = null;
|
||||
let bgImageScale = 1;
|
||||
@@ -208,9 +207,6 @@
|
||||
// 初始化文本编辑器工具栏
|
||||
initTextEditorToolbar();
|
||||
|
||||
// 初始化键盘微调功能
|
||||
initKeyboardNudge();
|
||||
|
||||
// 加载默认背景图
|
||||
loadDefaultBackground();
|
||||
|
||||
@@ -454,23 +450,27 @@
|
||||
const dx = clientX - startX;
|
||||
const dy = clientY - startY;
|
||||
|
||||
// 移除移动距离限制,实现 1px 精确移动
|
||||
let newLeft = startLeft + dx;
|
||||
let newTop = startTop + dy;
|
||||
const moveDistance = Math.sqrt(dx * dx + dy * dy);
|
||||
if (moveDistance > 5) {
|
||||
let newLeft = startLeft + dx;
|
||||
let newTop = startTop + dy;
|
||||
|
||||
// 使用吸附功能
|
||||
const snapped = getSnapPosition(overlayEl, newLeft, newTop);
|
||||
newLeft = snapped.left;
|
||||
newTop = snapped.top;
|
||||
// 使用吸附功能
|
||||
const snapped = getSnapPosition(overlayEl, newLeft, newTop);
|
||||
newLeft = snapped.left;
|
||||
newTop = snapped.top;
|
||||
|
||||
// 显示对齐辅助线(网格线已在 getSnapPosition 中显示)
|
||||
if (snapped.showGuide) {
|
||||
showAlignmentGuides(overlayEl);
|
||||
// 显示对齐辅助线
|
||||
if (snapped.showGuide) {
|
||||
showAlignmentGuides(overlayEl);
|
||||
} else {
|
||||
hideGuides();
|
||||
}
|
||||
|
||||
overlayEl.style.left = newLeft + 'px';
|
||||
overlayEl.style.top = newTop + 'px';
|
||||
}
|
||||
|
||||
overlayEl.style.left = newLeft + 'px';
|
||||
overlayEl.style.top = newTop + 'px';
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
@@ -974,8 +974,7 @@
|
||||
Math.pow(clientY - clickStartY, 2)
|
||||
);
|
||||
|
||||
// 只要有移动就开始拖拽,实现 1px 精确移动
|
||||
if (moveDistance > 0) {
|
||||
if (moveDistance > 5) {
|
||||
hasMoved = true;
|
||||
if (dragTimer) {
|
||||
clearTimeout(dragTimer);
|
||||
@@ -998,9 +997,11 @@
|
||||
element.style.left = snapped.left + 'px';
|
||||
element.style.top = snapped.top + 'px';
|
||||
|
||||
// 显示对齐辅助线(网格线已在 getSnapPosition 中显示)
|
||||
// 显示对齐辅助线
|
||||
if (snapped.showGuide) {
|
||||
showAlignmentGuides(element);
|
||||
} else {
|
||||
hideGuides();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1216,16 +1217,11 @@
|
||||
const cCenterX = cWidth / 2;
|
||||
const cCenterY = cHeight / 2;
|
||||
|
||||
const threshold = 1;
|
||||
const threshold = 5;
|
||||
let snapLeft = newLeft;
|
||||
let snapTop = newTop;
|
||||
let showGuide = false;
|
||||
|
||||
// 显示网格线
|
||||
if (gridLines) {
|
||||
gridLines.classList.add('visible');
|
||||
}
|
||||
|
||||
// 水平垂直分别检测
|
||||
let snapX = false;
|
||||
let snapY = false;
|
||||
@@ -1261,7 +1257,7 @@
|
||||
}
|
||||
|
||||
// 检测其他元素对齐(输入框和层叠图)
|
||||
const otherInputs = [input1, input2, input3].filter(el => el && el !== draggedEl);
|
||||
const otherInputs = [input1, input2].filter(el => el && el !== draggedEl);
|
||||
const otherOverlays = Array.from(overlayLayer.querySelectorAll('.overlay-image'))
|
||||
.filter(el => el !== draggedEl);
|
||||
|
||||
@@ -1424,7 +1420,7 @@
|
||||
const elCenterX = elLeft + elWidth / 2;
|
||||
const elCenterY = elTop + elHeight / 2;
|
||||
|
||||
const threshold = 1;
|
||||
const threshold = 5;
|
||||
let guideHPos = null;
|
||||
let guideVPos = null;
|
||||
|
||||
@@ -1575,9 +1571,6 @@
|
||||
function hideGuides() {
|
||||
guideH.classList.remove('visible');
|
||||
guideV.classList.remove('visible');
|
||||
if (gridLines) {
|
||||
gridLines.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
|
||||
// 取消所有选中
|
||||
@@ -1745,53 +1738,6 @@
|
||||
}, { passive: false });
|
||||
}
|
||||
|
||||
// 初始化键盘微调功能(方向键 1px 移动)
|
||||
function initKeyboardNudge() {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// 如果正在编辑文本,不处理方向键
|
||||
if (editingInput) return;
|
||||
|
||||
// 获取当前选中的元素
|
||||
const targetEl = selectedInput || selectedOverlay;
|
||||
if (!targetEl) return;
|
||||
|
||||
// 防止默认行为(页面滚动)
|
||||
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
let currentLeft = parseInt(targetEl.style.left) || targetEl.offsetLeft;
|
||||
let currentTop = parseInt(targetEl.style.top) || targetEl.offsetTop;
|
||||
|
||||
switch(e.key) {
|
||||
case 'ArrowUp':
|
||||
currentTop -= 1;
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
currentTop += 1;
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
currentLeft -= 1;
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
currentLeft += 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// 应用新位置
|
||||
targetEl.style.left = currentLeft + 'px';
|
||||
targetEl.style.top = currentTop + 'px';
|
||||
|
||||
// 显示对齐辅助线(网格线已在 getSnapPosition 中显示)
|
||||
const snapped = getSnapPosition(targetEl, currentLeft, currentTop);
|
||||
if (snapped.showGuide) {
|
||||
showAlignmentGuides(targetEl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 显示右键菜单
|
||||
function showContextMenu(x, y) {
|
||||
hideContextMenu();
|
||||
@@ -1832,49 +1778,10 @@
|
||||
document.body.appendChild(menu);
|
||||
}
|
||||
|
||||
// 从 blob 显示预览
|
||||
function showPreviewFromBlob(blob) {
|
||||
console.log('预览图片大小:', (blob.size / 1024).toFixed(2), 'KB');
|
||||
const dataUrl = URL.createObjectURL(blob);
|
||||
showPreviewModal(dataUrl, blob);
|
||||
}
|
||||
|
||||
// 显示预览
|
||||
async function showPreview() {
|
||||
const canvas = await generateCanvas();
|
||||
console.log('预览画布尺寸:', canvas.width, 'x', canvas.height);
|
||||
|
||||
// 尝试使用 toDataURL 获取更高质量的图片
|
||||
let dataUrl;
|
||||
let blob;
|
||||
|
||||
try {
|
||||
// 先尝试 toDataURL
|
||||
dataUrl = canvas.toDataURL('image/png');
|
||||
console.log('预览 toDataURL 成功,dataUrl 长度:', dataUrl.length);
|
||||
|
||||
// 将 dataUrl 转换为 blob
|
||||
const response = await fetch(dataUrl);
|
||||
blob = await response.blob();
|
||||
console.log('预览 dataUrl 转 blob 成功,blob 尺寸:', blob.size, 'bytes');
|
||||
} catch (err) {
|
||||
console.log('预览 toDataURL 失败,使用 toBlob:', err);
|
||||
// 如果 toDataURL 失败,使用 toBlob
|
||||
blob = await new Promise(resolve => {
|
||||
canvas.toBlob((b) => {
|
||||
console.log('预览 toBlob 回调 - blob 尺寸:', b.size, 'bytes');
|
||||
resolve(b);
|
||||
}, 'image/png', 1.0);
|
||||
});
|
||||
dataUrl = URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
console.log('预览图片大小:', (blob.size / 1024).toFixed(2), 'KB');
|
||||
showPreviewModal(dataUrl, blob);
|
||||
}
|
||||
|
||||
// 显示预览弹窗
|
||||
function showPreviewModal(dataUrl, blob) {
|
||||
const dataUrl = canvas.toDataURL('image/png');
|
||||
|
||||
const tipOverlay = document.createElement('div');
|
||||
tipOverlay.id = 'previewTip';
|
||||
@@ -1894,7 +1801,7 @@
|
||||
`;
|
||||
|
||||
const tipText = document.createElement('p');
|
||||
tipText.innerHTML = '长按图片保存到相册<br><span style="font-size:14px;opacity:0.8">(预览已缩放,实际保存为高清原图)</span>';
|
||||
tipText.textContent = '长按图片保存到相册';
|
||||
tipText.style.cssText = `
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
@@ -1934,12 +1841,7 @@
|
||||
`;
|
||||
saveBtn.onclick = async () => {
|
||||
tipOverlay.remove();
|
||||
await saveImageToGallery(dataUrl, blob);
|
||||
// 重新隐藏 canvas
|
||||
exportCanvas.setAttribute('hidden', '');
|
||||
exportCanvas.style.position = '';
|
||||
exportCanvas.style.left = '';
|
||||
exportCanvas.style.top = '';
|
||||
await saveImageToGallery(dataUrl);
|
||||
};
|
||||
|
||||
const closeBtn = document.createElement('button');
|
||||
@@ -1953,14 +1855,7 @@
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
closeBtn.onclick = () => {
|
||||
tipOverlay.remove();
|
||||
// 重新隐藏 canvas
|
||||
exportCanvas.setAttribute('hidden', '');
|
||||
exportCanvas.style.position = '';
|
||||
exportCanvas.style.left = '';
|
||||
exportCanvas.style.top = '';
|
||||
};
|
||||
closeBtn.onclick = () => tipOverlay.remove();
|
||||
|
||||
btnContainer.appendChild(saveBtn);
|
||||
btnContainer.appendChild(closeBtn);
|
||||
@@ -1971,17 +1866,13 @@
|
||||
}
|
||||
|
||||
// 保存图片到相册
|
||||
async function saveImageToGallery(dataUrl, blob) {
|
||||
async function saveImageToGallery(dataUrl) {
|
||||
const filename = '邀请函_' + Date.now() + '.png';
|
||||
|
||||
// 如果没有传入 blob,尝试从 window._lastExportBlob 获取
|
||||
if (!blob && window._lastExportBlob) {
|
||||
blob = window._lastExportBlob;
|
||||
}
|
||||
|
||||
// 尝试使用 File System Access API (Android/Chrome)
|
||||
if ('showSaveFilePicker' in window) {
|
||||
try {
|
||||
const blob = await (await fetch(dataUrl)).blob();
|
||||
const handle = await window.showSaveFilePicker({
|
||||
suggestedName: filename,
|
||||
types: [{
|
||||
@@ -2002,6 +1893,7 @@
|
||||
// 尝试使用 Web Share API (iOS Safari)
|
||||
if (navigator.share && navigator.canShare) {
|
||||
try {
|
||||
const blob = await (await fetch(dataUrl)).blob();
|
||||
const file = new File([blob], filename, { type: 'image/png' });
|
||||
|
||||
if (navigator.canShare({ files: [file] })) {
|
||||
@@ -2060,57 +1952,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 生成画布(高清无损,固定 1080×1920 分辨率)
|
||||
// 生成画布(所见即所得 - 使用显示尺寸)
|
||||
async function generateCanvas() {
|
||||
const canvas = exportCanvas;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const container = imageContainer;
|
||||
|
||||
// 固定高清分辨率 1080×1920
|
||||
const TARGET_WIDTH = 1080;
|
||||
const TARGET_HEIGHT = 1920;
|
||||
|
||||
console.log('目标画布尺寸:', TARGET_WIDTH, 'x', TARGET_HEIGHT);
|
||||
|
||||
// 显示尺寸(用于计算缩放比例)
|
||||
const displayWidth = container.clientWidth;
|
||||
const displayHeight = container.clientHeight;
|
||||
console.log('显示容器尺寸:', displayWidth, 'x', displayHeight);
|
||||
|
||||
// 临时显示 canvas 以避免浏览器对隐藏 canvas 的尺寸限制
|
||||
canvas.removeAttribute('hidden');
|
||||
canvas.style.position = 'fixed';
|
||||
canvas.style.left = '-9999px';
|
||||
canvas.style.top = '-9999px';
|
||||
|
||||
// 设置画布为高清分辨率
|
||||
canvas.width = TARGET_WIDTH;
|
||||
canvas.height = TARGET_HEIGHT;
|
||||
console.log('实际画布尺寸:', canvas.width, 'x', canvas.height);
|
||||
const width = container.clientWidth;
|
||||
const height = container.clientHeight;
|
||||
|
||||
// 启用最高质量渲染
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
// 白色背景
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(0, 0, TARGET_WIDTH, TARGET_HEIGHT);
|
||||
|
||||
// 计算从显示尺寸到目标分辨率的缩放比例
|
||||
const scaleX = TARGET_WIDTH / displayWidth;
|
||||
const scaleY = TARGET_HEIGHT / displayHeight;
|
||||
console.log('缩放比例:', scaleX, 'x', scaleY);
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
if (currentImage) {
|
||||
console.log('背景图原始尺寸:', currentImage.naturalWidth, 'x', currentImage.naturalHeight);
|
||||
drawBgImageOnCanvasHD(ctx, currentImage, TARGET_WIDTH, TARGET_HEIGHT, scaleX, scaleY);
|
||||
drawBgImageOnCanvas(ctx, currentImage, width, height);
|
||||
}
|
||||
|
||||
await drawOverlayImagesHD(ctx, scaleX, scaleY);
|
||||
await drawOverlayImages(ctx);
|
||||
|
||||
const textInputs = container.querySelectorAll('.preview-input-layer .text-input');
|
||||
textInputs.forEach(textInput => {
|
||||
drawTextOnCanvasHD(ctx, textInput, scaleX, scaleY);
|
||||
drawTextOnCanvas(ctx, textInput);
|
||||
});
|
||||
|
||||
return canvas;
|
||||
@@ -2148,39 +2013,6 @@
|
||||
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) => {
|
||||
@@ -2227,61 +2059,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 绘制层叠图(高清版本)
|
||||
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;
|
||||
|
||||
// 直接使用已加载的图片元素,避免重新加载导致质量下降
|
||||
if (imgEl.complete && imgEl.naturalWidth > 0) {
|
||||
ctx.drawImage(imgEl, left, top, width, height);
|
||||
ctx.globalAlpha = 1;
|
||||
checkComplete();
|
||||
} else {
|
||||
// 如果图片未加载完成,等待加载
|
||||
imgEl.onload = () => {
|
||||
ctx.drawImage(imgEl, left, top, width, height);
|
||||
ctx.globalAlpha = 1;
|
||||
checkComplete();
|
||||
};
|
||||
imgEl.onerror = () => {
|
||||
ctx.globalAlpha = 1;
|
||||
checkComplete();
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 在画布上绘制文字
|
||||
function drawTextOnCanvas(ctx, inputElement) {
|
||||
const deleteBtn = inputElement.querySelector('.text-delete-btn');
|
||||
@@ -2311,35 +2088,6 @@
|
||||
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) {
|
||||
@@ -2349,7 +2097,7 @@
|
||||
|
||||
try {
|
||||
const canvas = await generateCanvas();
|
||||
await showMobileSaveTip(canvas);
|
||||
showMobileSaveTip();
|
||||
} catch (err) {
|
||||
console.error('保存失败:', err);
|
||||
alert('保存失败,请重试');
|
||||
@@ -2357,42 +2105,10 @@
|
||||
}
|
||||
|
||||
// 显示移动端保存选项
|
||||
async function showMobileSaveTip(canvas) {
|
||||
console.log('导出画布尺寸:', canvas.width, 'x', canvas.height);
|
||||
|
||||
// 在 toBlob 之前再次确认 canvas 尺寸
|
||||
console.log('toBlob 前 canvas 尺寸:', canvas.width, 'x', canvas.height);
|
||||
|
||||
// 尝试使用 toDataURL 获取更高质量的图片
|
||||
let dataUrl;
|
||||
let blob;
|
||||
|
||||
try {
|
||||
// 先尝试 toDataURL
|
||||
dataUrl = canvas.toDataURL('image/png');
|
||||
console.log('toDataURL 成功,dataUrl 长度:', dataUrl.length);
|
||||
|
||||
// 将 dataUrl 转换为 blob
|
||||
const response = await fetch(dataUrl);
|
||||
blob = await response.blob();
|
||||
console.log('dataUrl 转 blob 成功,blob 尺寸:', blob.size, 'bytes');
|
||||
} catch (err) {
|
||||
console.log('toDataURL 失败,使用 toBlob:', err);
|
||||
// 如果 toDataURL 失败,使用 toBlob
|
||||
blob = await new Promise(resolve => {
|
||||
canvas.toBlob((b) => {
|
||||
console.log('toBlob 回调 - blob 尺寸:', b.size, 'bytes');
|
||||
resolve(b);
|
||||
}, 'image/png', 1.0);
|
||||
});
|
||||
dataUrl = URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
console.log('导出图片大小:', (blob.size / 1024).toFixed(2), 'KB');
|
||||
function showMobileSaveTip() {
|
||||
const canvas = exportCanvas;
|
||||
const dataUrl = canvas.toDataURL('image/png');
|
||||
const filename = '邀请函_' + Date.now() + '.png';
|
||||
|
||||
// 保存 blob 供后续使用
|
||||
window._lastExportBlob = blob;
|
||||
|
||||
const tipOverlay = document.createElement('div');
|
||||
tipOverlay.id = 'mobileSaveTip';
|
||||
@@ -2443,7 +2159,7 @@
|
||||
`;
|
||||
saveBtn.onclick = async () => {
|
||||
tipOverlay.remove();
|
||||
await saveImageToGallery(dataUrl, blob);
|
||||
await saveImageToGallery(dataUrl);
|
||||
};
|
||||
|
||||
const cancelBtn = document.createElement('button');
|
||||
@@ -2458,23 +2174,11 @@
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
cancelBtn.onclick = () => {
|
||||
tipOverlay.remove();
|
||||
// 重新隐藏 canvas
|
||||
exportCanvas.setAttribute('hidden', '');
|
||||
exportCanvas.style.position = '';
|
||||
exportCanvas.style.left = '';
|
||||
exportCanvas.style.top = '';
|
||||
};
|
||||
cancelBtn.onclick = () => tipOverlay.remove();
|
||||
|
||||
tipOverlay.addEventListener('click', (e) => {
|
||||
if (e.target === tipOverlay) {
|
||||
tipOverlay.remove();
|
||||
// 重新隐藏 canvas
|
||||
exportCanvas.setAttribute('hidden', '');
|
||||
exportCanvas.style.position = '';
|
||||
exportCanvas.style.left = '';
|
||||
exportCanvas.style.top = '';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2500,7 +2204,7 @@
|
||||
`;
|
||||
previewBtn.onclick = () => {
|
||||
tipOverlay.remove();
|
||||
showPreviewFromBlob(blob);
|
||||
showPreview();
|
||||
};
|
||||
|
||||
// 插入到标题后面
|
||||
|
||||
Reference in New Issue
Block a user