feat: 背景图新增缩放+透明度编辑,三图文共用统一切换工具栏

This commit is contained in:
Zhukj
2026-04-18 16:14:27 +08:00
parent feef470096
commit 2752943c77
4 changed files with 189 additions and 10 deletions

View File

@@ -58,6 +58,8 @@
18. 可添加多个文本框,以及文本框删除功能
19. 添加图片编辑器工具栏,图片编辑器与文本编辑器共用同一位置工具栏,选中对应元素切换对应编辑面板,点击空白处隐藏面板
20. 当选择文字的时候工具栏对应出现文字编辑器,当选择图片的时候工具栏对应出现图片编辑器,点击其他位置时隐藏工具栏,两个编辑栏的宽高保持和文本编辑器工具栏一致
21. 调整图文工具栏布局,解决顶部遮挡问题,图片透明度滑块添加百分比
22. 给背景图添加编辑器,与层叠图编辑器文本编辑器通用一个工具栏,背景图工具栏功能为:背景图可以放大缩小,可以调节透明度,其余功能不受影响,点击层叠图只显示当前编辑器工具栏

View File

@@ -173,6 +173,14 @@ html, body {
margin: 0;
}
.scale-slider {
width: 120px;
height: 40px;
cursor: pointer;
accent-color: #4a90e2;
margin: 0;
}
/* 字号滚轮选择器 */
.roller-picker {
display: flex;

View File

@@ -55,6 +55,20 @@
<span id="opacityValue">100%</span>
</label>
</div>
<!-- 背景图编辑工具 -->
<div class="toolbar-group bg-tools" id="bgTools">
<label class="toolbar-label">
<span>缩放</span>
<input type="range" id="bgScaleSlider" min="100" max="300" value="100" class="scale-slider">
<span id="bgScaleValue">100%</span>
</label>
<div class="toolbar-divider"></div>
<label class="toolbar-label">
<span>透明度</span>
<input type="range" id="bgOpacitySlider" min="0" max="100" value="100" class="opacity-slider">
<span id="bgOpacityValue">100%</span>
</label>
</div>
</div>
<!-- 图片展示区 -->

View File

@@ -17,8 +17,13 @@
const editorToolbar = document.getElementById('editorToolbar');
const textTools = document.getElementById('textTools');
const imageTools = document.getElementById('imageTools');
const bgTools = document.getElementById('bgTools');
const opacitySlider = document.getElementById('opacitySlider');
const opacityValue = document.getElementById('opacityValue');
const bgScaleSlider = document.getElementById('bgScaleSlider');
const bgScaleValue = document.getElementById('bgScaleValue');
const bgOpacitySlider = document.getElementById('bgOpacitySlider');
const bgOpacityValue = document.getElementById('bgOpacityValue');
const boldBtn = document.getElementById('boldBtn');
const italicBtn = document.getElementById('italicBtn');
const colorPicker = document.getElementById('colorPicker');
@@ -34,6 +39,8 @@
const guideV = document.getElementById('guideV');
let currentImage = null;
let bgImageScale = 1;
let bgImageOpacity = 1;
let selectedInput = null;
let editingInput = null;
let longPressTimer = null;
@@ -45,6 +52,7 @@
let imageStartOffsetX = 0;
let minOffsetX = 0;
let maxOffsetX = 0;
let isInteractingWithBgImage = false;
// 初始化
init();
@@ -113,6 +121,20 @@
}
});
// 背景图缩放滑块
bgScaleSlider.addEventListener('input', (e) => {
bgImageScale = e.target.value / 100;
bgScaleValue.textContent = e.target.value + '%';
updateBgImage();
});
// 背景图透明度滑块
bgOpacitySlider.addEventListener('input', (e) => {
bgImageOpacity = e.target.value / 100;
bgOpacityValue.textContent = e.target.value + '%';
bgImage.style.opacity = bgImageOpacity;
});
// 点击空白区域取消选中
document.addEventListener('click', (e) => {
// 只有点击真正的空白区域(不是输入框,不是图片容器,不是工具栏)才取消选中
@@ -128,6 +150,10 @@
// 点击容器空白处取消选中
function handleContainerClick(e) {
if (isInteractingWithBgImage) {
return;
}
const isBackground = e.target === imageContainer ||
e.target === bgLayer ||
e.target === bgImage ||
@@ -202,7 +228,15 @@
calculateImageBounds();
// 背景图选择后默认居中显示
imageOffsetX = 0;
updateImagePosition();
// 重置缩放
bgImageScale = 1;
bgScaleSlider.value = 100;
bgScaleValue.textContent = '100%';
// 重置透明度
bgImageOpacity = 1;
bgOpacitySlider.value = 100;
bgOpacityValue.textContent = '100%';
updateBgImage();
};
img.src = e.target.result;
};
@@ -523,6 +557,7 @@
editorToolbar.classList.add('active');
textTools.classList.remove('active');
imageTools.classList.add('active');
bgTools.classList.remove('active');
// 更新透明度滑块为当前层叠图的透明度
const currentOpacity = overlayEl.style.opacity || '1';
@@ -569,13 +604,10 @@
}
}
// 更新图片位置
function updateImagePosition() {
// 更新背景图(缩放和透明度)
function updateBgImage() {
if (!currentImage) return;
// 限制滑动范围,确保不露出白板
imageOffsetX = Math.max(minOffsetX, Math.min(maxOffsetX, imageOffsetX));
const containerWidth = imageContainer.clientWidth;
const containerHeight = imageContainer.clientHeight;
const imgRatio = currentImage.naturalWidth / currentImage.naturalHeight;
@@ -591,11 +623,71 @@
imgHeight = imgWidth / imgRatio;
}
// 应用位置
const baseLeft = (containerWidth - imgWidth) / 2;
bgImage.style.width = imgWidth + 'px';
bgImage.style.height = imgHeight + 'px';
// 应用缩放
bgImage.style.width = (imgWidth * bgImageScale) + 'px';
bgImage.style.height = (imgHeight * bgImageScale) + 'px';
// 应用透明度
bgImage.style.opacity = bgImageOpacity;
// 重新计算边界
const scaledWidth = imgWidth * bgImageScale;
const scaledHeight = imgHeight * bgImageScale;
if (scaledWidth > containerWidth) {
minOffsetX = -(scaledWidth - containerWidth) / 2;
maxOffsetX = (scaledWidth - containerWidth) / 2;
} else {
minOffsetX = 0;
maxOffsetX = 0;
}
// 限制当前位置在新范围内
imageOffsetX = Math.max(minOffsetX, Math.min(maxOffsetX, imageOffsetX));
// 重新应用位置(居中 + 偏移)
const baseLeft = (containerWidth - scaledWidth) / 2;
bgImage.style.left = (baseLeft + imageOffsetX) + 'px';
bgImage.style.top = ((containerHeight - scaledHeight) / 2) + 'px';
}
// 更新图片位置
function updateImagePosition() {
if (!currentImage) return;
const containerWidth = imageContainer.clientWidth;
const containerHeight = imageContainer.clientHeight;
const imgRatio = currentImage.naturalWidth / currentImage.naturalHeight;
let imgWidth, imgHeight;
if (imgRatio > containerWidth / containerHeight) {
imgHeight = containerHeight;
imgWidth = imgHeight * imgRatio;
} else {
imgWidth = containerWidth;
imgHeight = imgWidth / imgRatio;
}
// 应用缩放
const scaledWidth = imgWidth * bgImageScale;
const scaledHeight = imgHeight * bgImageScale;
// 限制滑动范围
if (scaledWidth > containerWidth) {
minOffsetX = -(scaledWidth - containerWidth) / 2;
maxOffsetX = (scaledWidth - containerWidth) / 2;
} else {
minOffsetX = 0;
maxOffsetX = 0;
}
imageOffsetX = Math.max(minOffsetX, Math.min(maxOffsetX, imageOffsetX));
// 应用位置
const baseLeft = (containerWidth - scaledWidth) / 2;
bgImage.style.width = scaledWidth + 'px';
bgImage.style.height = scaledHeight + 'px';
bgImage.style.left = (baseLeft + imageOffsetX) + 'px';
bgImage.style.top = ((containerHeight - scaledHeight) / 2) + 'px';
bgImage.style.opacity = bgImageOpacity;
bgImage.style.transform = 'none';
}
@@ -613,6 +705,53 @@
document.addEventListener('mouseup', handleImageEnd);
document.addEventListener('touchend', handleImageEnd);
// 点击背景图显示工具栏
bgImage.addEventListener('click', (e) => {
if (!currentImage) return;
e.stopPropagation();
isInteractingWithBgImage = true;
// 显示背景图工具栏
editorToolbar.classList.add('active');
textTools.classList.remove('active');
imageTools.classList.remove('active');
bgTools.classList.add('active');
// 同步滑块值
bgScaleSlider.value = bgImageScale * 100;
bgScaleValue.textContent = Math.round(bgImageScale * 100) + '%';
bgOpacitySlider.value = bgImageOpacity * 100;
bgOpacityValue.textContent = Math.round(bgImageOpacity * 100) + '%';
setTimeout(() => {
isInteractingWithBgImage = false;
}, 100);
});
bgLayer.addEventListener('click', (e) => {
if (!currentImage) return;
e.stopPropagation();
isInteractingWithBgImage = true;
// 显示背景图工具栏
editorToolbar.classList.add('active');
textTools.classList.remove('active');
imageTools.classList.remove('active');
bgTools.classList.add('active');
// 同步滑块值
bgScaleSlider.value = bgImageScale * 100;
bgScaleValue.textContent = Math.round(bgImageScale * 100) + '%';
bgOpacitySlider.value = bgImageOpacity * 100;
bgOpacityValue.textContent = Math.round(bgImageOpacity * 100) + '%';
setTimeout(() => {
isInteractingWithBgImage = false;
}, 100);
});
function handleImageStart(e) {
// 如果点击的是输入框,不处理图片滑动
if (e.target.closest('.text-input')) {
@@ -642,9 +781,22 @@
}
isDraggingImage = true;
isInteractingWithBgImage = true;
imageStartX = clientX;
imageStartOffsetX = imageOffsetX;
// 显示背景图工具栏
editorToolbar.classList.add('active');
textTools.classList.remove('active');
imageTools.classList.remove('active');
bgTools.classList.add('active');
// 同步滑块值
bgScaleSlider.value = bgImageScale * 100;
bgScaleValue.textContent = Math.round(bgImageScale * 100) + '%';
bgOpacitySlider.value = bgImageOpacity * 100;
bgOpacityValue.textContent = Math.round(bgImageOpacity * 100) + '%';
if (isTouch) {
e.preventDefault();
}
@@ -670,6 +822,7 @@
function handleImageEnd() {
isDraggingImage = false;
isInteractingWithBgImage = false;
}
}
@@ -863,6 +1016,7 @@
editorToolbar.classList.add('active');
textTools.classList.add('active');
imageTools.classList.remove('active');
bgTools.classList.remove('active');
// 更新工具栏状态
updateToolbarState(element);
@@ -885,6 +1039,7 @@
editorToolbar.classList.add('active');
textTools.classList.add('active');
imageTools.classList.remove('active');
bgTools.classList.remove('active');
// 更新工具栏状态
updateToolbarState(element);