qr-wizardry-portal / templates.html
Haroon999's picture
create this as a canvas fully functional drag and drop resize moving modern advance featur rich remove form instead of form make canvas fully flexible custoizbla using real canvas resources
5ded031 verified
Raw
History Blame Contribute Delete
23.7 kB
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Template Designer | QR Wizardry</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/react-dropzone@14.2.3/dist/react-dropzone.js"></script>
<script src="https://unpkg.com/react-resizable@3.0.5/dist/react-resizable.js"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
}
}
}
}
}
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
background-color: #0f172a;
color: #e2e8f0;
}
.resize-handle {
position: absolute;
width: 10px;
height: 10px;
background: #3b82f6;
right: -5px;
bottom: -5px;
cursor: se-resize;
border-radius: 2px;
}
.dropzone {
border: 2px dashed #334155;
transition: all 0.3s ease;
}
.dropzone.active {
border-color: #3b82f6;
background-color: #1e3a8a20;
}
</style>
</head>
<body class="min-h-screen">
<div class="relative">
<!-- Navigation -->
<nav class="bg-gray-900/80 backdrop-blur-sm border-b border-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<div class="flex items-center">
<div class="flex-shrink-0 flex items-center">
<i data-feather="maximize-2" class="text-primary-500 w-6 h-6"></i>
<span class="ml-2 font-bold">QR Wizardry</span>
</div>
</div>
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<a href="/" class="text-gray-400 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
Home
</a>
<a href="#" class="bg-gray-800 text-white px-3 py-2 rounded-md text-sm font-medium">
Templates
</a>
<a href="#" class="text-gray-400 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
Documentation
</a>
</div>
</div>
</div>
</div>
</nav>
<!-- Main content -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-8">
<h1 class="text-3xl font-bold mb-2">Template Designer</h1>
<p class="text-gray-400">Create your perfect QR code template with our intuitive designer</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Export Section -->
<div class="bg-gray-900/50 border border-gray-800 rounded-xl p-6">
<h2 class="text-xl font-semibold mb-4 flex items-center">
<i data-feather="download" class="w-5 h-5 mr-2 text-primary-500"></i>
Export Options
</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Export Format</label>
<select class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
<option>PNG</option>
<option>JPG</option>
<option>PDF</option>
<option>SVG</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">DPI</label>
<select class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
<option>72 (Web)</option>
<option selected>300 (Print)</option>
<option>600 (High Quality Print)</option>
</select>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Width (mm)</label>
<input type="number" value="65" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Height (mm)</label>
<input type="number" value="140" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
</div>
</div>
<button id="exportCanvas" class="w-full bg-primary-600 hover:bg-primary-700 text-white font-medium py-2 px-4 rounded-lg transition-colors flex items-center justify-center">
<i data-feather="download" class="w-4 h-4 mr-2"></i>
Export Design
</button>
</div>
</div>
<!-- Canvas Designer -->
<div class="space-y-6">
<div class="bg-gray-900/50 border border-gray-800 rounded-xl p-6">
<h2 class="text-xl font-semibold mb-4 flex items-center">
<i data-feather="edit-2" class="w-5 h-5 mr-2 text-primary-500"></i>
Canvas Designer
</h2>
<div class="flex flex-col space-y-4">
<div class="flex space-x-4">
<button id="addQR" class="px-4 py-2 bg-primary-600 hover:bg-primary-700 rounded-lg flex items-center">
<i data-feather="plus" class="w-4 h-4 mr-2"></i> Add QR
</button>
<button id="addText" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg flex items-center">
<i data-feather="type" class="w-4 h-4 mr-2"></i> Add Text
</button>
<button id="addImage" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg flex items-center">
<i data-feather="image" class="w-4 h-4 mr-2"></i> Add Image
</button>
</div>
<div class="flex space-x-4">
<button id="deleteSelected" class="px-4 py-2 bg-red-600 hover:bg-red-700 rounded-lg flex items-center">
<i data-feather="trash-2" class="w-4 h-4 mr-2"></i> Delete
</button>
<button id="clearCanvas" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg flex items-center">
<i data-feather="x" class="w-4 h-4 mr-2"></i> Clear All
</button>
</div>
</div>
<div class="mt-6 relative">
<div class="border-2 border-dashed border-gray-700 rounded-lg overflow-hidden">
<canvas id="designCanvas" class="bg-white w-full" style="height: 600px;"></canvas>
</div>
</div>
</div>
<!-- Properties Panel -->
<div class="bg-gray-900/50 border border-gray-800 rounded-xl p-6" id="propertiesPanel">
<h2 class="text-xl font-semibold mb-4 flex items-center">
<i data-feather="sliders" class="w-5 h-5 mr-2 text-primary-500"></i>
Properties
</h2>
<div class="space-y-4" id="propertyControls">
<div class="text-gray-400 italic text-center py-8">
Select an element to edit its properties
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Hidden elements for export -->
<a id="downloadLink" style="display: none;"></a>
<!-- Footer -->
<footer class="border-t border-gray-800 py-8 mt-12">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="flex items-center space-x-2 mb-4 md:mb-0">
<div class="w-8 h-8 rounded-full bg-primary-500 flex items-center justify-center">
<i data-feather="maximize-2" class="text-white w-4 h-4"></i>
</div>
<span class="text-lg font-bold">QR Wizardry</span>
</div>
<div class="text-sm text-gray-500">
© 2023 QR Wizardry Portal. All rights reserved.
</div>
</div>
</div>
</footer>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<script>
feather.replace();
document.addEventListener('DOMContentLoaded', function() {
const canvas = new fabric.Canvas('designCanvas', {
backgroundColor: '#ffffff',
selection: true,
preserveObjectStacking: true
});
// Add QR code element
document.getElementById('addQR').addEventListener('click', function() {
fabric.Image.fromURL('https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=Example', function(img) {
img.set({
left: 50,
top: 50,
scaleX: 1,
scaleY: 1,
cornerStyle: 'circle',
cornerColor: '#3b82f6',
cornerSize: 10,
transparentCorners: false,
borderColor: '#3b82f6',
borderScaleFactor: 1.5,
hasRotatingPoint: true
});
canvas.add(img);
canvas.setActiveObject(img);
updatePropertiesPanel(img);
});
});
// Add text element
document.getElementById('addText').addEventListener('click', function() {
const text = new fabric.Text('Double click to edit', {
left: 100,
top: 100,
fontFamily: 'Inter',
fontSize: 20,
fill: '#000000',
hasControls: true,
cornerStyle: 'circle',
cornerColor: '#3b82f6',
cornerSize: 10,
transparentCorners: false
});
canvas.add(text);
canvas.setActiveObject(text);
updatePropertiesPanel(text);
text.on('editing:entered', function() {
text.set('fill', '#3b82f6');
canvas.renderAll();
});
text.on('editing:exited', function() {
text.set('fill', '#000000');
canvas.renderAll();
});
});
// Add image element
document.getElementById('addImage').addEventListener('click', function() {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = function(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(event) {
fabric.Image.fromURL(event.target.result, function(img) {
img.set({
left: 150,
top: 150,
scaleX: 0.5,
scaleY: 0.5,
hasControls: true,
cornerStyle: 'circle',
cornerColor: '#3b82f6',
cornerSize: 10,
transparentCorners: false
});
canvas.add(img);
canvas.setActiveObject(img);
updatePropertiesPanel(img);
});
};
reader.readAsDataURL(file);
}
};
input.click();
});
// Delete selected element
document.getElementById('deleteSelected').addEventListener('click', function() {
const activeObject = canvas.getActiveObject();
if (activeObject) {
canvas.remove(activeObject);
updatePropertiesPanel(null);
}
});
// Clear canvas
document.getElementById('clearCanvas').addEventListener('click', function() {
if (confirm('Are you sure you want to clear the canvas?')) {
canvas.clear();
updatePropertiesPanel(null);
}
});
// Canvas selection event
canvas.on('selection:created', function(options) {
updatePropertiesPanel(options.selected[0]);
});
canvas.on('selection:updated', function(options) {
updatePropertiesPanel(options.selected[0]);
});
canvas.on('selection:cleared', function() {
updatePropertiesPanel(null);
});
// Update properties panel based on selected object
function updatePropertiesPanel(obj) {
const panel = document.getElementById('propertyControls');
if (!obj) {
panel.innerHTML = `
<div class="text-gray-400 italic text-center py-8">
Select an element to edit its properties
</div>
`;
return;
}
let controls = '';
// Common properties
controls += `
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Position</label>
<div class="grid grid-cols-2 gap-2">
<div>
<input type="number" class="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1"
id="posX" value="${Math.round(obj.left)}" placeholder="X">
</div>
<div>
<input type="number" class="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1"
id="posY" value="${Math.round(obj.top)}" placeholder="Y">
</div>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Size</label>
<div class="grid grid-cols-2 gap-2">
<div>
<input type="number" class="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1"
id="width" value="${Math.round(obj.width * obj.scaleX)}" placeholder="Width">
</div>
<div>
<input type="number" class="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1"
id="height" value="${Math.round(obj.height * obj.scaleY)}" placeholder="Height">
</div>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Rotation</label>
<input type="range" class="w-full bg-gray-800" id="rotation"
min="0" max="360" value="${Math.round(obj.angle || 0)}">
</div>
`;
// Text specific properties
if (obj.type === 'text') {
controls += `
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Text Content</label>
<textarea class="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1"
id="textContent" rows="2">${obj.text}</textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Font Size</label>
<input type="number" class="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1"
id="fontSize" value="${obj.fontSize}">
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Text Color</label>
<input type="color" class="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1"
id="textColor" value="${rgbToHex(obj.fill)}">
</div>
`;
}
// Image specific properties
if (obj.type === 'image') {
controls += `
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Opacity</label>
<input type="range" class="w-full bg-gray-800" id="opacity"
min="0" max="1" step="0.1" value="${obj.opacity}">
</div>
`;
}
panel.innerHTML = controls;
// Add event listeners for property changes
document.getElementById('posX')?.addEventListener('change', function(e) {
obj.set('left', parseInt(e.target.value));
canvas.renderAll();
});
document.getElementById('posY')?.addEventListener('change', function(e) {
obj.set('top', parseInt(e.target.value));
canvas.renderAll();
});
document.getElementById('width')?.addEventListener('change', function(e) {
const scale = parseInt(e.target.value) / obj.width;
obj.set('scaleX', scale);
canvas.renderAll();
});
document.getElementById('height')?.addEventListener('change', function(e) {
const scale = parseInt(e.target.value) / obj.height;
obj.set('scaleY', scale);
canvas.renderAll();
});
document.getElementById('rotation')?.addEventListener('input', function(e) {
obj.set('angle', parseInt(e.target.value));
canvas.renderAll();
});
if (obj.type === 'text') {
document.getElementById('textContent')?.addEventListener('change', function(e) {
obj.set('text', e.target.value);
canvas.renderAll();
});
document.getElementById('fontSize')?.addEventListener('change', function(e) {
obj.set('fontSize', parseInt(e.target.value));
canvas.renderAll();
});
document.getElementById('textColor')?.addEventListener('change', function(e) {
obj.set('fill', e.target.value);
canvas.renderAll();
});
}
if (obj.type === 'image') {
document.getElementById('opacity')?.addEventListener('input', function(e) {
obj.set('opacity', parseFloat(e.target.value));
canvas.renderAll();
});
}
}
// Helper function to convert RGB to HEX
function rgbToHex(rgb) {
if (!rgb) return '#000000';
if (typeof rgb === 'string' && rgb.startsWith('#')) return rgb;
return '#' +
(rgb[0] | 1 << 8).toString(16).slice(1) +
(rgb[1] | 1 << 8).toString(16).slice(1) +
(rgb[2] | 1 << 8).toString(16).slice(1);
}
// Set canvas size to fit container
function resizeCanvas() {
const container = document.querySelector('.border-2.border-dashed');
if (container) {
const width = container.clientWidth;
canvas.setWidth(width);
canvas.setHeight(600); // Fixed height for demo
canvas.renderAll();
}
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
});
</script>
</body>
</html>