From 6a0294f0c091142c1452d653a9852cea1285c448 Mon Sep 17 00:00:00 2001 From: Zhukj <3262118957> Date: Mon, 27 Apr 2026 08:46:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=9B=BE=E5=B1=82=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E4=B8=BA=E4=BA=8C=E7=BB=B4=E7=A0=81=E6=B7=BB=E5=8A=A0=E5=9B=BE?= =?UTF-8?q?=E5=B1=82=E6=94=AF=E6=8C=81=E5=B9=B6=E5=AE=9E=E7=8E=B0=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在状态管理中新增 qrCounter 用于生成唯一图层 ID - 为二维码元素添加 dataset.layerId 属性 - 实现二维码图层更新事件机制 - 在图层列表中显示二维码元素并支持选中操作 - 完善二维码的绘制逻辑,支持透明度设置 --- js/qr.js | 23 +++++++++++++++- js/script.js | 75 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 91 insertions(+), 7 deletions(-) diff --git a/js/qr.js b/js/qr.js index 0846e72..e29de7b 100644 --- a/js/qr.js +++ b/js/qr.js @@ -40,7 +40,8 @@ currentQR: null, selectedQR: null, previewTimer: null, - isDragging: false + isDragging: false, + qrCounter: 0 }; // ========== 核心功能 ========== @@ -119,10 +120,15 @@ const container = document.getElementById('imageContainer'); const rect = container.getBoundingClientRect(); + // 生成图层 ID + state.qrCounter++; + const layerId = `qr_${state.qrCounter}`; + // 创建二维码容器 const wrapper = document.createElement('div'); wrapper.className = 'qr-item'; wrapper.dataset.type = 'qrcode'; + wrapper.dataset.layerId = layerId; wrapper.style.cssText = ` position: absolute; width: ${CONFIG.DEFAULT_SIZE}px; @@ -168,6 +174,9 @@ // 保存状态 saveState(); + // 更新图层列表 + updateLayersList(); + return false; }; @@ -179,6 +188,9 @@ closeModal(); saveState(); + + // 更新图层列表 + updateLayersList(); } /** @@ -292,6 +304,15 @@ } } + /** + * 更新图层列表 + */ + function updateLayersList() { + // 触发图层更新事件 + const event = new CustomEvent('updateLayers'); + document.dispatchEvent(event); + } + // ========== 拖拽功能 ========== /** diff --git a/js/script.js b/js/script.js index 098cef9..b81d03e 100644 --- a/js/script.js +++ b/js/script.js @@ -2436,6 +2436,11 @@ if (!layersBtn || !layersPanel) return; + // 监听二维码图层更新事件 + document.addEventListener('updateLayers', () => { + updateLayersList(); + }); + // 打开/关闭图层面板(同时支持桌面和移动端) const toggleLayersPanel = () => { 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'); overlayImages.forEach((overlay, index) => { layers.push({ @@ -2660,7 +2678,8 @@ const names = { 'text': '文本', 'overlay': '层叠图', - 'background': '背景图' + 'background': '背景图', + 'qrcode': '二维码' }; return names[type] || type; } @@ -2687,6 +2706,40 @@ selectInput(element); } else if (element.classList.contains('overlay-image')) { 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') { // 选中背景图 editorToolbar.classList.add('active'); @@ -3206,16 +3259,24 @@ }); // 绘制二维码 - const qrImages = container.querySelectorAll('img[data-type="qrcode"]'); - for (const qrImg of qrImages) { - if (!qrImg.classList.contains('layer-hidden')) { - const rect = qrImg.getBoundingClientRect(); + const qrWrappers = container.querySelectorAll('.qr-item'); + for (const qrWrapper of qrWrappers) { + if (!qrWrapper.classList.contains('layer-hidden')) { + const qrImg = qrWrapper.querySelector('img'); + if (!qrImg) continue; + + const rect = qrWrapper.getBoundingClientRect(); const containerRect = container.getBoundingClientRect(); const x = (rect.left - containerRect.left) * scale; const y = (rect.top - containerRect.top) * scale; const w = rect.width * scale; const h = rect.height * scale; + // 应用透明度 + const opacity = parseFloat(qrImg.style.opacity) || 1; + ctx.save(); + ctx.globalAlpha = opacity; + // 确保图片已加载 if (qrImg.complete && qrImg.naturalWidth > 0) { ctx.drawImage(qrImg, x, y, w, h); @@ -3231,6 +3292,8 @@ setTimeout(resolve, 100); }); } + + ctx.restore(); } }