From 10bbf83e82fcbb7887ee7c2806c50ddddf4cd4d2 Mon Sep 17 00:00:00 2001
From: Zhukj <3262118957>
Date: Sat, 18 Apr 2026 13:56:53 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=9B=BE=E7=89=87?=
=?UTF-8?q?=E7=BC=96=E8=BE=91=E5=99=A8=E5=B7=A5=E5=85=B7=E6=A0=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 ++
css/style.css | 31 +++++++++++++++++
index.html | 12 +++++++
js/script.js | 93 ++++++++++++++++++++++++++++++++++++++++-----------
4 files changed, 119 insertions(+), 19 deletions(-)
diff --git a/README.md b/README.md
index 9403c5a..c7777f6 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,8 @@
15. 文本输入框添加辅助线,完善辅助线的吸附逻辑更加精准,辅助线颜色为红色
16. 顶部工具栏新增层叠图,可添加多张层叠图,单击选中状态可缩放删除自由移动位置,置于背景图之上,输入框与层叠图点击只能同时选中一个
17. 层叠图添加辅助线
+18. 可添加多个文本框,以及文本框删除功能
+19. 添加图片编辑器工具栏,图片编辑器与文本编辑器共用同一位置工具栏,选中对应元素切换对应编辑面板,点击空白出隐藏面板,修复层叠图不可移动问题
diff --git a/css/style.css b/css/style.css
index 3a5053a..7d4ca5a 100644
--- a/css/style.css
+++ b/css/style.css
@@ -17,6 +17,7 @@ html, body {
display: flex;
flex-direction: column;
height: 100vh;
+ position: relative;
}
/* 顶部工具栏 */
@@ -81,6 +82,10 @@ html, body {
/* 文本编辑器工具栏 */
.text-editor-toolbar {
+ position: absolute;
+ top: 49px;
+ left: 0;
+ right: 0;
display: flex;
justify-content: center;
align-items: center;
@@ -92,6 +97,7 @@ html, body {
visibility: hidden;
opacity: 0;
transition: opacity 0.2s;
+ z-index: 100;
}
.text-editor-toolbar.active {
@@ -99,6 +105,31 @@ html, body {
opacity: 1;
}
+/* 图片编辑器工具栏 - 与文本编辑器共享位置 */
+.image-editor-toolbar {
+ position: absolute;
+ top: 49px;
+ left: 0;
+ right: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 16px;
+ background: #fff;
+ border-bottom: 1px solid #e0e0e0;
+ flex-shrink: 0;
+ visibility: hidden;
+ opacity: 0;
+ transition: opacity 0.2s;
+ z-index: 100;
+}
+
+.image-editor-toolbar.active {
+ visibility: visible;
+ opacity: 1;
+}
+
.toolbar-btn {
width: 40px;
height: 40px;
diff --git a/index.html b/index.html
index 105d03c..457d306 100644
--- a/index.html
+++ b/index.html
@@ -46,6 +46,18 @@
+
+
diff --git a/js/script.js b/js/script.js
index 7ab1712..c1f5d97 100644
--- a/js/script.js
+++ b/js/script.js
@@ -15,11 +15,13 @@
const longPressTip = document.getElementById('longPressTip');
const canvasArea = document.getElementById('canvasArea');
const textEditorToolbar = document.getElementById('textEditorToolbar');
+ const imageEditorToolbar = document.getElementById('imageEditorToolbar');
const boldBtn = document.getElementById('boldBtn');
const italicBtn = document.getElementById('italicBtn');
const colorPicker = document.getElementById('colorPicker');
const fontPicker = document.getElementById('fontPicker');
const fontSizePicker = document.getElementById('fontSizePicker');
+ const opacityPicker = document.getElementById('opacityPicker');
const overlayLayer = document.getElementById('overlayLayer');
const overlayInput = document.createElement('input');
overlayInput.type = 'file';
@@ -142,8 +144,9 @@
}
// 隐藏辅助线
hideGuides();
- // 隐藏文本编辑器工具栏
+ // 隐藏文本编辑器工具栏和图片编辑器工具栏
textEditorToolbar.classList.remove('active');
+ imageEditorToolbar.classList.remove('active');
}
// 点击空白区域(不是层叠图和输入框)
@@ -307,12 +310,16 @@
let isDragging = false;
let isResizing = false;
let startX, startY, startLeft, startTop, startWidth, startHeight;
+ let hasMoved = false;
const minSize = 30;
// 点击选中(区分点击和拖拽)
overlayEl.addEventListener('click', (e) => {
e.stopPropagation();
- selectOverlay(overlayEl);
+ if (!hasMoved) {
+ selectOverlay(overlayEl);
+ }
+ hasMoved = false;
});
// 拖拽移动(不选中也可拖动)
@@ -329,6 +336,7 @@
const clientY = isTouch ? e.touches[0].clientY : e.clientY;
isDragging = true;
+ hasMoved = false;
startX = clientX;
startY = clientY;
startLeft = overlayEl.offsetLeft;
@@ -336,7 +344,6 @@
overlayEl.classList.add('dragging');
- // 拖动时设置为选中状态
selectOverlay(overlayEl);
document.addEventListener('mousemove', handleDragMove);
@@ -357,21 +364,10 @@
const moveDistance = Math.sqrt(dx * dx + dy * dy);
if (moveDistance > 5) {
+ hasMoved = true;
let newLeft = startLeft + dx;
let newTop = startTop + dy;
- // 使用吸附功能
- 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';
}
@@ -382,7 +378,6 @@
function handleDragEnd() {
isDragging = false;
overlayEl.classList.remove('dragging');
- hideGuides();
document.removeEventListener('mousemove', handleDragMove);
document.removeEventListener('mouseup', handleDragEnd);
@@ -506,6 +501,15 @@
}
selectedOverlay = overlayEl;
overlayEl.classList.add('selected');
+
+ // 显示图片编辑器工具栏,隐藏文本编辑器工具栏
+ imageEditorToolbar.classList.add('active');
+ textEditorToolbar.classList.remove('active');
+
+ // 更新透明度值显示
+ const currentOpacity = Math.round(parseFloat(overlayEl.style.opacity || 1) * 100);
+ opacityValue = currentOpacity;
+ opacityValueEl.textContent = currentOpacity;
}
// 计算图片滑动边界
@@ -597,6 +601,11 @@
return;
}
+ // 如果点击的是层叠图,不处理图片滑动(让层叠图的拖拽处理)
+ if (e.target.closest('.overlay-image')) {
+ return;
+ }
+
// 如果正在编辑,先退出编辑状态
if (editingInput) {
deselectAll();
@@ -837,8 +846,9 @@
element.classList.remove('editing');
element.contentEditable = 'false';
- // 显示文本编辑器工具栏
+ // 显示文本编辑器工具栏,隐藏图片编辑器工具栏
textEditorToolbar.classList.add('active');
+ imageEditorToolbar.classList.remove('active');
// 更新工具栏状态
updateToolbarState(element);
@@ -857,8 +867,9 @@
element.classList.remove('selected');
element.contentEditable = 'true';
- // 显示文本编辑器工具栏
+ // 显示文本编辑器工具栏,隐藏图片编辑器工具栏
textEditorToolbar.classList.add('active');
+ imageEditorToolbar.classList.remove('active');
// 更新工具栏状态
updateToolbarState(element);
@@ -1314,8 +1325,13 @@
selectedInput.classList.remove('selected');
selectedInput = null;
}
- // 隐藏文本编辑器工具栏
+ if (selectedOverlay) {
+ selectedOverlay.classList.remove('selected');
+ selectedOverlay = null;
+ }
+ // 隐藏所有工具栏
textEditorToolbar.classList.remove('active');
+ imageEditorToolbar.classList.remove('active');
}
// 初始化长按保存(只在3:5预览区域)
@@ -1468,6 +1484,45 @@
updateFontSize(currentSize + delta, true);
}
}, { passive: false });
+
+ // 透明度选择器 - 密码锁式滚轮
+ let opacityValue = 100;
+ const opacityRoller = document.getElementById('opacityPicker');
+ const opacityValueEl = opacityRoller.querySelector('.roller-value');
+ const opacityUpBtn = opacityRoller.querySelector('.roller-up');
+ const opacityDownBtn = opacityRoller.querySelector('.roller-down');
+
+ function updateOpacity(newOpacity, forceUpdate = false) {
+ newOpacity = Math.max(0, Math.min(100, newOpacity));
+ if (selectedOverlay) {
+ selectedOverlay.style.opacity = newOpacity / 100;
+ opacityValue = newOpacity;
+ opacityValueEl.textContent = newOpacity;
+ } else {
+ opacityValue = newOpacity;
+ opacityValueEl.textContent = newOpacity;
+ }
+ }
+
+ opacityUpBtn.addEventListener('click', () => {
+ if (selectedOverlay) {
+ updateOpacity(opacityValue + 5, true);
+ }
+ });
+
+ opacityDownBtn.addEventListener('click', () => {
+ if (selectedOverlay) {
+ updateOpacity(opacityValue - 5, true);
+ }
+ });
+
+ opacityRoller.addEventListener('wheel', (e) => {
+ e.preventDefault();
+ if (selectedOverlay) {
+ const delta = e.deltaY > 0 ? -5 : 5;
+ updateOpacity(opacityValue + delta, true);
+ }
+ }, { passive: false });
}
// 显示右键菜单