feat: 顶部工具栏新增叠加图片按钮
This commit is contained in:
291
js/script.js
291
js/script.js
@@ -2,6 +2,7 @@
|
||||
(function() {
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const addImageBtn = document.getElementById('addImageBtn');
|
||||
const addOverlayBtn = document.getElementById('addOverlayBtn');
|
||||
const saveBtn = document.getElementById('saveBtn');
|
||||
const imageContainer = document.getElementById('imageContainer');
|
||||
const bgLayer = document.getElementById('bgLayer');
|
||||
@@ -18,6 +19,12 @@
|
||||
const colorPicker = document.getElementById('colorPicker');
|
||||
const fontPicker = document.getElementById('fontPicker');
|
||||
const fontSizePicker = document.getElementById('fontSizePicker');
|
||||
const overlayLayer = document.getElementById('overlayLayer');
|
||||
const overlayInput = document.createElement('input');
|
||||
overlayInput.type = 'file';
|
||||
overlayInput.accept = 'image/*';
|
||||
overlayInput.hidden = true;
|
||||
document.body.appendChild(overlayInput);
|
||||
const guideH = document.getElementById('guideH');
|
||||
const guideV = document.getElementById('guideV');
|
||||
|
||||
@@ -43,6 +50,20 @@
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
// 层叠图按钮
|
||||
addOverlayBtn.addEventListener('click', () => {
|
||||
overlayInput.click();
|
||||
});
|
||||
|
||||
// 层叠图文件选择
|
||||
overlayInput.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
loadOverlayImage(file);
|
||||
}
|
||||
overlayInput.value = '';
|
||||
});
|
||||
|
||||
// 文件选择
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
@@ -139,6 +160,276 @@
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
// 加载层叠图
|
||||
function loadOverlayImage(file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
createOverlayElement(img);
|
||||
};
|
||||
img.src = e.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
// 创建层叠图元素
|
||||
function createOverlayElement(img) {
|
||||
const container = imageContainer;
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
|
||||
// 计算初始尺寸(不超过容器1/3)
|
||||
let width = img.naturalWidth;
|
||||
let height = img.naturalHeight;
|
||||
const maxSize = Math.min(containerWidth, containerHeight) / 2;
|
||||
|
||||
if (width > maxSize || height > maxSize) {
|
||||
if (width > height) {
|
||||
height = (height / width) * maxSize;
|
||||
width = maxSize;
|
||||
} else {
|
||||
width = (width / height) * maxSize;
|
||||
height = maxSize;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建层叠图元素
|
||||
const overlayEl = document.createElement('div');
|
||||
overlayEl.className = 'overlay-image';
|
||||
overlayEl.dataset.scale = 1;
|
||||
|
||||
const imgEl = document.createElement('img');
|
||||
imgEl.src = img.src;
|
||||
imgEl.style.width = '100%';
|
||||
imgEl.style.height = '100%';
|
||||
imgEl.style.objectFit = 'contain';
|
||||
overlayEl.appendChild(imgEl);
|
||||
|
||||
// 删除按钮
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.className = 'delete-btn';
|
||||
deleteBtn.innerHTML = '×';
|
||||
deleteBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
overlayEl.remove();
|
||||
});
|
||||
overlayEl.appendChild(deleteBtn);
|
||||
|
||||
// 居中显示
|
||||
overlayEl.style.left = (containerWidth / 2 - width / 2) + 'px';
|
||||
overlayEl.style.top = (containerHeight / 2 - height / 2) + 'px';
|
||||
overlayEl.style.width = width + 'px';
|
||||
overlayEl.style.height = height + 'px';
|
||||
|
||||
// 初始化拖拽和缩放
|
||||
initOverlayImage(overlayEl);
|
||||
|
||||
overlayLayer.appendChild(overlayEl);
|
||||
|
||||
// 选中新添加的层叠图
|
||||
selectOverlay(overlayEl);
|
||||
}
|
||||
|
||||
// 初始化层叠图交互
|
||||
function initOverlayImage(overlayEl) {
|
||||
let isDragging = false;
|
||||
let isResizing = false;
|
||||
let startX, startY, startLeft, startTop, startWidth, startHeight;
|
||||
let currentScale = 1;
|
||||
const minSize = 30;
|
||||
|
||||
// 点击选中
|
||||
overlayEl.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
selectOverlay(overlayEl);
|
||||
});
|
||||
|
||||
// 拖拽移动
|
||||
overlayEl.addEventListener('mousedown', handleDragStart);
|
||||
overlayEl.addEventListener('touchstart', handleDragStart, { passive: false });
|
||||
|
||||
function handleDragStart(e) {
|
||||
if (e.target.classList.contains('delete-btn')) return;
|
||||
|
||||
const isTouch = e.type === 'touchstart';
|
||||
const clientX = isTouch ? e.touches[0].clientX : e.clientX;
|
||||
const clientY = isTouch ? e.touches[0].clientY : e.clientY;
|
||||
|
||||
isDragging = true;
|
||||
startX = clientX;
|
||||
startY = clientY;
|
||||
startLeft = overlayEl.offsetLeft;
|
||||
startTop = overlayEl.offsetTop;
|
||||
|
||||
overlayEl.classList.add('dragging');
|
||||
|
||||
document.addEventListener('mousemove', handleDragMove);
|
||||
document.addEventListener('mouseup', handleDragEnd);
|
||||
document.addEventListener('touchmove', handleDragMove, { passive: false });
|
||||
document.addEventListener('touchend', handleDragEnd);
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function handleDragMove(e) {
|
||||
if (!isDragging) return;
|
||||
|
||||
const isTouch = e.type === 'touchmove';
|
||||
const clientX = isTouch ? e.touches[0].clientX : e.clientX;
|
||||
const clientY = isTouch ? e.touches[0].clientY : e.clientY;
|
||||
|
||||
const dx = clientX - startX;
|
||||
const dy = clientY - startY;
|
||||
|
||||
let newLeft = startLeft + dx;
|
||||
let newTop = startTop + dy;
|
||||
|
||||
// 边界限制
|
||||
newLeft = Math.max(-overlayEl.offsetWidth + minSize, Math.min(newLeft, imageContainer.offsetWidth - minSize));
|
||||
newTop = Math.max(-overlayEl.offsetHeight + minSize, Math.min(newTop, imageContainer.offsetHeight - minSize));
|
||||
|
||||
overlayEl.style.left = newLeft + 'px';
|
||||
overlayEl.style.top = newTop + 'px';
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function handleDragEnd() {
|
||||
isDragging = false;
|
||||
overlayEl.classList.remove('dragging');
|
||||
|
||||
document.removeEventListener('mousemove', handleDragMove);
|
||||
document.removeEventListener('mouseup', handleDragEnd);
|
||||
document.removeEventListener('touchmove', handleDragMove);
|
||||
document.removeEventListener('touchend', handleDragEnd);
|
||||
}
|
||||
|
||||
// 缩放控制(四角)
|
||||
const corners = ['tl', 'tr', 'bl', 'br'];
|
||||
corners.forEach(corner => {
|
||||
const handle = document.createElement('div');
|
||||
handle.className = 'resize-handle resize-' + corner;
|
||||
handle.style.cssText = `
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: #4a90e2;
|
||||
border: 1px solid #fff;
|
||||
`;
|
||||
|
||||
switch(corner) {
|
||||
case 'tl': handle.style.top = '-6px'; handle.style.left = '-6px'; handle.style.cursor = 'nw-resize'; break;
|
||||
case 'tr': handle.style.top = '-6px'; handle.style.right = '-6px'; handle.style.cursor = 'ne-resize'; break;
|
||||
case 'bl': handle.style.bottom = '-6px'; handle.style.left = '-6px'; handle.style.cursor = 'sw-resize'; break;
|
||||
case 'br': handle.style.bottom = '-6px'; handle.style.right = '-6px'; handle.style.cursor = 'se-resize'; break;
|
||||
}
|
||||
|
||||
handle.addEventListener('mousedown', startResize);
|
||||
handle.addEventListener('touchstart', startResize, { passive: false });
|
||||
|
||||
overlayEl.appendChild(handle);
|
||||
|
||||
function startResize(e) {
|
||||
e.stopPropagation();
|
||||
isResizing = true;
|
||||
const isTouch = e.type === 'touchstart';
|
||||
const clientX = isTouch ? e.touches[0].clientX : e.clientX;
|
||||
const clientY = isTouch ? e.touches[0].clientY : e.clientY;
|
||||
|
||||
startX = clientX;
|
||||
startY = clientY;
|
||||
startLeft = overlayEl.offsetLeft;
|
||||
startTop = overlayEl.offsetTop;
|
||||
startWidth = overlayEl.offsetWidth;
|
||||
startHeight = overlayEl.offsetHeight;
|
||||
|
||||
document.addEventListener('mousemove', handleResize);
|
||||
document.addEventListener('mouseup', endResize);
|
||||
document.addEventListener('touchmove', handleResize, { passive: false });
|
||||
document.addEventListener('touchend', endResize);
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function handleResize(e) {
|
||||
if (!isResizing) return;
|
||||
|
||||
const isTouch = e.type === 'touchmove';
|
||||
const clientX = isTouch ? e.touches[0].clientX : e.clientX;
|
||||
const clientY = isTouch ? e.touches[0].clientY : e.clientY;
|
||||
|
||||
const dx = clientX - startX;
|
||||
const dy = clientY - startY;
|
||||
|
||||
let newWidth = startWidth;
|
||||
let newHeight = startHeight;
|
||||
let newLeft = startLeft;
|
||||
let newTop = startTop;
|
||||
|
||||
const aspectRatio = startWidth / startHeight;
|
||||
|
||||
// 根据角落调整尺寸
|
||||
if (corner === 'br') {
|
||||
newWidth = Math.max(minSize, startWidth + dx);
|
||||
newHeight = newWidth / aspectRatio;
|
||||
} else if (corner === 'bl') {
|
||||
newWidth = Math.max(minSize, startWidth - dx);
|
||||
newHeight = newWidth / aspectRatio;
|
||||
newLeft = startLeft - (newWidth - startWidth);
|
||||
} else if (corner === 'tr') {
|
||||
newWidth = Math.max(minSize, startWidth + dx);
|
||||
newHeight = newWidth / aspectRatio;
|
||||
newTop = startTop - (newHeight - startHeight);
|
||||
} else if (corner === 'tl') {
|
||||
newWidth = Math.max(minSize, startWidth - dx);
|
||||
newHeight = newWidth / aspectRatio;
|
||||
newLeft = startLeft - (newWidth - startWidth);
|
||||
newTop = startTop - (newHeight - startHeight);
|
||||
}
|
||||
|
||||
overlayEl.style.width = newWidth + 'px';
|
||||
overlayEl.style.height = newHeight + 'px';
|
||||
overlayEl.style.left = newLeft + 'px';
|
||||
overlayEl.style.top = newTop + 'px';
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function endResize() {
|
||||
isResizing = false;
|
||||
document.removeEventListener('mousemove', handleResize);
|
||||
document.removeEventListener('mouseup', endResize);
|
||||
document.removeEventListener('touchmove', handleResize);
|
||||
document.removeEventListener('touchend', endResize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 选中层叠图
|
||||
let selectedOverlay = null;
|
||||
function selectOverlay(overlayEl) {
|
||||
if (selectedOverlay) {
|
||||
selectedOverlay.classList.remove('selected');
|
||||
}
|
||||
selectedOverlay = overlayEl;
|
||||
overlayEl.classList.add('selected');
|
||||
}
|
||||
|
||||
// 点击背景取消选中层叠图
|
||||
imageContainer.addEventListener('click', (e) => {
|
||||
if (e.target === imageContainer ||
|
||||
e.target === bgLayer ||
|
||||
e.target === bgImage ||
|
||||
e.target === emptyTip ||
|
||||
e.target.closest('.preview-bg-layer')) {
|
||||
if (selectedOverlay) {
|
||||
selectedOverlay.classList.remove('selected');
|
||||
selectedOverlay = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 计算图片滑动边界
|
||||
function calculateImageBounds() {
|
||||
if (!currentImage) return;
|
||||
|
||||
Reference in New Issue
Block a user