feat: 为输入框添加拖动对齐辅助线,优化排版定位体验

This commit is contained in:
Zhukj
2026-04-17 09:12:22 +08:00
parent 52cc784478
commit dfd0528151
4 changed files with 306 additions and 3 deletions

View File

@@ -18,6 +18,8 @@
const colorPicker = document.getElementById('colorPicker');
const fontPicker = document.getElementById('fontPicker');
const fontSizePicker = document.getElementById('fontSizePicker');
const guideH = document.getElementById('guideH');
const guideV = document.getElementById('guideV');
let currentImage = null;
let selectedInput = null;
@@ -356,8 +358,20 @@
if (isDragging) {
const dx = clientX - startX;
const dy = clientY - startY;
element.style.left = (startLeft + dx) + 'px';
element.style.top = (startTop + dy) + 'px';
const newLeft = startLeft + dx;
const newTop = startTop + dy;
// 检测对齐并吸附
const snapped = getSnapPosition(element, newLeft, newTop);
element.style.left = snapped.left + 'px';
element.style.top = snapped.top + 'px';
// 显示对齐辅助线
if (snapped.showGuide) {
showAlignmentGuides(element);
} else {
hideGuides();
}
}
// 阻止事件冒泡,避免触发图片滑动
@@ -385,6 +399,7 @@
if (isDragging) {
isDragging = false;
element.classList.remove('dragging');
hideGuides();
}
// 阻止事件冒泡
@@ -507,6 +522,146 @@
}
}
// 获取吸附后的位置
function getSnapPosition(draggedEl, newLeft, newTop) {
const container = imageContainer;
const containerRect = container.getBoundingClientRect();
const elWidth = draggedEl.offsetWidth;
const elHeight = draggedEl.offsetHeight;
// 计算元素各参考点相对容器的位置
const elCenterX = newLeft + elWidth / 2;
const elCenterY = newTop + elHeight / 2;
const elLeft = newLeft;
const elRight = newLeft + elWidth;
const elTop = newTop;
const elBottom = newTop + elHeight;
// 容器尺寸
const cWidth = containerRect.width;
const cHeight = containerRect.height;
const cCenterX = cWidth / 2;
const cCenterY = cHeight / 2;
const threshold = 5;
let snapLeft = newLeft;
let snapTop = newTop;
let showGuide = false;
// 水平垂直分别检测
let snapX = false;
let snapY = false;
let snapXPos = null;
let snapYPos = null;
// 检测容器中心对齐
if (Math.abs(elCenterX - cCenterX) < threshold) {
snapX = true;
snapXPos = cCenterX;
}
if (Math.abs(elCenterY - cCenterY) < threshold) {
snapY = true;
snapYPos = cCenterY;
}
// 检测容器边缘对齐
if (Math.abs(elLeft) < threshold) {
snapX = true;
snapXPos = 0;
}
if (Math.abs(elRight - cWidth) < threshold) {
snapX = true;
snapXPos = cWidth;
}
if (Math.abs(elTop) < threshold) {
snapY = true;
snapYPos = 0;
}
if (Math.abs(elBottom - cHeight) < threshold) {
snapY = true;
snapYPos = cHeight;
}
// 检测其他元素对齐
const otherInputs = [input1, input2].filter(el => el && el !== draggedEl);
for (const otherEl of otherInputs) {
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;
showGuide = true;
}
if (snapY && snapYPos !== null) {
snapTop = snapYPos;
showGuide = true;
}
// 边界限制
snapLeft = Math.max(0, Math.min(snapLeft, cWidth - elWidth));
snapTop = Math.max(0, Math.min(snapTop, cHeight - elHeight));
return { left: snapLeft, top: snapTop, showGuide };
}
// RGB转十六进制
function rgbToHex(rgb) {
if (rgb.startsWith('#')) return rgb;
@@ -515,6 +670,122 @@
return '#' + [match[1], match[2], match[3]].map(x => parseInt(x).toString(16).padStart(2, '0')).join('');
}
// 对齐辅助线 - 水平和垂直居中、对齐其他元素
function showAlignmentGuides(draggedEl) {
const container = imageContainer;
const cWidth = container.offsetWidth;
const cHeight = container.offsetHeight;
const elWidth = draggedEl.offsetWidth;
const elHeight = draggedEl.offsetHeight;
const elLeft = draggedEl.offsetLeft;
const elTop = draggedEl.offsetTop;
const elRight = elLeft + elWidth;
const elBottom = elTop + elHeight;
const elCenterX = elLeft + elWidth / 2;
const elCenterY = elTop + elHeight / 2;
const threshold = 5;
let guideHPos = null;
let guideVPos = null;
// 容器中心对齐
const cCenterX = cWidth / 2;
const cCenterY = cHeight / 2;
if (Math.abs(elCenterX - cCenterX) < threshold) {
guideVPos = cCenterX;
}
if (Math.abs(elCenterY - cCenterY) < threshold) {
guideHPos = cCenterY;
}
// 容器边缘对齐
if (Math.abs(elLeft) < threshold) {
guideVPos = 0;
}
if (Math.abs(elRight - cWidth) < threshold) {
guideVPos = cWidth;
}
if (Math.abs(elTop) < threshold) {
guideHPos = 0;
}
if (Math.abs(elBottom - cHeight) < threshold) {
guideHPos = cHeight;
}
// 其他输入框对齐检测
const otherInputs = [input1, input2].filter(el => el && el !== draggedEl);
for (const otherEl of otherInputs) {
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');
} else {
guideH.classList.remove('visible');
}
if (guideVPos !== null) {
guideV.style.left = guideVPos + 'px';
guideV.classList.add('visible');
} else {
guideV.classList.remove('visible');
}
}
function hideGuides() {
guideH.classList.remove('visible');
guideV.classList.remove('visible');
}
// 取消所有选中
function deselectAll() {
if (editingInput) {