feat: 新增文字移动实时显示网格辅助线
This commit is contained in:
@@ -513,3 +513,23 @@ html, body {
|
|||||||
.guide-line.visible {
|
.guide-line.visible {
|
||||||
display: block;
|
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,6 +80,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 层叠图图层 -->
|
<!-- 层叠图图层 -->
|
||||||
<div class="overlay-layer" id="overlayLayer"></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-horizontal" id="guideH"></div>
|
||||||
<div class="guide-line guide-vertical" id="guideV"></div>
|
<div class="guide-line guide-vertical" id="guideV"></div>
|
||||||
|
|||||||
80
js/script.js
80
js/script.js
@@ -38,6 +38,7 @@
|
|||||||
document.body.appendChild(overlayInput);
|
document.body.appendChild(overlayInput);
|
||||||
const guideH = document.getElementById('guideH');
|
const guideH = document.getElementById('guideH');
|
||||||
const guideV = document.getElementById('guideV');
|
const guideV = document.getElementById('guideV');
|
||||||
|
const gridLines = document.getElementById('gridLines');
|
||||||
|
|
||||||
let currentImage = null;
|
let currentImage = null;
|
||||||
let bgImageScale = 1;
|
let bgImageScale = 1;
|
||||||
@@ -207,6 +208,9 @@
|
|||||||
// 初始化文本编辑器工具栏
|
// 初始化文本编辑器工具栏
|
||||||
initTextEditorToolbar();
|
initTextEditorToolbar();
|
||||||
|
|
||||||
|
// 初始化键盘微调功能
|
||||||
|
initKeyboardNudge();
|
||||||
|
|
||||||
// 加载默认背景图
|
// 加载默认背景图
|
||||||
loadDefaultBackground();
|
loadDefaultBackground();
|
||||||
|
|
||||||
@@ -450,8 +454,7 @@
|
|||||||
const dx = clientX - startX;
|
const dx = clientX - startX;
|
||||||
const dy = clientY - startY;
|
const dy = clientY - startY;
|
||||||
|
|
||||||
const moveDistance = Math.sqrt(dx * dx + dy * dy);
|
// 移除移动距离限制,实现 1px 精确移动
|
||||||
if (moveDistance > 5) {
|
|
||||||
let newLeft = startLeft + dx;
|
let newLeft = startLeft + dx;
|
||||||
let newTop = startTop + dy;
|
let newTop = startTop + dy;
|
||||||
|
|
||||||
@@ -460,16 +463,13 @@
|
|||||||
newLeft = snapped.left;
|
newLeft = snapped.left;
|
||||||
newTop = snapped.top;
|
newTop = snapped.top;
|
||||||
|
|
||||||
// 显示对齐辅助线
|
// 显示对齐辅助线(网格线已在 getSnapPosition 中显示)
|
||||||
if (snapped.showGuide) {
|
if (snapped.showGuide) {
|
||||||
showAlignmentGuides(overlayEl);
|
showAlignmentGuides(overlayEl);
|
||||||
} else {
|
|
||||||
hideGuides();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
overlayEl.style.left = newLeft + 'px';
|
overlayEl.style.left = newLeft + 'px';
|
||||||
overlayEl.style.top = newTop + 'px';
|
overlayEl.style.top = newTop + 'px';
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
@@ -974,7 +974,8 @@
|
|||||||
Math.pow(clientY - clickStartY, 2)
|
Math.pow(clientY - clickStartY, 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (moveDistance > 5) {
|
// 只要有移动就开始拖拽,实现 1px 精确移动
|
||||||
|
if (moveDistance > 0) {
|
||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
if (dragTimer) {
|
if (dragTimer) {
|
||||||
clearTimeout(dragTimer);
|
clearTimeout(dragTimer);
|
||||||
@@ -997,11 +998,9 @@
|
|||||||
element.style.left = snapped.left + 'px';
|
element.style.left = snapped.left + 'px';
|
||||||
element.style.top = snapped.top + 'px';
|
element.style.top = snapped.top + 'px';
|
||||||
|
|
||||||
// 显示对齐辅助线
|
// 显示对齐辅助线(网格线已在 getSnapPosition 中显示)
|
||||||
if (snapped.showGuide) {
|
if (snapped.showGuide) {
|
||||||
showAlignmentGuides(element);
|
showAlignmentGuides(element);
|
||||||
} else {
|
|
||||||
hideGuides();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1217,11 +1216,16 @@
|
|||||||
const cCenterX = cWidth / 2;
|
const cCenterX = cWidth / 2;
|
||||||
const cCenterY = cHeight / 2;
|
const cCenterY = cHeight / 2;
|
||||||
|
|
||||||
const threshold = 5;
|
const threshold = 1;
|
||||||
let snapLeft = newLeft;
|
let snapLeft = newLeft;
|
||||||
let snapTop = newTop;
|
let snapTop = newTop;
|
||||||
let showGuide = false;
|
let showGuide = false;
|
||||||
|
|
||||||
|
// 显示网格线
|
||||||
|
if (gridLines) {
|
||||||
|
gridLines.classList.add('visible');
|
||||||
|
}
|
||||||
|
|
||||||
// 水平垂直分别检测
|
// 水平垂直分别检测
|
||||||
let snapX = false;
|
let snapX = false;
|
||||||
let snapY = 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'))
|
const otherOverlays = Array.from(overlayLayer.querySelectorAll('.overlay-image'))
|
||||||
.filter(el => el !== draggedEl);
|
.filter(el => el !== draggedEl);
|
||||||
|
|
||||||
@@ -1420,7 +1424,7 @@
|
|||||||
const elCenterX = elLeft + elWidth / 2;
|
const elCenterX = elLeft + elWidth / 2;
|
||||||
const elCenterY = elTop + elHeight / 2;
|
const elCenterY = elTop + elHeight / 2;
|
||||||
|
|
||||||
const threshold = 5;
|
const threshold = 1;
|
||||||
let guideHPos = null;
|
let guideHPos = null;
|
||||||
let guideVPos = null;
|
let guideVPos = null;
|
||||||
|
|
||||||
@@ -1571,6 +1575,9 @@
|
|||||||
function hideGuides() {
|
function hideGuides() {
|
||||||
guideH.classList.remove('visible');
|
guideH.classList.remove('visible');
|
||||||
guideV.classList.remove('visible');
|
guideV.classList.remove('visible');
|
||||||
|
if (gridLines) {
|
||||||
|
gridLines.classList.remove('visible');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消所有选中
|
// 取消所有选中
|
||||||
@@ -1738,6 +1745,53 @@
|
|||||||
}, { passive: false });
|
}, { 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) {
|
function showContextMenu(x, y) {
|
||||||
hideContextMenu();
|
hideContextMenu();
|
||||||
|
|||||||
Reference in New Issue
Block a user