Spaces:
Paused
Paused
Update index.html
Browse files- index.html +4 -53
index.html
CHANGED
|
@@ -9,7 +9,6 @@
|
|
| 9 |
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;900&display=swap" rel="stylesheet">
|
| 10 |
|
| 11 |
<style>
|
| 12 |
-
/* Глобальное применение шрифта ко всем элементам */
|
| 13 |
* {
|
| 14 |
font-family: 'Nunito', sans-serif;
|
| 15 |
box-sizing: border-box;
|
|
@@ -61,7 +60,6 @@
|
|
| 61 |
overflow-y: auto;
|
| 62 |
}
|
| 63 |
|
| 64 |
-
/* Скроллбар для контролов */
|
| 65 |
.controls::-webkit-scrollbar { width: 8px; }
|
| 66 |
.controls::-webkit-scrollbar-track { background: #222; border-radius: 4px; }
|
| 67 |
.controls::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }
|
|
@@ -149,7 +147,6 @@
|
|
| 149 |
margin-top: 5px;
|
| 150 |
}
|
| 151 |
|
| 152 |
-
/* Стили для ссылки Disclaimer */
|
| 153 |
.disclaimer-link {
|
| 154 |
text-align: center;
|
| 155 |
margin-top: 10px;
|
|
@@ -167,9 +164,8 @@
|
|
| 167 |
color: #999;
|
| 168 |
}
|
| 169 |
|
| 170 |
-
/* Стили модального окна */
|
| 171 |
.modal-overlay {
|
| 172 |
-
display: none;
|
| 173 |
position: fixed;
|
| 174 |
top: 0;
|
| 175 |
left: 0;
|
|
@@ -231,7 +227,6 @@
|
|
| 231 |
font-weight: 900;
|
| 232 |
}
|
| 233 |
|
| 234 |
-
/* Скроллбар внутри модалки */
|
| 235 |
.legal-text::-webkit-scrollbar { width: 6px; }
|
| 236 |
.legal-text::-webkit-scrollbar-track { background: #222; }
|
| 237 |
.legal-text::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }
|
|
@@ -257,29 +252,24 @@
|
|
| 257 |
<h1>MandreIcon Creator</h1>
|
| 258 |
|
| 259 |
<div class="container">
|
| 260 |
-
<!-- Область превью -->
|
| 261 |
<div class="preview-area">
|
| 262 |
<canvas id="mainCanvas" width="512" height="512"></canvas>
|
| 263 |
<p class="hint">Кликни на стикер для выделения. <br>Delete = удалить.</p>
|
| 264 |
</div>
|
| 265 |
|
| 266 |
-
<!-- Панель управления -->
|
| 267 |
<div class="controls">
|
| 268 |
|
| 269 |
<div class="section-title">Фон (Base Icon)</div>
|
| 270 |
|
| 271 |
-
<!-- Драг-н-дроп SVG -->
|
| 272 |
<div class="drop-zone" id="dropZoneBase">
|
| 273 |
<p><b>SVG</b> (Градиентный фон)<br>Клик или Drop</p>
|
| 274 |
<input type="file" id="fileInputBase" class="hidden-input" accept=".svg">
|
| 275 |
</div>
|
| 276 |
|
| 277 |
-
<!-- Текстовые настройки -->
|
| 278 |
<div>
|
| 279 |
<input type="text" id="textInput" placeholder="Или текст (A)">
|
| 280 |
</div>
|
| 281 |
|
| 282 |
-
<!-- Ползунки настроек фона -->
|
| 283 |
<div>
|
| 284 |
<label>Размер хуйни этой: <span id="scaleVal">100%</span></label>
|
| 285 |
<input type="range" id="scaleRange" min="10" max="200" value="100">
|
|
@@ -302,14 +292,12 @@
|
|
| 302 |
|
| 303 |
<button class="btn-primary" id="downloadBtn">Скачать PNG</button>
|
| 304 |
|
| 305 |
-
<!-- Кнопка вызова Disclaimer -->
|
| 306 |
<div class="disclaimer-link">
|
| 307 |
<span id="openDisclaimerBtn">Отказ от ответственности</span>
|
| 308 |
</div>
|
| 309 |
</div>
|
| 310 |
</div>
|
| 311 |
|
| 312 |
-
<!-- Модальное окно -->
|
| 313 |
<div class="modal-overlay" id="modalOverlay">
|
| 314 |
<div class="modal-content">
|
| 315 |
<button class="close-btn" id="closeModalBtn">×</button>
|
|
@@ -341,7 +329,6 @@
|
|
| 341 |
const canvas = document.getElementById('mainCanvas');
|
| 342 |
const ctx = canvas.getContext('2d');
|
| 343 |
|
| 344 |
-
// Элементы UI
|
| 345 |
const dropZoneBase = document.getElementById('dropZoneBase');
|
| 346 |
const fileInputBase = document.getElementById('fileInputBase');
|
| 347 |
const textInput = document.getElementById('textInput');
|
|
@@ -353,25 +340,21 @@
|
|
| 353 |
const addStickerBtn = document.getElementById('addStickerBtn');
|
| 354 |
const stickerInput = document.getElementById('stickerInput');
|
| 355 |
|
| 356 |
-
// Константы
|
| 357 |
const BG_COLOR = '#E13839';
|
| 358 |
const GRADIENT_START = '#FFFFFF';
|
| 359 |
const GRADIENT_END = '#FFACC7';
|
| 360 |
|
| 361 |
-
// Состояние "Базового слоя" (градиент)
|
| 362 |
let baseLayer = {
|
| 363 |
-
type: 'none',
|
| 364 |
object: null,
|
| 365 |
scale: 1,
|
| 366 |
x: 0,
|
| 367 |
y: 0
|
| 368 |
};
|
| 369 |
|
| 370 |
-
// Состояние "Стикеров" (PNG поверх)
|
| 371 |
let stickers = [];
|
| 372 |
let selectedStickerIndex = -1;
|
| 373 |
|
| 374 |
-
// Переменные для манипуляции мышью
|
| 375 |
let isDragging = false;
|
| 376 |
let isRotating = false;
|
| 377 |
let isResizing = false;
|
|
@@ -380,14 +363,10 @@
|
|
| 380 |
let initialDistance = 0;
|
| 381 |
let initialScale = 1;
|
| 382 |
|
| 383 |
-
// Инициализация
|
| 384 |
window.onload = () => {
|
| 385 |
drawAll();
|
| 386 |
};
|
| 387 |
|
| 388 |
-
// ==========================================
|
| 389 |
-
// ЛОГИКА МОДАЛЬНОГО ОКНА
|
| 390 |
-
// ==========================================
|
| 391 |
const modalOverlay = document.getElementById('modalOverlay');
|
| 392 |
const openDisclaimerBtn = document.getElementById('openDisclaimerBtn');
|
| 393 |
const closeModalBtn = document.getElementById('closeModalBtn');
|
|
@@ -411,11 +390,6 @@
|
|
| 411 |
if (e.target === modalOverlay) closeModal();
|
| 412 |
});
|
| 413 |
|
| 414 |
-
|
| 415 |
-
// ==========================================
|
| 416 |
-
// 1. ЛОГИКА БАЗОВОГО СЛОЯ (SVG/TEXT)
|
| 417 |
-
// ==========================================
|
| 418 |
-
|
| 419 |
function updateBaseLayerParams() {
|
| 420 |
baseLayer.scale = parseInt(scaleRange.value) / 100;
|
| 421 |
baseLayer.x = parseInt(offsetXRange.value);
|
|
@@ -469,11 +443,6 @@
|
|
| 469 |
reader.readAsDataURL(file);
|
| 470 |
}
|
| 471 |
|
| 472 |
-
|
| 473 |
-
// ==========================================
|
| 474 |
-
// 2. ЛОГИКА СТИКЕРОВ (PNG)
|
| 475 |
-
// ==========================================
|
| 476 |
-
|
| 477 |
addStickerBtn.addEventListener('click', () => stickerInput.click());
|
| 478 |
stickerInput.addEventListener('change', (e) => {
|
| 479 |
Array.from(e.target.files).forEach(file => {
|
|
@@ -523,24 +492,16 @@
|
|
| 523 |
}
|
| 524 |
});
|
| 525 |
|
| 526 |
-
|
| 527 |
-
// ==========================================
|
| 528 |
-
// 3. ГЛАВНАЯ ОТРИСОВКА
|
| 529 |
-
// ==========================================
|
| 530 |
-
|
| 531 |
function drawAll() {
|
| 532 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 533 |
|
| 534 |
-
// 1. Фон
|
| 535 |
ctx.fillStyle = BG_COLOR;
|
| 536 |
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
| 537 |
|
| 538 |
-
// 2. Базовый градиентный слой
|
| 539 |
if (baseLayer.type !== 'none') {
|
| 540 |
drawBaseLayer();
|
| 541 |
}
|
| 542 |
|
| 543 |
-
// 3. Стикеры
|
| 544 |
stickers.forEach((sticker, index) => {
|
| 545 |
ctx.save();
|
| 546 |
ctx.translate(sticker.x, sticker.y);
|
|
@@ -549,7 +510,6 @@
|
|
| 549 |
ctx.restore();
|
| 550 |
});
|
| 551 |
|
| 552 |
-
// 4. Интерфейс управления
|
| 553 |
if (selectedStickerIndex !== -1) {
|
| 554 |
drawControls(stickers[selectedStickerIndex]);
|
| 555 |
}
|
|
@@ -571,7 +531,7 @@
|
|
| 571 |
const w = 300;
|
| 572 |
const h = 300 * (baseLayer.object.height / baseLayer.object.width);
|
| 573 |
tempCtx.drawImage(baseLayer.object, -w/2, -h/2, w, h);
|
| 574 |
-
|
| 575 |
tempCtx.font = "900 300px 'Nunito'";
|
| 576 |
tempCtx.textAlign = "center";
|
| 577 |
tempCtx.textBaseline = "middle";
|
|
@@ -626,11 +586,6 @@
|
|
| 626 |
ctx.restore();
|
| 627 |
}
|
| 628 |
|
| 629 |
-
|
| 630 |
-
// ==========================================
|
| 631 |
-
// 4. ОБРАБОТКА МЫШИ (Canvas)
|
| 632 |
-
// ==========================================
|
| 633 |
-
|
| 634 |
function getMousePos(evt) {
|
| 635 |
const rect = canvas.getBoundingClientRect();
|
| 636 |
const scaleX = canvas.width / rect.width;
|
|
@@ -742,7 +697,6 @@
|
|
| 742 |
}
|
| 743 |
canvas.style.cursor = cursor;
|
| 744 |
|
| 745 |
-
|
| 746 |
if (!sticker) return;
|
| 747 |
|
| 748 |
if (isDragging) {
|
|
@@ -778,16 +732,13 @@
|
|
| 778 |
isResizing = false;
|
| 779 |
});
|
| 780 |
|
| 781 |
-
// ==========================================
|
| 782 |
-
// 5. СКАЧИВАНИЕ
|
| 783 |
-
// ==========================================
|
| 784 |
downloadBtn.addEventListener('click', () => {
|
| 785 |
const prevSelection = selectedStickerIndex;
|
| 786 |
selectedStickerIndex = -1;
|
| 787 |
drawAll();
|
| 788 |
|
| 789 |
const link = document.createElement('a');
|
| 790 |
-
link.download = 'SWAGA_ICON.png';
|
| 791 |
link.href = canvas.toDataURL('image/png');
|
| 792 |
link.click();
|
| 793 |
|
|
|
|
| 9 |
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;900&display=swap" rel="stylesheet">
|
| 10 |
|
| 11 |
<style>
|
|
|
|
| 12 |
* {
|
| 13 |
font-family: 'Nunito', sans-serif;
|
| 14 |
box-sizing: border-box;
|
|
|
|
| 60 |
overflow-y: auto;
|
| 61 |
}
|
| 62 |
|
|
|
|
| 63 |
.controls::-webkit-scrollbar { width: 8px; }
|
| 64 |
.controls::-webkit-scrollbar-track { background: #222; border-radius: 4px; }
|
| 65 |
.controls::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }
|
|
|
|
| 147 |
margin-top: 5px;
|
| 148 |
}
|
| 149 |
|
|
|
|
| 150 |
.disclaimer-link {
|
| 151 |
text-align: center;
|
| 152 |
margin-top: 10px;
|
|
|
|
| 164 |
color: #999;
|
| 165 |
}
|
| 166 |
|
|
|
|
| 167 |
.modal-overlay {
|
| 168 |
+
display: none;
|
| 169 |
position: fixed;
|
| 170 |
top: 0;
|
| 171 |
left: 0;
|
|
|
|
| 227 |
font-weight: 900;
|
| 228 |
}
|
| 229 |
|
|
|
|
| 230 |
.legal-text::-webkit-scrollbar { width: 6px; }
|
| 231 |
.legal-text::-webkit-scrollbar-track { background: #222; }
|
| 232 |
.legal-text::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }
|
|
|
|
| 252 |
<h1>MandreIcon Creator</h1>
|
| 253 |
|
| 254 |
<div class="container">
|
|
|
|
| 255 |
<div class="preview-area">
|
| 256 |
<canvas id="mainCanvas" width="512" height="512"></canvas>
|
| 257 |
<p class="hint">Кликни на стикер для выделения. <br>Delete = удалить.</p>
|
| 258 |
</div>
|
| 259 |
|
|
|
|
| 260 |
<div class="controls">
|
| 261 |
|
| 262 |
<div class="section-title">Фон (Base Icon)</div>
|
| 263 |
|
|
|
|
| 264 |
<div class="drop-zone" id="dropZoneBase">
|
| 265 |
<p><b>SVG</b> (Градиентный фон)<br>Клик или Drop</p>
|
| 266 |
<input type="file" id="fileInputBase" class="hidden-input" accept=".svg">
|
| 267 |
</div>
|
| 268 |
|
|
|
|
| 269 |
<div>
|
| 270 |
<input type="text" id="textInput" placeholder="Или текст (A)">
|
| 271 |
</div>
|
| 272 |
|
|
|
|
| 273 |
<div>
|
| 274 |
<label>Размер хуйни этой: <span id="scaleVal">100%</span></label>
|
| 275 |
<input type="range" id="scaleRange" min="10" max="200" value="100">
|
|
|
|
| 292 |
|
| 293 |
<button class="btn-primary" id="downloadBtn">Скачать PNG</button>
|
| 294 |
|
|
|
|
| 295 |
<div class="disclaimer-link">
|
| 296 |
<span id="openDisclaimerBtn">Отказ от ответственности</span>
|
| 297 |
</div>
|
| 298 |
</div>
|
| 299 |
</div>
|
| 300 |
|
|
|
|
| 301 |
<div class="modal-overlay" id="modalOverlay">
|
| 302 |
<div class="modal-content">
|
| 303 |
<button class="close-btn" id="closeModalBtn">×</button>
|
|
|
|
| 329 |
const canvas = document.getElementById('mainCanvas');
|
| 330 |
const ctx = canvas.getContext('2d');
|
| 331 |
|
|
|
|
| 332 |
const dropZoneBase = document.getElementById('dropZoneBase');
|
| 333 |
const fileInputBase = document.getElementById('fileInputBase');
|
| 334 |
const textInput = document.getElementById('textInput');
|
|
|
|
| 340 |
const addStickerBtn = document.getElementById('addStickerBtn');
|
| 341 |
const stickerInput = document.getElementById('stickerInput');
|
| 342 |
|
|
|
|
| 343 |
const BG_COLOR = '#E13839';
|
| 344 |
const GRADIENT_START = '#FFFFFF';
|
| 345 |
const GRADIENT_END = '#FFACC7';
|
| 346 |
|
|
|
|
| 347 |
let baseLayer = {
|
| 348 |
+
type: 'none',
|
| 349 |
object: null,
|
| 350 |
scale: 1,
|
| 351 |
x: 0,
|
| 352 |
y: 0
|
| 353 |
};
|
| 354 |
|
|
|
|
| 355 |
let stickers = [];
|
| 356 |
let selectedStickerIndex = -1;
|
| 357 |
|
|
|
|
| 358 |
let isDragging = false;
|
| 359 |
let isRotating = false;
|
| 360 |
let isResizing = false;
|
|
|
|
| 363 |
let initialDistance = 0;
|
| 364 |
let initialScale = 1;
|
| 365 |
|
|
|
|
| 366 |
window.onload = () => {
|
| 367 |
drawAll();
|
| 368 |
};
|
| 369 |
|
|
|
|
|
|
|
|
|
|
| 370 |
const modalOverlay = document.getElementById('modalOverlay');
|
| 371 |
const openDisclaimerBtn = document.getElementById('openDisclaimerBtn');
|
| 372 |
const closeModalBtn = document.getElementById('closeModalBtn');
|
|
|
|
| 390 |
if (e.target === modalOverlay) closeModal();
|
| 391 |
});
|
| 392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
function updateBaseLayerParams() {
|
| 394 |
baseLayer.scale = parseInt(scaleRange.value) / 100;
|
| 395 |
baseLayer.x = parseInt(offsetXRange.value);
|
|
|
|
| 443 |
reader.readAsDataURL(file);
|
| 444 |
}
|
| 445 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
addStickerBtn.addEventListener('click', () => stickerInput.click());
|
| 447 |
stickerInput.addEventListener('change', (e) => {
|
| 448 |
Array.from(e.target.files).forEach(file => {
|
|
|
|
| 492 |
}
|
| 493 |
});
|
| 494 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 495 |
function drawAll() {
|
| 496 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 497 |
|
|
|
|
| 498 |
ctx.fillStyle = BG_COLOR;
|
| 499 |
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
| 500 |
|
|
|
|
| 501 |
if (baseLayer.type !== 'none') {
|
| 502 |
drawBaseLayer();
|
| 503 |
}
|
| 504 |
|
|
|
|
| 505 |
stickers.forEach((sticker, index) => {
|
| 506 |
ctx.save();
|
| 507 |
ctx.translate(sticker.x, sticker.y);
|
|
|
|
| 510 |
ctx.restore();
|
| 511 |
});
|
| 512 |
|
|
|
|
| 513 |
if (selectedStickerIndex !== -1) {
|
| 514 |
drawControls(stickers[selectedStickerIndex]);
|
| 515 |
}
|
|
|
|
| 531 |
const w = 300;
|
| 532 |
const h = 300 * (baseLayer.object.height / baseLayer.object.width);
|
| 533 |
tempCtx.drawImage(baseLayer.object, -w/2, -h/2, w, h);
|
| 534 |
+
iile} else if (baseLayer.type === 'text') {
|
| 535 |
tempCtx.font = "900 300px 'Nunito'";
|
| 536 |
tempCtx.textAlign = "center";
|
| 537 |
tempCtx.textBaseline = "middle";
|
|
|
|
| 586 |
ctx.restore();
|
| 587 |
}
|
| 588 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
function getMousePos(evt) {
|
| 590 |
const rect = canvas.getBoundingClientRect();
|
| 591 |
const scaleX = canvas.width / rect.width;
|
|
|
|
| 697 |
}
|
| 698 |
canvas.style.cursor = cursor;
|
| 699 |
|
|
|
|
| 700 |
if (!sticker) return;
|
| 701 |
|
| 702 |
if (isDragging) {
|
|
|
|
| 732 |
isResizing = false;
|
| 733 |
});
|
| 734 |
|
|
|
|
|
|
|
|
|
|
| 735 |
downloadBtn.addEventListener('click', () => {
|
| 736 |
const prevSelection = selectedStickerIndex;
|
| 737 |
selectedStickerIndex = -1;
|
| 738 |
drawAll();
|
| 739 |
|
| 740 |
const link = document.createElement('a');
|
| 741 |
+
link.download = 'SWAGA_ICON.png';
|
| 742 |
link.href = canvas.toDataURL('image/png');
|
| 743 |
link.click();
|
| 744 |
|