sterepando commited on
Commit
2ec7489
·
verified ·
1 Parent(s): b11bc56

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +44 -115
index.html CHANGED
@@ -9,7 +9,7 @@
9
  <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;900&display=swap" rel="stylesheet">
10
 
11
  <style>
12
- /* 1. Шрифт Nunito везде */
13
  * {
14
  font-family: 'Nunito', sans-serif;
15
  box-sizing: border-box;
@@ -61,6 +61,7 @@
61
  overflow-y: auto;
62
  }
63
 
 
64
  .controls::-webkit-scrollbar { width: 8px; }
65
  .controls::-webkit-scrollbar-track { background: #222; border-radius: 4px; }
66
  .controls::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }
@@ -148,6 +149,7 @@
148
  margin-top: 5px;
149
  }
150
 
 
151
  .disclaimer-link {
152
  text-align: center;
153
  margin-top: 10px;
@@ -165,8 +167,9 @@
165
  color: #999;
166
  }
167
 
 
168
  .modal-overlay {
169
- display: none;
170
  position: fixed;
171
  top: 0;
172
  left: 0;
@@ -228,6 +231,7 @@
228
  font-weight: 900;
229
  }
230
 
 
231
  .legal-text::-webkit-scrollbar { width: 6px; }
232
  .legal-text::-webkit-scrollbar-track { background: #222; }
233
  .legal-text::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }
@@ -250,28 +254,32 @@
250
  </head>
251
  <body>
252
 
253
- <!-- 2. Убрано PRO -->
254
  <h1>MandreIcon Creator</h1>
255
 
256
  <div class="container">
 
257
  <div class="preview-area">
258
  <canvas id="mainCanvas" width="512" height="512"></canvas>
259
  <p class="hint">Кликни на стикер для выделения. <br>Delete = удалить.</p>
260
  </div>
261
 
 
262
  <div class="controls">
263
 
264
  <div class="section-title">Фон (Base Icon)</div>
265
 
 
266
  <div class="drop-zone" id="dropZoneBase">
267
  <p><b>SVG</b> (Градиентный фон)<br>Клик или Drop</p>
268
  <input type="file" id="fileInputBase" class="hidden-input" accept=".svg">
269
  </div>
270
 
 
271
  <div>
272
  <input type="text" id="textInput" placeholder="Или текст (A)">
273
  </div>
274
 
 
275
  <div>
276
  <label>Размер хуйни этой: <span id="scaleVal">100%</span></label>
277
  <input type="range" id="scaleRange" min="10" max="200" value="100">
@@ -294,6 +302,7 @@
294
 
295
  <button class="btn-primary" id="downloadBtn">Скачать PNG</button>
296
 
 
297
  <div class="disclaimer-link">
298
  <span id="openDisclaimerBtn">Отказ от ответственности</span>
299
  </div>
@@ -319,7 +328,6 @@
319
  <p><strong>4. Технические ограничения</strong><br>
320
  Разработчик не гарантирует бесперебойную работу сайта, отсутствие программных ошибок или полную совместимость с конкретными устройствами и браузерами.</p>
321
 
322
- <!-- 3. Добавлен пункт о законодательстве стран -->
323
  <p><strong>5. Различия в законодательстве стран</strong><br>
324
  Сайт доступен пользователям по всему миру. Пользователь признает, что законы, регулирующие авторское право, использование символики и распространение контента, могут существенно различаться в зависимости от юрисдикции. Пользователь самостоятельно несет ответственность за соблюдение законодательства страны своего проживания, а также законодательства стран, на территории которых планируется использование созданных материалов. Разработчик не несет ответственности за нарушение пользователем локальных законов.</p>
325
 
@@ -333,6 +341,7 @@
333
  const canvas = document.getElementById('mainCanvas');
334
  const ctx = canvas.getContext('2d');
335
 
 
336
  const dropZoneBase = document.getElementById('dropZoneBase');
337
  const fileInputBase = document.getElementById('fileInputBase');
338
  const textInput = document.getElementById('textInput');
@@ -340,17 +349,29 @@
340
  const offsetYRange = document.getElementById('offsetYRange');
341
  const offsetXRange = document.getElementById('offsetXRange');
342
  const downloadBtn = document.getElementById('downloadBtn');
 
343
  const addStickerBtn = document.getElementById('addStickerBtn');
344
  const stickerInput = document.getElementById('stickerInput');
345
 
 
346
  const BG_COLOR = '#E13839';
347
  const GRADIENT_START = '#FFFFFF';
348
  const GRADIENT_END = '#FFACC7';
349
 
350
- let baseLayer = { type: 'none', object: null, scale: 1, x: 0, y: 0 };
 
 
 
 
 
 
 
 
 
351
  let stickers = [];
352
  let selectedStickerIndex = -1;
353
 
 
354
  let isDragging = false;
355
  let isRotating = false;
