Загрузка данных


// ==UserScript==
// @name         линейка
// @namespace    http://tampermonkey.net/
// @version      3.0.0
// @description линейка делать ровные полоска
// @author       Assistant & Vetanskiy
// @match        https://drawaria.online/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  window.transparencyBrushEnabled = true;
  window.transparencyBrushOpacity = 0.5;

  // Создаем закадровый буферный холст для рисования 1 слоя без самонаслоения
  const strokeCanvas = document.createElement('canvas');
  const strokeCtx = strokeCanvas.getContext('2d');
  let isDrawingLayer = false;

  function syncCanvasSize() {
    const mainCanvas = document.getElementById('canvas');
    if (mainCanvas) {
      if (strokeCanvas.width !== mainCanvas.width || strokeCanvas.height !== mainCanvas.height) {
        strokeCanvas.width = mainCanvas.width;
        strokeCanvas.height = mainCanvas.height;
      }
    }
  }

  // Преобразование цвета
  window.applyColorAlpha = function (colorStr, alpha) {
    if (!colorStr) return `rgba(0,0,0,${alpha})`;
    colorStr = String(colorStr).trim();

    if (colorStr.startsWith('rgba(')) {
      return colorStr.replace(
        /rgba\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*[^)]+\)/,
        `rgba($1,$2,$3,${alpha})`
      );
    }
    if (colorStr.startsWith('rgb(')) {
      return colorStr.replace(
        /rgb\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^)]+)\)/,
        `rgba($1,$2,$3,${alpha})`
      );
    }
    if (colorStr.startsWith('#')) {
      let hex = colorStr.slice(1);
      if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
      else if (hex.length === 8) hex = hex.slice(0, 6);
      if (hex.length === 6) {
        const r = parseInt(hex.slice(0, 2), 16) || 0;
        const g = parseInt(hex.slice(2, 4), 16) || 0;
        const b = parseInt(hex.slice(2, 4), 16) || 0;
        return `rgba(${r},${g},${b},${alpha})`;
      }
    }
    return `rgba(0,0,0,${alpha})`;
  };

  // 1. Отправка через WebSocket с нужной прозрачностью для всех участников
  const originalSend = WebSocket.prototype.send;
  WebSocket.prototype.send = function (data) {
    if (typeof data === 'string' && window.transparencyBrushEnabled) {
      if (data.startsWith('42[') && data.includes('"drawcmd"')) {
        try {
          const parsed = JSON.parse(data.slice(2));
          if (Array.isArray(parsed) && parsed[0] === 'drawcmd' && Array.isArray(parsed[2])) {
            const payload = parsed[2];
            if (payload.length >= 7 && typeof payload[6] === 'string') {
              // Применяем прозрачность к отправляемому цвету
              payload[6] = window.applyColorAlpha(payload[6], window.transparencyBrushOpacity);
              data = '42' + JSON.stringify(parsed);
            }
          }
        } catch (e) {}
      }
    }
    return originalSend.call(this, data);
  };

  // 2. Отслеживание начала и конца штриха для слияния слоя
  document.addEventListener('pointerdown', e => {
    if (e.target && (e.target.id === 'canvas' || e.target.classList.contains('canvas-drawer'))) {
      syncCanvasSize();
      isDrawingLayer = true;
      strokeCtx.clearRect(0, 0, strokeCanvas.width, strokeCanvas.height);
    }
  }, true);

  const endStroke = () => {
    if (isDrawingLayer) {
      isDrawingLayer = false;
      const mainCanvas = document.getElementById('canvas');
      if (mainCanvas && window.transparencyBrushEnabled) {
        const mainCtx = mainCanvas.getContext('2d');
        mainCtx.save();
        mainCtx.globalAlpha = window.transparencyBrushOpacity;
        mainCtx.drawImage(strokeCanvas, 0, 0);
        mainCtx.restore();
        strokeCtx.clearRect(0, 0, strokeCanvas.width, strokeCanvas.height);
      }
    }
  };

  document.addEventListener('pointerup', endStroke, true);
  document.addEventListener('pointercancel', endStroke, true);

  // 3. Перехват контекста рисования: во время движения пишем в 100% альфе на временный буфер,
  // чтобы штрих не темнел сам на себе при пересечении линий в одном клике.
  const originalStroke = CanvasRenderingContext2D.prototype.stroke;
  CanvasRenderingContext2D.prototype.stroke = function () {
    if (window.transparencyBrushEnabled && this.canvas && this.canvas.id === 'canvas' && isDrawingLayer) {
      // Рисуем на вспомогательном буфере
      strokeCtx.lineWidth = this.lineWidth;
      strokeCtx.lineCap = this.lineCap;
      strokeCtx.lineJoin = this.lineJoin;
      strokeCtx.strokeStyle = this.strokeStyle;
      strokeCtx.stroke(this.currentPath || arguments[0]);
    }
    return originalStroke.apply(this, arguments);
  };

  // 4. Панель управления UI
  function injectNativeUI() {
    if (document.getElementById('tm-transparency-control')) return;

    const drawcontrols = document.getElementById('drawcontrols');
    if (!drawcontrols) return;

    const widthToggle = drawcontrols.querySelector('.drawcontrols-widthtoggle');
    if (!widthToggle) return;

    const control = document.createElement('div');
    control.id = 'tm-transparency-control';
    control.className = 'drawcontrols-button drawcontrols-sep';
    control.style.cssText = `
      width: 175px;
      padding: 0 8px;
      position: relative;
      display: flex;
      align-items: center;
      gap: 6px;
      background: rgba(0,0,0,0.05);
      border-radius: 4px;
    `;
    control.title = 'Gartic Layered Transparency';

    control.innerHTML = `
      <span id="tm-tb-val" style="font-size: 11px; font-weight: bold; min-width: 32px; color: #333;">50%</span>
      <input type="range" id="tm-tb-opacity-range"
             min="5" max="100" step="5" value="50"
             style="flex: 1; height: 5px; cursor: pointer; accent-color: #007bff;"
             title="Прозрачность">

      <div id="tm-tb-toggle-btn" style="
        width: 28px;
        height: 18px;
        font-size: 10px;
        line-height: 18px;
        text-align: center;
        border-radius: 3px;
        background: #28a745;
        color: #fff;
        cursor: pointer;
        user-select: none;
        font-weight: bold;
        box-shadow: 0 1px 2px rgba(0,0,0,0.2);
      " title="Включить / Выключить">ON</div>
    `;

    widthToggle.parentNode.insertBefore(control, widthToggle.nextSibling);

    const toggleBtn = document.getElementById('tm-tb-toggle-btn');
    const range = document.getElementById('tm-tb-opacity-range');
    const valText = document.getElementById('tm-tb-val');

    function updateVisual() {
      const pct = Math.round(window.transparencyBrushOpacity * 100);
      range.value = pct;
      valText.textContent = `${pct}%`;
      if (window.transparencyBrushEnabled) {
        toggleBtn.textContent = 'ON';
        toggleBtn.style.background = '#28a745';
      } else {
        toggleBtn.textContent = 'OFF';
        toggleBtn.style.background = '#6c757d';
      }
    }

    toggleBtn.addEventListener('click', (e) => {
      e.stopPropagation();
      window.transparencyBrushEnabled = !window.transparencyBrushEnabled;
      updateVisual();
    });

    range.addEventListener('input', (e) => {
      window.transparencyBrushOpacity = (parseInt(e.target.value, 10) || 50) / 100;
      updateVisual();
    });

    range.addEventListener('mousedown', e => e.stopPropagation());
    range.addEventListener('pointerdown', e => e.stopPropagation());

    updateVisual();
  }

  const observer = new MutationObserver(() => {
    if (document.getElementById('drawcontrols') && !document.getElementById('tm-transparency-control')) {
      injectNativeUI();
    }
  });
  observer.observe(document.documentElement, { childList: true, subtree: true });

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => setTimeout(injectNativeUI, 800));
  } else {
    setTimeout(injectNativeUI, 800);
  }
})();