diff --git a/README.md b/README.md index a9810d3..9403c5a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ 14. 字号逻辑调整,输入框字号只在自身原本字号上增减 15. 文本输入框添加辅助线,完善辅助线的吸附逻辑更加精准,辅助线颜色为红色 16. 顶部工具栏新增层叠图,可添加多张层叠图,单击选中状态可缩放删除自由移动位置,置于背景图之上,输入框与层叠图点击只能同时选中一个 - +17. 层叠图添加辅助线 diff --git a/css/style.css b/css/style.css index 2ad4c6d..dbb5885 100644 --- a/css/style.css +++ b/css/style.css @@ -229,7 +229,7 @@ html, body { left: 0; width: 100%; height: 100%; - pointer-events: auto; + pointer-events: none; z-index: 6; } diff --git a/js/script.js b/js/script.js index 8f98eed..61a676e 100644 --- a/js/script.js +++ b/js/script.js @@ -251,6 +251,8 @@ function handleDragStart(e) { if (e.target.classList.contains('delete-btn') || e.target.classList.contains('resize-handle')) return; + e.stopPropagation(); // 阻止事件冒泡到 imageContainer + const isTouch = e.type === 'touchstart'; const clientX = isTouch ? e.touches[0].clientX : e.clientX; const clientY = isTouch ? e.touches[0].clientY : e.clientY; @@ -263,6 +265,9 @@ overlayEl.classList.add('dragging'); + // 拖动时设置为选中状态 + selectOverlay(overlayEl); + document.addEventListener('mousemove', handleDragMove); document.addEventListener('mouseup', handleDragEnd); document.addEventListener('touchmove', handleDragMove, { passive: false }); @@ -284,8 +289,17 @@ 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)); + // 使用吸附功能 + const snapped = getSnapPosition(overlayEl, newLeft, newTop); + newLeft = snapped.left; + newTop = snapped.top; + + // 显示对齐辅助线 + if (snapped.showGuide) { + showAlignmentGuides(overlayEl); + } else { + hideGuides(); + } overlayEl.style.left = newLeft + 'px'; overlayEl.style.top = newTop + 'px'; @@ -297,6 +311,7 @@ function handleDragEnd() { isDragging = false; overlayEl.classList.remove('dragging'); + hideGuides(); document.removeEventListener('mousemove', handleDragMove); document.removeEventListener('mouseup', handleDragEnd); @@ -897,8 +912,10 @@ snapYPos = cHeight; } - // 检测其他元素对齐 + // 检测其他元素对齐(输入框和层叠图) const otherInputs = [input1, input2].filter(el => el && el !== draggedEl); + const otherOverlays = Array.from(overlayLayer.querySelectorAll('.overlay-image')) + .filter(el => el !== draggedEl); for (const otherEl of otherInputs) { if (!otherEl) continue; @@ -959,6 +976,66 @@ } } + // 检测层叠图之间对齐 + for (const otherEl of otherOverlays) { + if (!otherEl) continue; + + const oLeft = otherEl.offsetLeft; + const oTop = otherEl.offsetTop; + const oWidth = otherEl.offsetWidth; + const oHeight = otherEl.offsetHeight; + const oRight = oLeft + oWidth; + const oBottom = oTop + oHeight; + const oCenterX = oLeft + oWidth / 2; + const oCenterY = oTop + oHeight / 2; + + // 垂直边缘对齐 + if (Math.abs(elLeft - oLeft) < threshold) { + snapX = true; + snapXPos = oLeft; + } + if (Math.abs(elRight - oRight) < threshold) { + snapX = true; + snapXPos = oRight - elWidth; + } + if (Math.abs(elLeft - oRight) < threshold) { + snapX = true; + snapXPos = oRight; + } + if (Math.abs(elRight - oLeft) < threshold) { + snapX = true; + snapXPos = oLeft - elWidth; + } + + // 水平边缘对齐 + if (Math.abs(elTop - oTop) < threshold) { + snapY = true; + snapYPos = oTop; + } + if (Math.abs(elBottom - oBottom) < threshold) { + snapY = true; + snapYPos = oBottom - elHeight; + } + if (Math.abs(elTop - oBottom) < threshold) { + snapY = true; + snapYPos = oBottom; + } + if (Math.abs(elBottom - oTop) < threshold) { + snapY = true; + snapYPos = oTop - elHeight; + } + + // 中心对齐 + if (Math.abs(elCenterX - oCenterX) < threshold) { + snapX = true; + snapXPos = oCenterX - elWidth / 2; + } + if (Math.abs(elCenterY - oCenterY) < threshold) { + snapY = true; + snapYPos = oCenterY - elHeight / 2; + } + } + // 应用吸附 if (snapX && snapXPos !== null) { snapLeft = snapXPos; @@ -1030,6 +1107,8 @@ // 其他输入框对齐检测 const otherInputs = [input1, input2].filter(el => el && el !== draggedEl); + const otherOverlays = Array.from(overlayLayer.querySelectorAll('.overlay-image')) + .filter(el => el !== draggedEl); for (const otherEl of otherInputs) { if (!otherEl) continue; @@ -1080,6 +1159,56 @@ } } + // 其他层叠图对齐检测 + for (const otherEl of otherOverlays) { + if (!otherEl) continue; + + const oLeft = otherEl.offsetLeft; + const oTop = otherEl.offsetTop; + const oWidth = otherEl.offsetWidth; + const oHeight = otherEl.offsetHeight; + const oRight = oLeft + oWidth; + const oBottom = oTop + oHeight; + const oCenterX = oLeft + oWidth / 2; + const oCenterY = oTop + oHeight / 2; + + // 垂直边缘对齐 + if (Math.abs(elLeft - oLeft) < threshold) { + guideVPos = oLeft; + } + if (Math.abs(elRight - oRight) < threshold) { + guideVPos = oRight; + } + if (Math.abs(elLeft - oRight) < threshold) { + guideVPos = oRight; + } + if (Math.abs(elRight - oLeft) < threshold) { + guideVPos = oLeft; + } + + // 水平边缘对齐 + if (Math.abs(elTop - oTop) < threshold) { + guideHPos = oTop; + } + if (Math.abs(elBottom - oBottom) < threshold) { + guideHPos = oBottom; + } + if (Math.abs(elTop - oBottom) < threshold) { + guideHPos = oBottom; + } + if (Math.abs(elBottom - oTop) < threshold) { + guideHPos = oTop; + } + + // 中心对齐 + if (Math.abs(elCenterX - oCenterX) < threshold) { + guideVPos = oCenterX; + } + if (Math.abs(elCenterY - oCenterY) < threshold) { + guideHPos = oCenterY; + } + } + if (guideHPos !== null) { guideH.style.top = guideHPos + 'px'; guideH.classList.add('visible');