431 lines
12 KiB
JavaScript
431 lines
12 KiB
JavaScript
// 二维码功能 - 优化版编辑器工具栏
|
||
(function() {
|
||
'use strict';
|
||
|
||
// ========== 配置 ==========
|
||
const CONFIG = {
|
||
MIN_SIZE: 0,
|
||
MAX_SIZE: 200,
|
||
DEFAULT_SIZE: 100,
|
||
PREVIEW_SIZE: 128,
|
||
DEBOUNCE_DELAY: 300
|
||
};
|
||
|
||
// ========== DOM 元素缓存 ==========
|
||
const elements = {
|
||
btn: document.getElementById('qr-btn'),
|
||
modal: document.getElementById('qr-modal'),
|
||
input: document.getElementById('qr-input'),
|
||
preview: document.getElementById('qr-preview'),
|
||
closeBtn: document.querySelector('.qr-close'),
|
||
cancelBtn: document.querySelector('.qr-cancel'),
|
||
okBtn: document.querySelector('.qr-ok'),
|
||
// 工具栏
|
||
editorToolbar: document.getElementById('editorToolbar'),
|
||
textTools: document.getElementById('textTools'),
|
||
imageTools: document.getElementById('imageTools'),
|
||
bgTools: document.getElementById('bgTools'),
|
||
qrTools: document.getElementById('qrTools'),
|
||
qrOpacitySlider: document.getElementById('qrOpacitySlider'),
|
||
qrOpacityValue: document.getElementById('qrOpacityValue'),
|
||
qrScaleSlider: document.getElementById('qrScaleSlider'),
|
||
qrScaleValue: document.getElementById('qrScaleValue')
|
||
};
|
||
|
||
// 验证必需元素
|
||
if (!elements.btn || !elements.modal) return;
|
||
|
||
// ========== 状态管理 ==========
|
||
const state = {
|
||
currentQR: null,
|
||
selectedQR: null,
|
||
previewTimer: null,
|
||
isDragging: false
|
||
};
|
||
|
||
// ========== 核心功能 ==========
|
||
|
||
/**
|
||
* 检查背景图并显示/隐藏二维码按钮
|
||
*/
|
||
function checkBackground() {
|
||
const container = document.getElementById('imageContainer');
|
||
if (!container) return;
|
||
|
||
const bgImg = container.querySelector('img:not([data-type="qrcode"])');
|
||
const hasBackground = bgImg && bgImg.src && !bgImg.src.endsWith(window.location.href);
|
||
|
||
elements.btn.classList.toggle('show', hasBackground);
|
||
}
|
||
|
||
/**
|
||
* 打开二维码生成弹窗
|
||
*/
|
||
function openModal() {
|
||
elements.modal.classList.add('active');
|
||
elements.input.value = '';
|
||
elements.preview.innerHTML = '';
|
||
state.currentQR = null;
|
||
elements.input.focus();
|
||
}
|
||
|
||
/**
|
||
* 关闭二维码生成弹窗
|
||
*/
|
||
function closeModal() {
|
||
elements.modal.classList.remove('active');
|
||
}
|
||
|
||
/**
|
||
* 生成二维码预览
|
||
*/
|
||
function generatePreview() {
|
||
const text = elements.input.value.trim();
|
||
|
||
if (!text) {
|
||
elements.preview.innerHTML = '';
|
||
state.currentQR = null;
|
||
return;
|
||
}
|
||
|
||
elements.preview.innerHTML = '';
|
||
|
||
try {
|
||
state.currentQR = new QRCode(elements.preview, {
|
||
text: text,
|
||
width: CONFIG.PREVIEW_SIZE,
|
||
height: CONFIG.PREVIEW_SIZE,
|
||
colorDark: '#000000',
|
||
colorLight: '#ffffff',
|
||
correctLevel: QRCode.CorrectLevel.M
|
||
});
|
||
} catch (error) {
|
||
console.error('二维码生成失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将二维码添加到画布
|
||
*/
|
||
function addToCanvas() {
|
||
if (!state.currentQR) {
|
||
alert('请输入内容');
|
||
return;
|
||
}
|
||
|
||
const canvas = elements.preview.querySelector('canvas');
|
||
if (!canvas) return;
|
||
|
||
const container = document.getElementById('imageContainer');
|
||
const rect = container.getBoundingClientRect();
|
||
|
||
// 创建二维码容器
|
||
const wrapper = document.createElement('div');
|
||
wrapper.className = 'qr-item';
|
||
wrapper.dataset.type = 'qrcode';
|
||
wrapper.style.cssText = `
|
||
position: absolute;
|
||
width: ${CONFIG.DEFAULT_SIZE}px;
|
||
height: ${CONFIG.DEFAULT_SIZE}px;
|
||
left: ${(rect.width / 2 - CONFIG.DEFAULT_SIZE / 2)}px;
|
||
top: ${(rect.height / 2 - CONFIG.DEFAULT_SIZE / 2)}px;
|
||
`;
|
||
|
||
// 创建二维码图片
|
||
const img = document.createElement('img');
|
||
img.src = canvas.toDataURL('image/png');
|
||
img.style.cssText = `
|
||
width: 100%;
|
||
height: 100%;
|
||
opacity: 1;
|
||
display: block;
|
||
`;
|
||
|
||
// 创建删除按钮
|
||
const deleteBtn = document.createElement('button');
|
||
deleteBtn.className = 'qr-delete-btn';
|
||
deleteBtn.innerHTML = '×';
|
||
deleteBtn.title = '删除二维码';
|
||
deleteBtn.type = 'button';
|
||
|
||
// 删除按钮点击事件
|
||
deleteBtn.onclick = function(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
|
||
// 删除二维码元素
|
||
wrapper.remove();
|
||
|
||
// 清除选中状态
|
||
if (state.selectedQR === wrapper) {
|
||
state.selectedQR = null;
|
||
hideQRTools();
|
||
if (elements.editorToolbar) {
|
||
elements.editorToolbar.classList.remove('active');
|
||
}
|
||
}
|
||
|
||
// 保存状态
|
||
saveState();
|
||
|
||
return false;
|
||
};
|
||
|
||
wrapper.appendChild(img);
|
||
wrapper.appendChild(deleteBtn);
|
||
container.appendChild(wrapper);
|
||
makeDraggable(wrapper);
|
||
selectQR(wrapper);
|
||
closeModal();
|
||
|
||
saveState();
|
||
}
|
||
|
||
/**
|
||
* 选中二维码
|
||
*/
|
||
function selectQR(img) {
|
||
// 取消其他选中状态
|
||
document.querySelectorAll('.qr-item').forEach(el => {
|
||
el.classList.remove('qr-selected');
|
||
});
|
||
|
||
// 选中当前二维码
|
||
img.classList.add('qr-selected');
|
||
state.selectedQR = img;
|
||
|
||
// 显示二维码工具栏
|
||
showQRTools();
|
||
}
|
||
|
||
/**
|
||
* 显示二维码工具栏
|
||
*/
|
||
function showQRTools() {
|
||
if (!state.selectedQR) return;
|
||
|
||
// 激活编辑器工具栏
|
||
if (elements.editorToolbar) {
|
||
elements.editorToolbar.classList.add('active');
|
||
}
|
||
|
||
// 隐藏其他工具栏
|
||
['textTools', 'imageTools', 'bgTools'].forEach(key => {
|
||
if (elements[key]) elements[key].classList.remove('active');
|
||
});
|
||
|
||
// 显示二维码工具栏
|
||
if (elements.qrTools) {
|
||
elements.qrTools.classList.add('active');
|
||
}
|
||
|
||
// 同步滑块值
|
||
syncToolbarValues();
|
||
}
|
||
|
||
/**
|
||
* 同步工具栏滑块值
|
||
*/
|
||
function syncToolbarValues() {
|
||
if (!state.selectedQR) return;
|
||
|
||
const img = state.selectedQR.querySelector('img');
|
||
const opacity = img ? (parseFloat(img.style.opacity) || 1) : 1;
|
||
const size = parseInt(state.selectedQR.style.width) || CONFIG.DEFAULT_SIZE;
|
||
|
||
if (elements.qrOpacitySlider) {
|
||
elements.qrOpacitySlider.value = Math.round(opacity * 100);
|
||
elements.qrOpacityValue.textContent = Math.round(opacity * 100) + '%';
|
||
}
|
||
|
||
if (elements.qrScaleSlider) {
|
||
elements.qrScaleSlider.value = size;
|
||
elements.qrScaleValue.textContent = size + '%';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 隐藏二维码工具栏
|
||
*/
|
||
function hideQRTools() {
|
||
if (elements.qrTools) {
|
||
elements.qrTools.classList.remove('active');
|
||
}
|
||
|
||
document.querySelectorAll('.qr-item').forEach(el => {
|
||
el.classList.remove('qr-selected');
|
||
});
|
||
|
||
state.selectedQR = null;
|
||
}
|
||
|
||
/**
|
||
* 更新二维码透明度
|
||
*/
|
||
function updateOpacity(value) {
|
||
if (!state.selectedQR) return;
|
||
|
||
const img = state.selectedQR.querySelector('img');
|
||
if (img) {
|
||
img.style.opacity = value / 100;
|
||
}
|
||
elements.qrOpacityValue.textContent = value + '%';
|
||
}
|
||
|
||
/**
|
||
* 更新二维码大小
|
||
*/
|
||
function updateScale(value) {
|
||
if (!state.selectedQR) return;
|
||
|
||
state.selectedQR.style.width = value + 'px';
|
||
state.selectedQR.style.height = value + 'px';
|
||
elements.qrScaleValue.textContent = value + '%';
|
||
}
|
||
|
||
/**
|
||
* 保存状态
|
||
*/
|
||
function saveState() {
|
||
if (typeof window.saveState === 'function') {
|
||
window.saveState();
|
||
}
|
||
}
|
||
|
||
// ========== 拖拽功能 ==========
|
||
|
||
/**
|
||
* 使元素可拖拽
|
||
*/
|
||
function makeDraggable(el) {
|
||
let isDragging = false;
|
||
let startX, startY, startLeft, startTop;
|
||
|
||
function startDrag(clientX, clientY) {
|
||
isDragging = true;
|
||
startX = clientX;
|
||
startY = clientY;
|
||
startLeft = parseInt(el.style.left) || 0;
|
||
startTop = parseInt(el.style.top) || 0;
|
||
el.style.cursor = 'grabbing';
|
||
}
|
||
|
||
function moveDrag(clientX, clientY) {
|
||
if (!isDragging) return;
|
||
el.style.left = (startLeft + clientX - startX) + 'px';
|
||
el.style.top = (startTop + clientY - startY) + 'px';
|
||
}
|
||
|
||
function endDrag() {
|
||
if (isDragging) {
|
||
isDragging = false;
|
||
el.style.cursor = 'move';
|
||
saveState();
|
||
}
|
||
}
|
||
|
||
// 鼠标事件 - 排除删除按钮
|
||
el.addEventListener('mousedown', (e) => {
|
||
// 如果点击的是删除按钮,不处理拖拽
|
||
if (e.target.closest('.qr-delete-btn')) {
|
||
return;
|
||
}
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
selectQR(el);
|
||
startDrag(e.clientX, e.clientY);
|
||
});
|
||
|
||
// 触摸事件 - 排除删除按钮
|
||
el.addEventListener('touchstart', (e) => {
|
||
// 如果触摸的是删除按钮,不处理拖拽
|
||
if (e.target.closest('.qr-delete-btn')) {
|
||
return;
|
||
}
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
selectQR(el);
|
||
if (e.touches.length === 1) {
|
||
startDrag(e.touches[0].clientX, e.touches[0].clientY);
|
||
}
|
||
}, { passive: false });
|
||
|
||
// 全局移动事件
|
||
document.addEventListener('mousemove', (e) => moveDrag(e.clientX, e.clientY));
|
||
document.addEventListener('touchmove', (e) => {
|
||
if (isDragging && e.touches.length === 1) {
|
||
e.preventDefault();
|
||
moveDrag(e.touches[0].clientX, e.touches[0].clientY);
|
||
}
|
||
}, { passive: false });
|
||
|
||
// 全局结束事件
|
||
document.addEventListener('mouseup', endDrag);
|
||
document.addEventListener('touchend', endDrag);
|
||
}
|
||
|
||
// ========== 事件绑定 ==========
|
||
|
||
function bindEvents() {
|
||
// 弹窗事件
|
||
elements.btn.addEventListener('click', openModal);
|
||
elements.closeBtn.addEventListener('click', closeModal);
|
||
elements.cancelBtn.addEventListener('click', closeModal);
|
||
elements.okBtn.addEventListener('click', addToCanvas);
|
||
elements.modal.addEventListener('click', (e) => {
|
||
if (e.target === elements.modal) closeModal();
|
||
});
|
||
|
||
// 输入防抖
|
||
elements.input.addEventListener('input', () => {
|
||
clearTimeout(state.previewTimer);
|
||
state.previewTimer = setTimeout(generatePreview, CONFIG.DEBOUNCE_DELAY);
|
||
});
|
||
|
||
// 透明度滑块
|
||
if (elements.qrOpacitySlider) {
|
||
elements.qrOpacitySlider.addEventListener('input', (e) => {
|
||
updateOpacity(e.target.value);
|
||
});
|
||
elements.qrOpacitySlider.addEventListener('change', saveState);
|
||
}
|
||
|
||
// 缩放滑块
|
||
if (elements.qrScaleSlider) {
|
||
elements.qrScaleSlider.addEventListener('input', (e) => {
|
||
updateScale(e.target.value);
|
||
});
|
||
elements.qrScaleSlider.addEventListener('change', saveState);
|
||
}
|
||
}
|
||
|
||
// ========== 初始化 ==========
|
||
|
||
function init() {
|
||
// 监听背景图变化
|
||
const container = document.getElementById('imageContainer');
|
||
if (container) {
|
||
const observer = new MutationObserver(checkBackground);
|
||
observer.observe(container, { childList: true, subtree: true });
|
||
}
|
||
|
||
// 定期检查背景图状态
|
||
setInterval(checkBackground, 500);
|
||
|
||
// 绑定事件
|
||
bindEvents();
|
||
|
||
// 初始检查
|
||
checkBackground();
|
||
|
||
// 暴露全局 API
|
||
window.QRCodeAPI = {
|
||
hideTools: hideQRTools,
|
||
getSelected: () => state.selectedQR
|
||
};
|
||
}
|
||
|
||
// 启动
|
||
init();
|
||
})();
|