diff --git a/README.md b/README.md
index 5817034..5351728 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,8 @@
11. 添加图片尺寸不一致时可以左右滑动图片调整位置,并且图片反转拖拽
12. 电脑端右键保存/复制/预览图片,手机端长按屏幕弹窗保存/复制/预览图片,可预览
13. 顶部工具栏下方16px的位置,固定放置文本编辑器工具栏,加粗,斜体,字体颜色,字体,字号,可编辑文字样式
-14.
+14. 字号逻辑调整,输入框字号只在自身原本字号上增减
+
diff --git a/css/style.css b/css/style.css
index 6da2311..62db4aa 100644
--- a/css/style.css
+++ b/css/style.css
@@ -355,3 +355,31 @@ html, body {
.context-menu-item:active {
background: #e8e8e8;
}
+
+/* 拖拽辅助线 */
+.guide-line {
+ position: absolute;
+ pointer-events: none;
+ z-index: 1000;
+ display: none;
+}
+
+.guide-horizontal {
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: #4a90e2;
+ box-shadow: 0 0 4px rgba(74, 144, 226, 0.5);
+}
+
+.guide-vertical {
+ top: 0;
+ bottom: 0;
+ width: 1px;
+ background: #4a90e2;
+ box-shadow: 0 0 4px rgba(74, 144, 226, 0.5);
+}
+
+.guide-line.visible {
+ display: block;
+}
diff --git a/index.html b/index.html
index c36fb5b..a9d4112 100644
--- a/index.html
+++ b/index.html
@@ -53,6 +53,9 @@
+
+
+
diff --git a/js/script.js b/js/script.js
index 08997f9..a8d429a 100644
--- a/js/script.js
+++ b/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) {