576 lines
18 KiB
HTML
576 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>AeThex Logo Designer - Interactive</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Inter', -apple-system, system-ui, sans-serif;
|
||
background: #111827;
|
||
color: #F9FAFB;
|
||
padding: 1rem;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
display: grid;
|
||
grid-template-columns: 300px 1fr 300px;
|
||
gap: 1rem;
|
||
height: calc(100vh - 2rem);
|
||
}
|
||
|
||
.panel {
|
||
background: rgba(31, 41, 55, 0.8);
|
||
border: 1px solid rgba(139, 92, 246, 0.2);
|
||
border-radius: 8px;
|
||
padding: 1rem;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 1.5rem;
|
||
margin-bottom: 1rem;
|
||
background: linear-gradient(135deg, #8B5CF6, #06B6D4);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
}
|
||
|
||
h2 {
|
||
font-size: 1rem;
|
||
color: #06B6D4;
|
||
margin: 1.5rem 0 0.75rem;
|
||
border-bottom: 1px solid rgba(6, 182, 212, 0.3);
|
||
padding-bottom: 0.5rem;
|
||
}
|
||
|
||
.canvas-wrapper {
|
||
background: #1F2937;
|
||
border: 2px solid rgba(139, 92, 246, 0.3);
|
||
border-radius: 8px;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
canvas {
|
||
width: 100%;
|
||
height: 100%;
|
||
cursor: crosshair;
|
||
}
|
||
|
||
.control-group {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
label {
|
||
display: block;
|
||
color: #9CA3AF;
|
||
font-size: 0.875rem;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
input[type="range"] {
|
||
width: 100%;
|
||
height: 4px;
|
||
background: rgba(139, 92, 246, 0.2);
|
||
outline: none;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
input[type="range"]::-webkit-slider-thumb {
|
||
appearance: none;
|
||
width: 16px;
|
||
height: 16px;
|
||
background: #8B5CF6;
|
||
cursor: pointer;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
input[type="color"] {
|
||
width: 100%;
|
||
height: 40px;
|
||
border: 1px solid rgba(139, 92, 246, 0.3);
|
||
border-radius: 4px;
|
||
background: transparent;
|
||
cursor: pointer;
|
||
}
|
||
|
||
select {
|
||
width: 100%;
|
||
padding: 0.5rem;
|
||
background: rgba(139, 92, 246, 0.1);
|
||
border: 1px solid rgba(139, 92, 246, 0.3);
|
||
color: #F9FAFB;
|
||
border-radius: 4px;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
button {
|
||
width: 100%;
|
||
padding: 0.75rem;
|
||
background: linear-gradient(135deg, #8B5CF6, #7C3AED);
|
||
border: none;
|
||
color: white;
|
||
border-radius: 6px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
margin-bottom: 0.5rem;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
button:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
|
||
}
|
||
|
||
button.secondary {
|
||
background: rgba(6, 182, 212, 0.2);
|
||
border: 1px solid rgba(6, 182, 212, 0.3);
|
||
}
|
||
|
||
.value-display {
|
||
float: right;
|
||
color: #06B6D4;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.preset-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 0.5rem;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.preset-btn {
|
||
padding: 0.5rem;
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.toggle {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.toggle button {
|
||
flex: 1;
|
||
padding: 0.5rem;
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.toggle button.active {
|
||
background: linear-gradient(135deg, #06B6D4, #0891B2);
|
||
}
|
||
|
||
#coordinates {
|
||
position: absolute;
|
||
top: 0.5rem;
|
||
right: 0.5rem;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
padding: 0.5rem;
|
||
border-radius: 4px;
|
||
font-family: monospace;
|
||
font-size: 0.75rem;
|
||
color: #22D3EE;
|
||
}
|
||
|
||
.export-info {
|
||
background: rgba(6, 182, 212, 0.1);
|
||
border: 1px solid rgba(6, 182, 212, 0.2);
|
||
border-radius: 4px;
|
||
padding: 0.75rem;
|
||
font-size: 0.75rem;
|
||
color: #9CA3AF;
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
.shape-buttons {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 0.5rem;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.shape-btn {
|
||
padding: 0.5rem;
|
||
font-size: 0.75rem;
|
||
aspect-ratio: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<!-- Left Panel - Tools -->
|
||
<div class="panel">
|
||
<h1>🎨 Logo Designer</h1>
|
||
|
||
<h2>Shape Tools</h2>
|
||
<div class="shape-buttons">
|
||
<button class="shape-btn" onclick="setTool('circle')">⭕ Circle</button>
|
||
<button class="shape-btn" onclick="setTool('line')">📏 Line</button>
|
||
<button class="shape-btn" onclick="setTool('triangle')">🔺 Triangle</button>
|
||
<button class="shape-btn" onclick="setTool('square')">⬛ Square</button>
|
||
<button class="shape-btn" onclick="setTool('hexagon')">⬡ Hex</button>
|
||
<button class="shape-btn" onclick="setTool('star')">⭐ Star</button>
|
||
</div>
|
||
|
||
<h2>Colors</h2>
|
||
<div class="control-group">
|
||
<label>Fill Color</label>
|
||
<input type="color" id="fillColor" value="#8B5CF6" onchange="updateSettings()">
|
||
</div>
|
||
<div class="control-group">
|
||
<label>Stroke Color</label>
|
||
<input type="color" id="strokeColor" value="#06B6D4" onchange="updateSettings()">
|
||
</div>
|
||
|
||
<h2>Style</h2>
|
||
<div class="control-group">
|
||
<label>Stroke Width <span class="value-display" id="strokeWidthVal">3</span></label>
|
||
<input type="range" id="strokeWidth" min="0" max="20" value="3" oninput="updateSlider(this, 'strokeWidthVal')">
|
||
</div>
|
||
<div class="control-group">
|
||
<label>Opacity <span class="value-display" id="opacityVal">100%</span></label>
|
||
<input type="range" id="opacity" min="0" max="100" value="100" oninput="updateSlider(this, 'opacityVal', '%')">
|
||
</div>
|
||
|
||
<h2>Grid</h2>
|
||
<div class="toggle">
|
||
<button id="gridBtn" class="active" onclick="toggleGrid()">Show Grid</button>
|
||
<button id="snapBtn" class="active" onclick="toggleSnap()">Snap to Grid</button>
|
||
</div>
|
||
<div class="control-group">
|
||
<label>Grid Size <span class="value-display" id="gridSizeVal">20</span></label>
|
||
<input type="range" id="gridSize" min="5" max="50" value="20" oninput="updateSlider(this, 'gridSizeVal'); redraw()">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Center Panel - Canvas -->
|
||
<div class="panel">
|
||
<div class="canvas-wrapper">
|
||
<canvas id="canvas" width="800" height="800"></canvas>
|
||
<div id="coordinates">X: 0, Y: 0</div>
|
||
</div>
|
||
<div class="toggle" style="margin-top: 1rem;">
|
||
<button onclick="undo()">↶ Undo</button>
|
||
<button onclick="clearCanvas()">🗑️ Clear</button>
|
||
<button onclick="redraw()">🔄 Redraw</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Right Panel - Presets & Export -->
|
||
<div class="panel">
|
||
<h1>⚡ Quick Actions</h1>
|
||
|
||
<h2>Logo Presets</h2>
|
||
<div class="preset-grid">
|
||
<button class="preset-btn" onclick="drawPreset('letterA')">Letter A</button>
|
||
<button class="preset-btn" onclick="drawPreset('hexNet')">Hex Network</button>
|
||
<button class="preset-btn" onclick="drawPreset('triangle')">Triangle</button>
|
||
<button class="preset-btn" onclick="drawPreset('circles')">Circles</button>
|
||
<button class="preset-btn" onclick="drawPreset('infinity')">Infinity</button>
|
||
<button class="preset-btn" onclick="drawPreset('nodes')">Nodes</button>
|
||
</div>
|
||
|
||
<h2>Export Options</h2>
|
||
<button onclick="exportSVG()">💾 Export as SVG</button>
|
||
<button onclick="exportPNG()" class="secondary">📸 Export as PNG</button>
|
||
<button onclick="copyToClipboard()" class="secondary">📋 Copy SVG Code</button>
|
||
|
||
<h2>Export Sizes</h2>
|
||
<div class="preset-grid">
|
||
<button class="preset-btn" onclick="exportPNG(512)">512×512</button>
|
||
<button class="preset-btn" onclick="exportPNG(256)">256×256</button>
|
||
<button class="preset-btn" onclick="exportPNG(128)">128×128</button>
|
||
<button class="preset-btn" onclick="exportPNG(64)">64×64</button>
|
||
</div>
|
||
|
||
<div class="export-info">
|
||
<strong>💡 Tips:</strong><br>
|
||
• Click and drag to draw shapes<br>
|
||
• Grid snapping helps alignment<br>
|
||
• Presets are editable after drawing<br>
|
||
• SVG is scalable, PNG for icons
|
||
</div>
|
||
|
||
<h2>SVG Code</h2>
|
||
<textarea id="svgOutput" rows="10" style="width: 100%; background: rgba(0,0,0,0.3); border: 1px solid rgba(139,92,246,0.3); color: #D1D5DB; padding: 0.5rem; border-radius: 4px; font-family: monospace; font-size: 0.75rem;"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const canvas = document.getElementById('canvas');
|
||
const ctx = canvas.getContext('2d');
|
||
let shapes = [];
|
||
let currentTool = 'circle';
|
||
let isDrawing = false;
|
||
let startX, startY;
|
||
let showGrid = true;
|
||
let snapToGrid = true;
|
||
|
||
const settings = {
|
||
fillColor: '#8B5CF6',
|
||
strokeColor: '#06B6D4',
|
||
strokeWidth: 3,
|
||
opacity: 1,
|
||
gridSize: 20
|
||
};
|
||
|
||
function updateSettings() {
|
||
settings.fillColor = document.getElementById('fillColor').value;
|
||
settings.strokeColor = document.getElementById('strokeColor').value;
|
||
settings.strokeWidth = parseInt(document.getElementById('strokeWidth').value);
|
||
settings.opacity = parseInt(document.getElementById('opacity').value) / 100;
|
||
settings.gridSize = parseInt(document.getElementById('gridSize').value);
|
||
}
|
||
|
||
function updateSlider(slider, displayId, suffix = '') {
|
||
document.getElementById(displayId).textContent = slider.value + suffix;
|
||
updateSettings();
|
||
}
|
||
|
||
function setTool(tool) {
|
||
currentTool = tool;
|
||
}
|
||
|
||
function toggleGrid() {
|
||
showGrid = !showGrid;
|
||
document.getElementById('gridBtn').classList.toggle('active');
|
||
redraw();
|
||
}
|
||
|
||
function toggleSnap() {
|
||
snapToGrid = !snapToGrid;
|
||
document.getElementById('snapBtn').classList.toggle('active');
|
||
}
|
||
|
||
function snap(coord) {
|
||
if (!snapToGrid) return coord;
|
||
return Math.round(coord / settings.gridSize) * settings.gridSize;
|
||
}
|
||
|
||
function drawGrid() {
|
||
if (!showGrid) return;
|
||
|
||
ctx.strokeStyle = 'rgba(6, 182, 212, 0.1)';
|
||
ctx.lineWidth = 0.5;
|
||
|
||
for (let x = 0; x <= canvas.width; x += settings.gridSize) {
|
||
ctx.beginPath();
|
||
ctx.moveTo(x, 0);
|
||
ctx.lineTo(x, canvas.height);
|
||
ctx.stroke();
|
||
}
|
||
|
||
for (let y = 0; y <= canvas.height; y += settings.gridSize) {
|
||
ctx.beginPath();
|
||
ctx.moveTo(0, y);
|
||
ctx.lineTo(canvas.width, y);
|
||
ctx.stroke();
|
||
}
|
||
|
||
// Center lines
|
||
ctx.strokeStyle = 'rgba(34, 211, 238, 0.3)';
|
||
ctx.lineWidth = 1;
|
||
ctx.beginPath();
|
||
ctx.moveTo(canvas.width / 2, 0);
|
||
ctx.lineTo(canvas.width / 2, canvas.height);
|
||
ctx.stroke();
|
||
ctx.beginPath();
|
||
ctx.moveTo(0, canvas.height / 2);
|
||
ctx.lineTo(canvas.width, canvas.height / 2);
|
||
ctx.stroke();
|
||
}
|
||
|
||
function drawShape(shape) {
|
||
ctx.globalAlpha = shape.opacity;
|
||
ctx.fillStyle = shape.fillColor;
|
||
ctx.strokeStyle = shape.strokeColor;
|
||
ctx.lineWidth = shape.strokeWidth;
|
||
|
||
switch(shape.type) {
|
||
case 'circle':
|
||
ctx.beginPath();
|
||
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);
|
||
if (shape.fillColor !== 'transparent') ctx.fill();
|
||
if (shape.strokeWidth > 0) ctx.stroke();
|
||
break;
|
||
case 'line':
|
||
ctx.beginPath();
|
||
ctx.moveTo(shape.x1, shape.y1);
|
||
ctx.lineTo(shape.x2, shape.y2);
|
||
ctx.stroke();
|
||
break;
|
||
case 'triangle':
|
||
ctx.beginPath();
|
||
ctx.moveTo(shape.x, shape.y);
|
||
ctx.lineTo(shape.x - shape.size/2, shape.y + shape.size);
|
||
ctx.lineTo(shape.x + shape.size/2, shape.y + shape.size);
|
||
ctx.closePath();
|
||
if (shape.fillColor !== 'transparent') ctx.fill();
|
||
if (shape.strokeWidth > 0) ctx.stroke();
|
||
break;
|
||
}
|
||
|
||
ctx.globalAlpha = 1;
|
||
}
|
||
|
||
function redraw() {
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
drawGrid();
|
||
shapes.forEach(drawShape);
|
||
generateSVG();
|
||
}
|
||
|
||
function clearCanvas() {
|
||
shapes = [];
|
||
redraw();
|
||
}
|
||
|
||
function undo() {
|
||
shapes.pop();
|
||
redraw();
|
||
}
|
||
|
||
canvas.addEventListener('mousedown', (e) => {
|
||
const rect = canvas.getBoundingClientRect();
|
||
startX = snap((e.clientX - rect.left) * (canvas.width / rect.width));
|
||
startY = snap((e.clientY - rect.top) * (canvas.height / rect.height));
|
||
isDrawing = true;
|
||
});
|
||
|
||
canvas.addEventListener('mousemove', (e) => {
|
||
const rect = canvas.getBoundingClientRect();
|
||
const x = Math.round((e.clientX - rect.left) * (canvas.width / rect.width));
|
||
const y = Math.round((e.clientY - rect.top) * (canvas.height / rect.height));
|
||
document.getElementById('coordinates').textContent = `X: ${x}, Y: ${y}`;
|
||
});
|
||
|
||
canvas.addEventListener('mouseup', (e) => {
|
||
if (!isDrawing) return;
|
||
isDrawing = false;
|
||
|
||
const rect = canvas.getBoundingClientRect();
|
||
const endX = snap((e.clientX - rect.left) * (canvas.width / rect.width));
|
||
const endY = snap((e.clientY - rect.top) * (canvas.height / rect.height));
|
||
|
||
const shape = {
|
||
...settings,
|
||
type: currentTool
|
||
};
|
||
|
||
if (currentTool === 'circle') {
|
||
const radius = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
|
||
shape.x = startX;
|
||
shape.y = startY;
|
||
shape.radius = radius;
|
||
} else if (currentTool === 'line') {
|
||
shape.x1 = startX;
|
||
shape.y1 = startY;
|
||
shape.x2 = endX;
|
||
shape.y2 = endY;
|
||
} else if (currentTool === 'triangle') {
|
||
shape.x = startX;
|
||
shape.y = startY;
|
||
shape.size = Math.abs(endY - startY);
|
||
}
|
||
|
||
shapes.push(shape);
|
||
redraw();
|
||
});
|
||
|
||
function drawPreset(preset) {
|
||
clearCanvas();
|
||
const cx = canvas.width / 2;
|
||
const cy = canvas.height / 2;
|
||
|
||
if (preset === 'letterA') {
|
||
// Perfect A
|
||
shapes.push(
|
||
{type: 'line', x1: cx, y1: cy-100, x2: cx-70, y2: cy+100, strokeColor: settings' #8B5CF6', strokeWidth: 20, opacity: 1, fillColor: 'transparent'},
|
||
{type: 'line', x1: cx, y1: cy-100, x2: cx+70, y2: cy+100, strokeColor: '#8B5CF6', strokeWidth: 20, opacity: 1, fillColor: 'transparent'},
|
||
{type: 'line', x1: cx-40, y1: cy+10, x2: cx+40, y2: cy+10, strokeColor: '#06B6D4', strokeWidth: 8, opacity: 1, fillColor: 'transparent'},
|
||
{type: 'circle', x: cx-40, y: cy+10, radius: 6, fillColor: '#22D3EE', strokeWidth: 0, opacity: 1},
|
||
{type: 'circle', x: cx+40, y: cy+10, radius: 6, fillColor: '#22D3EE', strokeWidth: 0, opacity: 1}
|
||
);
|
||
} else if (preset === 'triangle') {
|
||
shapes.push(
|
||
{type: 'triangle', x: cx, y: cy-80, size: 160, fillColor: 'transparent', strokeColor: '#8B5CF6', strokeWidth: 6, opacity: 1},
|
||
{type: 'line', x1: cx-50, y1: cy+20, x2: cx+50, y2: cy+20, strokeColor: '#06B6D4', strokeWidth: 6, opacity: 1, fillColor: 'transparent'},
|
||
{type: 'circle', x: cx-50, y: cy+20, radius: 8, fillColor: '#22D3EE', strokeWidth: 0, opacity: 1},
|
||
{type: 'circle', x: cx+50, y: cy+20, radius: 8, fillColor: '#22D3EE', strokeWidth: 0, opacity: 1}
|
||
);
|
||
}
|
||
|
||
redraw();
|
||
}
|
||
|
||
function generateSVG() {
|
||
let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="200" height="200">\n`;
|
||
|
||
shapes.forEach(shape => {
|
||
if (shape.type === 'circle') {
|
||
svg += ` <circle cx="${shape.x}" cy="${shape.y}" r="${shape.radius}" fill="${shape.fillColor}" stroke="${shape.strokeColor}" stroke-width="${shape.strokeWidth}" opacity="${shape.opacity}"/>\n`;
|
||
} else if (shape.type === 'line') {
|
||
svg += ` <line x1="${shape.x1}" y1="${shape.y1}" x2="${shape.x2}" y2="${shape.y2}" stroke="${shape.strokeColor}" stroke-width="${shape.strokeWidth}" opacity="${shape.opacity}"/>\n`;
|
||
}
|
||
});
|
||
|
||
svg += `</svg>`;
|
||
document.getElementById('svgOutput').value = svg;
|
||
return svg;
|
||
}
|
||
|
||
function exportSVG() {
|
||
const svg = generateSVG();
|
||
const blob = new Blob([svg], {type: 'image/svg+xml'});
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = 'aethex-logo.svg';
|
||
a.click();
|
||
}
|
||
|
||
function exportPNG(size = 512) {
|
||
const tempCanvas = document.createElement('canvas');
|
||
tempCanvas.width = size;
|
||
tempCanvas.height = size;
|
||
const tempCtx = tempCanvas.getContext('2d');
|
||
|
||
const scale = size / canvas.width;
|
||
tempCtx.scale(scale, scale);
|
||
|
||
shapes.forEach(shape => drawShape.call({ctx: tempCtx}, shape));
|
||
|
||
tempCanvas.toBlob(blob => {
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `aethex-logo-${size}.png`;
|
||
a.click();
|
||
});
|
||
}
|
||
|
||
function copyToClipboard() {
|
||
const svg = document.getElementById('svgOutput').value;
|
||
navigator.clipboard.writeText(svg);
|
||
alert('SVG code copied to clipboard!');
|
||
}
|
||
|
||
// Initialize
|
||
redraw();
|
||
</script>
|
||
</body>
|
||
</html>
|