feat(图层管理): 为二维码添加图层支持并实现相关功能
- 在状态管理中新增 qrCounter 用于生成唯一图层 ID - 为二维码元素添加 dataset.layerId 属性 - 实现二维码图层更新事件机制 - 在图层列表中显示二维码元素并支持选中操作 - 完善二维码的绘制逻辑,支持透明度设置
This commit is contained in:
23
js/qr.js
23
js/qr.js
@@ -40,7 +40,8 @@
|
|||||||
currentQR: null,
|
currentQR: null,
|
||||||
selectedQR: null,
|
selectedQR: null,
|
||||||
previewTimer: null,
|
previewTimer: null,
|
||||||
isDragging: false
|
isDragging: false,
|
||||||
|
qrCounter: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== 核心功能 ==========
|
// ========== 核心功能 ==========
|
||||||
@@ -119,10 +120,15 @@
|
|||||||
const container = document.getElementById('imageContainer');
|
const container = document.getElementById('imageContainer');
|
||||||
const rect = container.getBoundingClientRect();
|
const rect = container.getBoundingClientRect();
|
||||||
|
|
||||||
|
// 生成图层 ID
|
||||||
|
state.qrCounter++;
|
||||||
|
const layerId = `qr_${state.qrCounter}`;
|
||||||
|
|
||||||
// 创建二维码容器
|
// 创建二维码容器
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
wrapper.className = 'qr-item';
|
wrapper.className = 'qr-item';
|
||||||
wrapper.dataset.type = 'qrcode';
|
wrapper.dataset.type = 'qrcode';
|
||||||
|
wrapper.dataset.layerId = layerId;
|
||||||
wrapper.style.cssText = `
|
wrapper.style.cssText = `
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: ${CONFIG.DEFAULT_SIZE}px;
|
width: ${CONFIG.DEFAULT_SIZE}px;
|
||||||
@@ -168,6 +174,9 @@
|
|||||||
// 保存状态
|
// 保存状态
|
||||||
saveState();
|
saveState();
|
||||||
|
|
||||||
|
// 更新图层列表
|
||||||
|
updateLayersList();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -179,6 +188,9 @@
|
|||||||
closeModal();
|
closeModal();
|
||||||
|
|
||||||
saveState();
|
saveState();
|
||||||
|
|
||||||
|
// 更新图层列表
|
||||||
|
updateLayersList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -292,6 +304,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新图层列表
|
||||||
|
*/
|
||||||
|
function updateLayersList() {
|
||||||
|
// 触发图层更新事件
|
||||||
|
const event = new CustomEvent('updateLayers');
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 拖拽功能 ==========
|
// ========== 拖拽功能 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
75
js/script.js
75
js/script.js
@@ -2436,6 +2436,11 @@
|
|||||||
|
|
||||||
if (!layersBtn || !layersPanel) return;
|
if (!layersBtn || !layersPanel) return;
|
||||||
|
|
||||||
|
// 监听二维码图层更新事件
|
||||||
|
document.addEventListener('updateLayers', () => {
|
||||||
|
updateLayersList();
|
||||||
|
});
|
||||||
|
|
||||||
// 打开/关闭图层面板(同时支持桌面和移动端)
|
// 打开/关闭图层面板(同时支持桌面和移动端)
|
||||||
const toggleLayersPanel = () => {
|
const toggleLayersPanel = () => {
|
||||||
layersPanel.classList.toggle('active');
|
layersPanel.classList.toggle('active');
|
||||||
@@ -2590,7 +2595,20 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4. 层叠图(最上层)
|
// 4. 二维码
|
||||||
|
const qrItems = document.querySelectorAll('.qr-item');
|
||||||
|
qrItems.forEach((qr, index) => {
|
||||||
|
layers.push({
|
||||||
|
id: qr.dataset.layerId || `qr_${index}`,
|
||||||
|
element: qr,
|
||||||
|
name: `二维码 ${index + 1}`,
|
||||||
|
type: 'qrcode',
|
||||||
|
icon: '🔲',
|
||||||
|
visible: !qr.classList.contains('layer-hidden')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5. 层叠图(最上层)
|
||||||
const overlayImages = overlayLayer.querySelectorAll('.overlay-image');
|
const overlayImages = overlayLayer.querySelectorAll('.overlay-image');
|
||||||
overlayImages.forEach((overlay, index) => {
|
overlayImages.forEach((overlay, index) => {
|
||||||
layers.push({
|
layers.push({
|
||||||
@@ -2660,7 +2678,8 @@
|
|||||||
const names = {
|
const names = {
|
||||||
'text': '文本',
|
'text': '文本',
|
||||||
'overlay': '层叠图',
|
'overlay': '层叠图',
|
||||||
'background': '背景图'
|
'background': '背景图',
|
||||||
|
'qrcode': '二维码'
|
||||||
};
|
};
|
||||||
return names[type] || type;
|
return names[type] || type;
|
||||||
}
|
}
|
||||||
@@ -2687,6 +2706,40 @@
|
|||||||
selectInput(element);
|
selectInput(element);
|
||||||
} else if (element.classList.contains('overlay-image')) {
|
} else if (element.classList.contains('overlay-image')) {
|
||||||
selectOverlay(element);
|
selectOverlay(element);
|
||||||
|
} else if (element.classList.contains('qr-item')) {
|
||||||
|
// 选中二维码
|
||||||
|
if (window.QRCodeAPI) {
|
||||||
|
window.QRCodeAPI.hideTools();
|
||||||
|
}
|
||||||
|
// 取消其他选中
|
||||||
|
document.querySelectorAll('.qr-item').forEach(el => {
|
||||||
|
el.classList.remove('qr-selected');
|
||||||
|
});
|
||||||
|
// 选中当前二维码
|
||||||
|
element.classList.add('qr-selected');
|
||||||
|
// 显示二维码工具栏
|
||||||
|
editorToolbar.classList.add('active');
|
||||||
|
textTools.classList.remove('active');
|
||||||
|
imageTools.classList.remove('active');
|
||||||
|
bgTools.classList.remove('active');
|
||||||
|
const qrTools = document.getElementById('qrTools');
|
||||||
|
if (qrTools) qrTools.classList.add('active');
|
||||||
|
// 同步工具栏值
|
||||||
|
const qrImg = element.querySelector('img');
|
||||||
|
const qrOpacitySlider = document.getElementById('qrOpacitySlider');
|
||||||
|
const qrOpacityValue = document.getElementById('qrOpacityValue');
|
||||||
|
const qrScaleSlider = document.getElementById('qrScaleSlider');
|
||||||
|
const qrScaleValue = document.getElementById('qrScaleValue');
|
||||||
|
if (qrImg && qrOpacitySlider) {
|
||||||
|
const opacity = parseFloat(qrImg.style.opacity) || 1;
|
||||||
|
qrOpacitySlider.value = Math.round(opacity * 100);
|
||||||
|
qrOpacityValue.textContent = Math.round(opacity * 100) + '%';
|
||||||
|
}
|
||||||
|
if (qrScaleSlider) {
|
||||||
|
const size = parseInt(element.style.width) || 100;
|
||||||
|
qrScaleSlider.value = size;
|
||||||
|
qrScaleValue.textContent = size + '%';
|
||||||
|
}
|
||||||
} else if (element.id === 'bgImage') {
|
} else if (element.id === 'bgImage') {
|
||||||
// 选中背景图
|
// 选中背景图
|
||||||
editorToolbar.classList.add('active');
|
editorToolbar.classList.add('active');
|
||||||
@@ -3206,16 +3259,24 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 绘制二维码
|
// 绘制二维码
|
||||||
const qrImages = container.querySelectorAll('img[data-type="qrcode"]');
|
const qrWrappers = container.querySelectorAll('.qr-item');
|
||||||
for (const qrImg of qrImages) {
|
for (const qrWrapper of qrWrappers) {
|
||||||
if (!qrImg.classList.contains('layer-hidden')) {
|
if (!qrWrapper.classList.contains('layer-hidden')) {
|
||||||
const rect = qrImg.getBoundingClientRect();
|
const qrImg = qrWrapper.querySelector('img');
|
||||||
|
if (!qrImg) continue;
|
||||||
|
|
||||||
|
const rect = qrWrapper.getBoundingClientRect();
|
||||||
const containerRect = container.getBoundingClientRect();
|
const containerRect = container.getBoundingClientRect();
|
||||||
const x = (rect.left - containerRect.left) * scale;
|
const x = (rect.left - containerRect.left) * scale;
|
||||||
const y = (rect.top - containerRect.top) * scale;
|
const y = (rect.top - containerRect.top) * scale;
|
||||||
const w = rect.width * scale;
|
const w = rect.width * scale;
|
||||||
const h = rect.height * scale;
|
const h = rect.height * scale;
|
||||||
|
|
||||||
|
// 应用透明度
|
||||||
|
const opacity = parseFloat(qrImg.style.opacity) || 1;
|
||||||
|
ctx.save();
|
||||||
|
ctx.globalAlpha = opacity;
|
||||||
|
|
||||||
// 确保图片已加载
|
// 确保图片已加载
|
||||||
if (qrImg.complete && qrImg.naturalWidth > 0) {
|
if (qrImg.complete && qrImg.naturalWidth > 0) {
|
||||||
ctx.drawImage(qrImg, x, y, w, h);
|
ctx.drawImage(qrImg, x, y, w, h);
|
||||||
@@ -3231,6 +3292,8 @@
|
|||||||
setTimeout(resolve, 100);
|
setTimeout(resolve, 100);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user