diff --git a/css/style.css b/css/style.css
index 240371a..7538e8f 100644
--- a/css/style.css
+++ b/css/style.css
@@ -222,6 +222,50 @@ html, body {
overflow: hidden;
}
+/* 层叠图层 - 位于背景图之上、文字之下 */
+.overlay-layer {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+}
+
+.overlay-layer .overlay-image {
+ position: absolute;
+ pointer-events: auto;
+ cursor: move;
+ touch-action: none;
+}
+
+.overlay-layer .overlay-image.selected {
+ outline: 2px dashed rgba(74, 144, 226, 0.8);
+ outline-offset: 2px;
+}
+
+.overlay-layer .overlay-image .delete-btn {
+ position: absolute;
+ top: -10px;
+ right: -10px;
+ width: 20px;
+ height: 20px;
+ background: #e74c3c;
+ color: #fff;
+ border: none;
+ border-radius: 50%;
+ font-size: 12px;
+ line-height: 20px;
+ text-align: center;
+ cursor: pointer;
+ display: none;
+ z-index: 10;
+}
+
+.overlay-layer .overlay-image.selected .delete-btn {
+ display: block;
+}
+
/* 输入框图层 - 与背景图层隔离 */
.preview-input-layer {
position: absolute;
diff --git a/index.html b/index.html
index a9d4112..f612664 100644
--- a/index.html
+++ b/index.html
@@ -11,6 +11,7 @@
+
@@ -53,6 +54,8 @@
+
+
diff --git a/js/script.js b/js/script.js
index 53acbc3..62a7423 100644
--- a/js/script.js
+++ b/js/script.js
@@ -2,6 +2,7 @@
(function() {
const fileInput = document.getElementById('fileInput');
const addImageBtn = document.getElementById('addImageBtn');
+ const addOverlayBtn = document.getElementById('addOverlayBtn');
const saveBtn = document.getElementById('saveBtn');
const imageContainer = document.getElementById('imageContainer');
const bgLayer = document.getElementById('bgLayer');
@@ -18,6 +19,12 @@
const colorPicker = document.getElementById('colorPicker');
const fontPicker = document.getElementById('fontPicker');
const fontSizePicker = document.getElementById('fontSizePicker');
+ const overlayLayer = document.getElementById('overlayLayer');
+ const overlayInput = document.createElement('input');
+ overlayInput.type = 'file';
+ overlayInput.accept = 'image/*';
+ overlayInput.hidden = true;
+ document.body.appendChild(overlayInput);
const guideH = document.getElementById('guideH');
const guideV = document.getElementById('guideV');
@@ -43,6 +50,20 @@
fileInput.click();
});
+ // 层叠图按钮
+ addOverlayBtn.addEventListener('click', () => {
+ overlayInput.click();
+ });
+
+ // 层叠图文件选择
+ overlayInput.addEventListener('change', (e) => {
+ const file = e.target.files[0];
+ if (file) {
+ loadOverlayImage(file);
+ }
+ overlayInput.value = '';
+ });
+
// 文件选择
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
@@ -139,6 +160,276 @@
reader.readAsDataURL(file);
}
+ // 加载层叠图
+ function loadOverlayImage(file) {
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const img = new Image();
+ img.onload = () => {
+ createOverlayElement(img);
+ };
+ img.src = e.target.result;
+ };
+ reader.readAsDataURL(file);
+ }
+
+ // 创建层叠图元素
+ function createOverlayElement(img) {
+ const container = imageContainer;
+ const containerWidth = container.clientWidth;
+ const containerHeight = container.clientHeight;
+
+ // 计算初始尺寸(不超过容器1/3)
+ let width = img.naturalWidth;
+ let height = img.naturalHeight;
+ const maxSize = Math.min(containerWidth, containerHeight) / 2;
+
+ if (width > maxSize || height > maxSize) {
+ if (width > height) {
+ height = (height / width) * maxSize;
+ width = maxSize;
+ } else {
+ width = (width / height) * maxSize;
+ height = maxSize;
+ }
+ }
+
+ // 创建层叠图元素
+ const overlayEl = document.createElement('div');
+ overlayEl.className = 'overlay-image';
+ overlayEl.dataset.scale = 1;
+
+ const imgEl = document.createElement('img');
+ imgEl.src = img.src;
+ imgEl.style.width = '100%';
+ imgEl.style.height = '100%';
+ imgEl.style.objectFit = 'contain';
+ overlayEl.appendChild(imgEl);
+
+ // 删除按钮
+ const deleteBtn = document.createElement('button');
+ deleteBtn.className = 'delete-btn';
+ deleteBtn.innerHTML = '×';
+ deleteBtn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ overlayEl.remove();
+ });
+ overlayEl.appendChild(deleteBtn);
+
+ // 居中显示
+ overlayEl.style.left = (containerWidth / 2 - width / 2) + 'px';
+ overlayEl.style.top = (containerHeight / 2 - height / 2) + 'px';
+ overlayEl.style.width = width + 'px';
+ overlayEl.style.height = height + 'px';
+
+ // 初始化拖拽和缩放
+ initOverlayImage(overlayEl);
+
+ overlayLayer.appendChild(overlayEl);
+
+ // 选中新添加的层叠图
+ selectOverlay(overlayEl);
+ }
+
+ // 初始化层叠图交互
+ function initOverlayImage(overlayEl) {
+ let isDragging = false;
+ let isResizing = false;
+ let startX, startY, startLeft, startTop, startWidth, startHeight;
+ let currentScale = 1;
+ const minSize = 30;
+
+ // 点击选中
+ overlayEl.addEventListener('click', (e) => {
+ e.stopPropagation();
+ selectOverlay(overlayEl);
+ });
+
+ // 拖拽移动
+ overlayEl.addEventListener('mousedown', handleDragStart);
+ overlayEl.addEventListener('touchstart', handleDragStart, { passive: false });
+
+ function handleDragStart(e) {
+ if (e.target.classList.contains('delete-btn')) return;
+
+ const isTouch = e.type === 'touchstart';
+ const clientX = isTouch ? e.touches[0].clientX : e.clientX;
+ const clientY = isTouch ? e.touches[0].clientY : e.clientY;
+
+ isDragging = true;
+ startX = clientX;
+ startY = clientY;
+ startLeft = overlayEl.offsetLeft;
+ startTop = overlayEl.offsetTop;
+
+ overlayEl.classList.add('dragging');
+
+ document.addEventListener('mousemove', handleDragMove);
+ document.addEventListener('mouseup', handleDragEnd);
+ document.addEventListener('touchmove', handleDragMove, { passive: false });
+ document.addEventListener('touchend', handleDragEnd);
+
+ e.preventDefault();
+ }
+
+ function handleDragMove(e) {
+ if (!isDragging) return;
+
+ const isTouch = e.type === 'touchmove';
+ const clientX = isTouch ? e.touches[0].clientX : e.clientX;
+ const clientY = isTouch ? e.touches[0].clientY : e.clientY;
+
+ const dx = clientX - startX;
+ const dy = clientY - startY;
+
+ let newLeft = startLeft + dx;
+ let newTop = startTop + dy;
+
+ // 边界限制
+ newLeft = Math.max(-overlayEl.offsetWidth + minSize, Math.min(newLeft, imageContainer.offsetWidth - minSize));
+ newTop = Math.max(-overlayEl.offsetHeight + minSize, Math.min(newTop, imageContainer.offsetHeight - minSize));
+
+ overlayEl.style.left = newLeft + 'px';
+ overlayEl.style.top = newTop + 'px';
+
+ e.preventDefault();
+ }
+
+ function handleDragEnd() {
+ isDragging = false;
+ overlayEl.classList.remove('dragging');
+
+ document.removeEventListener('mousemove', handleDragMove);
+ document.removeEventListener('mouseup', handleDragEnd);
+ document.removeEventListener('touchmove', handleDragMove);
+ document.removeEventListener('touchend', handleDragEnd);
+ }
+
+ // 缩放控制(四角)
+ const corners = ['tl', 'tr', 'bl', 'br'];
+ corners.forEach(corner => {
+ const handle = document.createElement('div');
+ handle.className = 'resize-handle resize-' + corner;
+ handle.style.cssText = `
+ position: absolute;
+ width: 12px;
+ height: 12px;
+ background: #4a90e2;
+ border: 1px solid #fff;
+ `;
+
+ switch(corner) {
+ case 'tl': handle.style.top = '-6px'; handle.style.left = '-6px'; handle.style.cursor = 'nw-resize'; break;
+ case 'tr': handle.style.top = '-6px'; handle.style.right = '-6px'; handle.style.cursor = 'ne-resize'; break;
+ case 'bl': handle.style.bottom = '-6px'; handle.style.left = '-6px'; handle.style.cursor = 'sw-resize'; break;
+ case 'br': handle.style.bottom = '-6px'; handle.style.right = '-6px'; handle.style.cursor = 'se-resize'; break;
+ }
+
+ handle.addEventListener('mousedown', startResize);
+ handle.addEventListener('touchstart', startResize, { passive: false });
+
+ overlayEl.appendChild(handle);
+
+ function startResize(e) {
+ e.stopPropagation();
+ isResizing = true;
+ const isTouch = e.type === 'touchstart';
+ const clientX = isTouch ? e.touches[0].clientX : e.clientX;
+ const clientY = isTouch ? e.touches[0].clientY : e.clientY;
+
+ startX = clientX;
+ startY = clientY;
+ startLeft = overlayEl.offsetLeft;
+ startTop = overlayEl.offsetTop;
+ startWidth = overlayEl.offsetWidth;
+ startHeight = overlayEl.offsetHeight;
+
+ document.addEventListener('mousemove', handleResize);
+ document.addEventListener('mouseup', endResize);
+ document.addEventListener('touchmove', handleResize, { passive: false });
+ document.addEventListener('touchend', endResize);
+
+ e.preventDefault();
+ }
+
+ function handleResize(e) {
+ if (!isResizing) return;
+
+ const isTouch = e.type === 'touchmove';
+ const clientX = isTouch ? e.touches[0].clientX : e.clientX;
+ const clientY = isTouch ? e.touches[0].clientY : e.clientY;
+
+ const dx = clientX - startX;
+ const dy = clientY - startY;
+
+ let newWidth = startWidth;
+ let newHeight = startHeight;
+ let newLeft = startLeft;
+ let newTop = startTop;
+
+ const aspectRatio = startWidth / startHeight;
+
+ // 根据角落调整尺寸
+ if (corner === 'br') {
+ newWidth = Math.max(minSize, startWidth + dx);
+ newHeight = newWidth / aspectRatio;
+ } else if (corner === 'bl') {
+ newWidth = Math.max(minSize, startWidth - dx);
+ newHeight = newWidth / aspectRatio;
+ newLeft = startLeft - (newWidth - startWidth);
+ } else if (corner === 'tr') {
+ newWidth = Math.max(minSize, startWidth + dx);
+ newHeight = newWidth / aspectRatio;
+ newTop = startTop - (newHeight - startHeight);
+ } else if (corner === 'tl') {
+ newWidth = Math.max(minSize, startWidth - dx);
+ newHeight = newWidth / aspectRatio;
+ newLeft = startLeft - (newWidth - startWidth);
+ newTop = startTop - (newHeight - startHeight);
+ }
+
+ overlayEl.style.width = newWidth + 'px';
+ overlayEl.style.height = newHeight + 'px';
+ overlayEl.style.left = newLeft + 'px';
+ overlayEl.style.top = newTop + 'px';
+
+ e.preventDefault();
+ }
+
+ function endResize() {
+ isResizing = false;
+ document.removeEventListener('mousemove', handleResize);
+ document.removeEventListener('mouseup', endResize);
+ document.removeEventListener('touchmove', handleResize);
+ document.removeEventListener('touchend', endResize);
+ }
+ });
+ }
+
+ // 选中层叠图
+ let selectedOverlay = null;
+ function selectOverlay(overlayEl) {
+ if (selectedOverlay) {
+ selectedOverlay.classList.remove('selected');
+ }
+ selectedOverlay = overlayEl;
+ overlayEl.classList.add('selected');
+ }
+
+ // 点击背景取消选中层叠图
+ imageContainer.addEventListener('click', (e) => {
+ if (e.target === imageContainer ||
+ e.target === bgLayer ||
+ e.target === bgImage ||
+ e.target === emptyTip ||
+ e.target.closest('.preview-bg-layer')) {
+ if (selectedOverlay) {
+ selectedOverlay.classList.remove('selected');
+ selectedOverlay = null;
+ }
+ }
+ });
+
// 计算图片滑动边界
function calculateImageBounds() {
if (!currentImage) return;