feat(邀请函): 添加二维码生成功能
- 新增二维码按钮及弹窗界面 - 实现二维码生成、预览和添加到画布功能 - 支持二维码在画布上的拖拽操作 - 添加相关样式和事件处理
This commit is contained in:
136
css/qr.css
Normal file
136
css/qr.css
Normal file
@@ -0,0 +1,136 @@
|
||||
/* 二维码功能样式 */
|
||||
|
||||
/* 悬浮按钮 */
|
||||
#qr-btn {
|
||||
position: fixed;
|
||||
bottom: 100px;
|
||||
left: 20px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
background: #4a90e2;
|
||||
color: #fff;
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#qr-btn.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#qr-btn:hover {
|
||||
background: #3a7bc8;
|
||||
}
|
||||
|
||||
/* 弹窗 */
|
||||
#qr-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
z-index: 10000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#qr-modal.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.qr-box {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
width: 320px;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.qr-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.qr-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.qr-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.qr-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#qr-input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#qr-preview {
|
||||
margin-top: 15px;
|
||||
min-height: 128px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#qr-preview canvas {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.qr-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.qr-btn {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.qr-cancel {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.qr-ok {
|
||||
background: #4a90e2;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 画布上的二维码 */
|
||||
.qr-item {
|
||||
position: absolute;
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
}
|
||||
22
index.html
22
index.html
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>极简邀请函制作</title>
|
||||
<link rel="stylesheet" href="./css/style.css">
|
||||
<link rel="stylesheet" href="./css/qr.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
@@ -136,6 +137,27 @@
|
||||
<input type="file" id="fileInput" accept="image/*" hidden>
|
||||
<canvas id="exportCanvas" hidden></canvas>
|
||||
|
||||
<!-- 二维码功能 -->
|
||||
<button id="qr-btn" title="添加二维码">二维码</button>
|
||||
<div id="qr-modal">
|
||||
<div class="qr-box">
|
||||
<div class="qr-header">
|
||||
<h3>生成二维码</h3>
|
||||
<button class="qr-close">×</button>
|
||||
</div>
|
||||
<div class="qr-body">
|
||||
<input type="text" id="qr-input" placeholder="输入网址或文字" maxlength="200">
|
||||
<div id="qr-preview"></div>
|
||||
</div>
|
||||
<div class="qr-footer">
|
||||
<button class="qr-btn qr-cancel">取消</button>
|
||||
<button class="qr-btn qr-ok">添加到画布</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
|
||||
<script src="./js/script.js"></script>
|
||||
<script src="./js/qr.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
179
js/qr.js
Normal file
179
js/qr.js
Normal file
@@ -0,0 +1,179 @@
|
||||
// 二维码功能 - 简洁可靠
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// 获取元素
|
||||
const btn = document.getElementById('qr-btn');
|
||||
const modal = document.getElementById('qr-modal');
|
||||
const input = document.getElementById('qr-input');
|
||||
const preview = document.getElementById('qr-preview');
|
||||
const closeBtn = document.querySelector('.qr-close');
|
||||
const cancelBtn = document.querySelector('.qr-cancel');
|
||||
const okBtn = document.querySelector('.qr-ok');
|
||||
|
||||
if (!btn || !modal) return;
|
||||
|
||||
let currentQR = null;
|
||||
let qrCount = 0;
|
||||
|
||||
// 检查背景图并显示按钮
|
||||
function checkBg() {
|
||||
const container = document.getElementById('imageContainer');
|
||||
if (!container) return;
|
||||
// 排除二维码图片,只检查背景图
|
||||
const img = container.querySelector('img:not([data-type="qrcode"])');
|
||||
if (img && img.src && !img.src.endsWith(window.location.href)) {
|
||||
btn.classList.add('show');
|
||||
} else {
|
||||
btn.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// 监听背景图变化
|
||||
const observer = new MutationObserver(checkBg);
|
||||
const container = document.getElementById('imageContainer');
|
||||
if (container) {
|
||||
observer.observe(container, { childList: true, subtree: true });
|
||||
}
|
||||
setInterval(checkBg, 500);
|
||||
|
||||
// 打开弹窗
|
||||
function openModal() {
|
||||
modal.classList.add('active');
|
||||
input.value = '';
|
||||
preview.innerHTML = '';
|
||||
currentQR = null;
|
||||
input.focus();
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
function closeModal() {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
|
||||
// 生成预览
|
||||
function genPreview() {
|
||||
const text = input.value.trim();
|
||||
if (!text) {
|
||||
preview.innerHTML = '';
|
||||
currentQR = null;
|
||||
return;
|
||||
}
|
||||
preview.innerHTML = '';
|
||||
try {
|
||||
currentQR = new QRCode(preview, {
|
||||
text: text,
|
||||
width: 128,
|
||||
height: 128
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('生成失败', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加到画布
|
||||
function addToCanvas() {
|
||||
if (!currentQR) {
|
||||
alert('请输入内容');
|
||||
return;
|
||||
}
|
||||
const canvas = preview.querySelector('canvas');
|
||||
if (!canvas) return;
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = canvas.toDataURL();
|
||||
img.className = 'qr-item';
|
||||
img.dataset.type = 'qrcode';
|
||||
img.style.width = '100px';
|
||||
img.style.height = '100px';
|
||||
|
||||
const container = document.getElementById('imageContainer');
|
||||
const rect = container.getBoundingClientRect();
|
||||
img.style.left = (rect.width / 2 - 50) + 'px';
|
||||
img.style.top = (rect.height / 2 - 50) + 'px';
|
||||
|
||||
container.appendChild(img);
|
||||
makeDraggable(img);
|
||||
closeModal();
|
||||
|
||||
if (typeof saveState === 'function') saveState();
|
||||
}
|
||||
|
||||
// 拖拽功能 - 支持鼠标和触摸
|
||||
function makeDraggable(el) {
|
||||
let dragging = false;
|
||||
let startX, startY, startLeft, startTop;
|
||||
|
||||
function startDrag(clientX, clientY) {
|
||||
dragging = true;
|
||||
startX = clientX;
|
||||
startY = clientY;
|
||||
startLeft = parseInt(el.style.left) || 0;
|
||||
startTop = parseInt(el.style.top) || 0;
|
||||
}
|
||||
|
||||
function moveDrag(clientX, clientY) {
|
||||
if (!dragging) return;
|
||||
el.style.left = (startLeft + clientX - startX) + 'px';
|
||||
el.style.top = (startTop + clientY - startY) + 'px';
|
||||
}
|
||||
|
||||
function endDrag() {
|
||||
if (dragging) {
|
||||
dragging = false;
|
||||
if (typeof saveState === 'function') saveState();
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标事件
|
||||
el.addEventListener('mousedown', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
startDrag(e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
// 触摸事件
|
||||
el.addEventListener('touchstart', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (e.touches.length === 1) {
|
||||
startDrag(e.touches[0].clientX, e.touches[0].clientY);
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
// 全局移动事件
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
moveDrag(e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
document.addEventListener('touchmove', function(e) {
|
||||
if (dragging && 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);
|
||||
}
|
||||
|
||||
// 事件绑定
|
||||
btn.addEventListener('click', openModal);
|
||||
closeBtn.addEventListener('click', closeModal);
|
||||
cancelBtn.addEventListener('click', closeModal);
|
||||
okBtn.addEventListener('click', addToCanvas);
|
||||
modal.addEventListener('click', function(e) {
|
||||
if (e.target === modal) closeModal();
|
||||
});
|
||||
|
||||
// 输入防抖
|
||||
let timer;
|
||||
input.addEventListener('input', function() {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(genPreview, 300);
|
||||
});
|
||||
|
||||
// 初始检查
|
||||
checkBg();
|
||||
})();
|
||||
29
js/script.js
29
js/script.js
@@ -3173,6 +3173,35 @@
|
||||
}
|
||||
});
|
||||
|
||||
// 绘制二维码
|
||||
const qrImages = container.querySelectorAll('img[data-type="qrcode"]');
|
||||
for (const qrImg of qrImages) {
|
||||
if (!qrImg.classList.contains('layer-hidden')) {
|
||||
const rect = qrImg.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;
|
||||
|
||||
// 确保图片已加载
|
||||
if (qrImg.complete && qrImg.naturalWidth > 0) {
|
||||
ctx.drawImage(qrImg, x, y, w, h);
|
||||
} else {
|
||||
// 等待图片加载完成
|
||||
await new Promise(resolve => {
|
||||
qrImg.onload = () => {
|
||||
ctx.drawImage(qrImg, x, y, w, h);
|
||||
resolve();
|
||||
};
|
||||
qrImg.onerror = () => resolve();
|
||||
// 超时处理
|
||||
setTimeout(resolve, 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 记录总耗时
|
||||
const endTime = performance.now();
|
||||
console.log(`画布生成总耗时: ${(endTime - startTime).toFixed(2)}ms`);
|
||||
|
||||
Reference in New Issue
Block a user