feat: 为输入框添加拖动对齐辅助线,优化排版定位体验
This commit is contained in:
275
js/script.js
275
js/script.js
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user