356
  let isResizing = false;
@@ -359,111 +380,13 @@
359
  let initialDistance = 0;
360
  let initialScale = 1;
361
 
362
- window.onload = () => { drawAll(); };
363
-
364
- // ==========================================
365
- // МЕТАДАННЫЕ PNG (СЛОЖНАЯ ЧАСТЬ)
366
- // ==========================================
367
-
368
- // Таблица для CRC32
369
- const crcTable = [];
370
- for (let n = 0; n < 256; n++) {
371
- let c = n;
372
- for (let k = 0; k < 8; k++) {
373
- c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
374
- }
375
- crcTable[n] = c;
376
- }
377
-
378
- function crc32(buf) {
379
- let crc = 0xFFFFFFFF;
380
- for (let i = 0; i < buf.length; i++) {
381
- crc = (crc >>> 8) ^ crcTable[(crc ^ buf[i]) & 0xFF];
382
- }
383
- return crc ^ 0xFFFFFFFF;
384
- }
385
-
386
- // Функция создания чанка
387
- function createChunk(type, data) {
388
- const len = data.length;
389
- const buf = new Uint8Array(4 + 4 + len + 4);
390
- const view = new DataView(buf.buffer);
391
-
392
- view.setUint32(0, len); // Length
393
-
394
- // Type
395
- buf[4] = type.charCodeAt(0);
396
- buf[5] = type.charCodeAt(1);
397
- buf[6] = type.charCodeAt(2);
398
- buf[7] = type.charCodeAt(3);
399
-
400
- // Data
401
- buf.set(data, 8);
402
-
403
- // CRC (Type + Data)
404
- const crc = crc32(buf.subarray(4, 8 + len));
405
- view.setUint32(8 + len, crc);
406
-
407
- return buf;
408
- }
409
-
410
- function createTextChunk(keyword, text) {
411
- // tEXt поддерживает Latin1, но современные вьюеры едят UTF8
412
- const keywordArr = new TextEncoder().encode(keyword);
413
- const textArr = new TextEncoder().encode(text);
414
-
415
- // Keyword + Null separator + Text
416
- const data = new Uint8Array(keywordArr.length + 1 + textArr.length);
417
- data.set(keywordArr, 0);
418
- data[keywordArr.length] = 0; // Null separator
419
- data.set(textArr, keywordArr.length + 1);
420
-
421
- return createChunk("tEXt", data);
422
- }
423
-
424
- async function downloadWithMetadata(filename) {
425
- // 1. Получаем сырой Blob из канваса
426
- const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
427
- const buffer = await blob.arrayBuffer();
428
- const uint8 = new Uint8Array(buffer);
429
-
430
- // 2. PNG структура: Signature (8 bytes) + IHDR Chunk (25 bytes) + ...
431
- // Мы вставим метаданные сразу после IHDR (индекс 33)
432
- const insertPos = 33;
433
-
434
- // 3. Генерируем чанки метаданных
435
- const chunkAuthor = createTextChunk("Author", "@sterepando");
436
- const chunkComment = createTextChunk("Comment", "сделанно плагином @swagnonher");
437
- // Для надежности дублируем в Description
438
- const chunkDesc = createTextChunk("Description", "сделанно плагином @swagnonher & @sterepando");
439
-
440
- // 4. Собираем новый файл
441
- const newFileSize = uint8.length + chunkAuthor.length + chunkComment.length + chunkDesc.length;
442
- const newBuf = new Uint8Array(newFileSize);
443
-
444
- // Копируем заголовок (Sig + IHDR)
445
- newBuf.set(uint8.subarray(0, insertPos), 0);
446
-
447
- // Вставляем метаданные
448
- let offset = insertPos;
449
- newBuf.set(chunkAuthor, offset); offset += chunkAuthor.length;
450
- newBuf.set(chunkComment, offset); offset += chunkComment.length;
451
- newBuf.set(chunkDesc, offset); offset += chunkDesc.length;
452
-
453
- // Копируем остаток файла
454
- newBuf.set(uint8.subarray(insertPos), offset);
455
-
456
- // 5. Скачиваем
457
- const newBlob = new Blob([newBuf], { type: 'image/png' });
458
- const link = document.createElement('a');
459
- link.download = filename;
460
- link.href = URL.createObjectURL(newBlob);
461
- link.click();
462
- URL.revokeObjectURL(link.href);
463
- }
464
 
465
  // ==========================================
466
- // МОДАЛКА
467
  // ==========================================
468
  const modalOverlay = document.getElementById('modalOverlay');
469
  const openDisclaimerBtn = document.getElementById('openDisclaimerBtn');
@@ -490,7 +413,7 @@
490
 
491
 
492
  // ==========================================
493
- // ЛОГИКА БАЗЫ
494
  // ==========================================
495
 
