diff --git a/css/style.css b/css/style.css index 49a8b74..e49d0a1 100644 --- a/css/style.css +++ b/css/style.css @@ -513,3 +513,23 @@ 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; +} diff --git a/index.html b/index.html index 8e7937a..81a0974 100644 --- a/index.html +++ b/index.html @@ -80,6 +80,8 @@
+ +
diff --git a/js/script.js b/js/script.js index 4fc97d6..2c7e68d 100644 --- a/js/script.js +++ b/js/script.js @@ -38,6 +38,7 @@ 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; @@ -207,6 +208,9 @@ // 初始化文本编辑器工具栏 initTextEditorToolbar(); + // 初始化键盘微调功能 + initKeyboardNudge(); + // 加载默认背景图 loadDefaultBackground(); @@ -450,27 +454,23 @@ const dx = clientX - startX; const dy = clientY - startY; - const moveDistance = Math.sqrt(dx * dx + dy * dy); - if (moveDistance > 5) { - let newLeft = startLeft + dx; - let newTop = startTop + dy; + // 移除移动距离限制,实现 1px 精确移动 + 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; - // 显示对齐辅助线 - if (snapped.showGuide) { - showAlignmentGuides(overlayEl); - } else { - hideGuides(); - } - - overlayEl.style.left = newLeft + 'px'; - overlayEl.style.top = newTop + 'px'; + // 显示对齐辅助线(网格线已在 getSnapPosition 中显示) + if (snapped.showGuide) { + showAlignmentGuides(overlayEl); } + overlayEl.style.left = newLeft + 'px'; + overlayEl.style.top = newTop + 'px'; + e.preventDefault(); } @@ -974,7 +974,8 @@ Math.pow(clientY - clickStartY, 2) ); - if (moveDistance > 5) { + // 只要有移动就开始拖拽,实现 1px 精确移动 + if (moveDistance > 0) { hasMoved = true; if (dragTimer) { clearTimeout(dragTimer); @@ -997,11 +998,9 @@ element.style.left = snapped.left + 'px'; element.style.top = snapped.top + 'px'; - // 显示对齐辅助线 + // 显示对齐辅助线(网格线已在 getSnapPosition 中显示) if (snapped.showGuide) { showAlignmentGuides(element); - } else { - hideGuides(); } } @@ -1217,11 +1216,16 @@ const cCenterX = cWidth / 2; const cCenterY = cHeight / 2; - const threshold = 5; + const threshold = 1; let snapLeft = newLeft; let snapTop = newTop; let showGuide = false; + // 显示网格线 + if (gridLines) { + gridLines.classList.add('visible'); + } + // 水平垂直分别检测 let snapX = false; let snapY = false; @@ -1257,7 +1261,7 @@ } // 检测其他元素对齐(输入框和层叠图) - const otherInputs = [input1, input2].filter(el => el && el !== draggedEl); + const otherInputs = [input1, input2, input3].filter(el => el && el !== draggedEl); const otherOverlays = Array.from(overlayLayer.querySelectorAll('.overlay-image')) .filter(el => el !== draggedEl); @@ -1420,7 +1424,7 @@ const elCenterX = elLeft + elWidth / 2; const elCenterY = elTop + elHeight / 2; - const threshold = 5; + const threshold = 1; let guideHPos = null; let guideVPos = null; @@ -1571,6 +1575,9 @@ function hideGuides() { guideH.classList.remove('visible'); guideV.classList.remove('visible'); + if (gridLines) { + gridLines.classList.remove('visible'); + } } // 取消所有选中 @@ -1738,6 +1745,53 @@ }, { 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();