feat: 背景图新增缩放+透明度编辑,三图文共用统一切换工具栏
This commit is contained in:
@@ -58,6 +58,8 @@
|
||||
18. 可添加多个文本框,以及文本框删除功能
|
||||
19. 添加图片编辑器工具栏,图片编辑器与文本编辑器共用同一位置工具栏,选中对应元素切换对应编辑面板,点击空白处隐藏面板
|
||||
20. 当选择文字的时候工具栏对应出现文字编辑器,当选择图片的时候工具栏对应出现图片编辑器,点击其他位置时隐藏工具栏,两个编辑栏的宽高保持和文本编辑器工具栏一致
|
||||
21. 调整图文工具栏布局,解决顶部遮挡问题,图片透明度滑块添加百分比
|
||||
22. 给背景图添加编辑器,与层叠图编辑器文本编辑器通用一个工具栏,背景图工具栏功能为:背景图可以放大缩小,可以调节透明度,其余功能不受影响,点击层叠图只显示当前编辑器工具栏
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
14
index.html
14
index.html
@@ -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>
|
||||
|
||||
<!-- 图片展示区 -->
|
||||
|
||||
175
js/script.js
175
js/script.js
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user