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 }); } // 显示右键菜单