496
  function updateBaseLayerParams() {
@@ -548,7 +471,7 @@
548
 
549
 
550
  // ==========================================
551
- // ЛОГИКА СТИКЕРОВ
552
  // ==========================================
553
 
554
  addStickerBtn.addEventListener('click', () => stickerInput.click());
@@ -602,19 +525,22 @@
602
 
603
 
604
  // ==========================================
605
- // ОТРИСОВКА
606
  // ==========================================
607
 
608
  function drawAll() {
609
  ctx.clearRect(0, 0, canvas.width, canvas.height);
610
 
 
611
  ctx.fillStyle = BG_COLOR;
612
  ctx.fillRect(0, 0, canvas.width, canvas.height);
613
 
 
614
  if (baseLayer.type !== 'none') {
615
  drawBaseLayer();
616
  }
617
 
 
618
  stickers.forEach((sticker, index) => {
619
  ctx.save();
620
  ctx.translate(sticker.x, sticker.y);
@@ -623,6 +549,7 @@
623
  ctx.restore();
624
  });
625
 
 
626
  if (selectedStickerIndex !== -1) {
627
  drawControls(stickers[selectedStickerIndex]);
628
  }
@@ -701,7 +628,7 @@
701
 
702
 
703
  // ==========================================
704
- // МЫШЬ
705
  // ==========================================
706
 
707
  function getMousePos(evt) {
@@ -852,15 +779,17 @@
852
  });
853
 
854
  // ==========================================
855
- // СКАЧИВАНИЕ (ОБНОВЛЕННОЕ)
856
  // ==========================================
857
  downloadBtn.addEventListener('click', () => {
858
  const prevSelection = selectedStickerIndex;
859
  selectedStickerIndex = -1;
860
  drawAll();
861
 
862
- // Используем функцию с внедрением метаданных
863
- downloadWithMetadata('SWAGA_ICON.png');
 
 
864
 
865
  selectedStickerIndex = prevSelection;
866
  drawAll();
 
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
  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
  margin-top: 5px;
150
  }
151
 
152
+ /* Стили для ссылки Disclaimer */
153
  .disclaimer-link {
154
  text-align: center;
155
  margin-top: 10px;
 
167
  color: #999;
168
  }
169
 
170
+ /* Стили модального окна */
171
  .modal-overlay {
172
+ display: none; /* Скрыто по умолчанию */
173
  position: fixed;
174
  top: 0;
175
  left: 0;
 
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; }
 
254
  </head>
255
  <body>
256
 
 
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
 
303
  <button class="btn-primary" id="downloadBtn">Скачать PNG</button>
304
 
305
+ <!-- Кнопка вызова Disclaimer -->
306
  <div class="disclaimer-link">
307
  <span id="openDisclaimerBtn">Отказ от ответственности</span>
308
  </div>
 
328
  <p><strong>4. Технические ограничения</strong><br>
329
  Разработчик не гарантирует бесперебойную работу сайта, отсутствие программных ошибок или полную совместимость с конкретными устройствами и браузерами.</p>
330
 
 
331
  <p><strong>5. Различия в законодательстве стран</strong><br>
332
  Сайт доступен пользователям по всему миру. Пользователь признает, что законы, регулирующие авторское право, использование символики и распространение контента, могут существенно различаться в зависимости от юрисдикции. Пользователь самостоятельно несет ответственность за соблюдение законодательства страны своего проживания, а также законодательства стран, на территории которых планируется использование созданных материалов. Разработчик не несет ответственности за нарушение пользователем локальных законов.</p>
333
 
 
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');
 
349
  const offsetYRange = document.getElementById('offsetYRange');
350
  const offsetXRange = document.getElementById('offsetXRange');
351
  const downloadBtn = document.getElementById('downloadBtn');
352
+
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', // 'image' | 'text' | '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
  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');
 
413
 
414
 
415
  // ==========================================
416
+ // 1. ЛОГИКА БАЗОВОГО СЛОЯ (SVG/TEXT)
417
  // ==========================================
418
 
419
  function updateBaseLayerParams() {
 
471
 
472
 
473
  // ==========================================
474
+ // 2. ЛОГИКА СТИКЕРОВ (PNG)
475
  // ==========================================
476
 
477
  addStickerBtn.addEventListener('click', () => stickerInput.click());
 
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
  ctx.restore();
550
  });
551
 
552
+ // 4. Интерфейс управления
553
  if (selectedStickerIndex !== -1) {
554
  drawControls(stickers[selectedStickerIndex]);
555
  }
 
628
 
629
 
630
  // ==========================================
631
+ // 4. ОБРАБОТКА МЫШИ (Canvas)
632
  // ==========================================
633
 
634
  function getMousePos(evt) {
 
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'; // Убрано PRO
791
+ link.href = canvas.toDataURL('image/png');
792
+ link.click();
793
 
794
  selectedStickerIndex = prevSelection;
795
  drawAll();