r/"What is My Cookie Cutter" Drawing Software!

Yes! Allows for quick drawing on r/whatismycookiecuutter ! There isn't even a need to exit Reddit! So much easier than having to load an entire drawing program, or trying to download it to your computer!

// ==UserScript==
// @name     r/"What is My Cookie Cutter" Drawing Software!
// @namespace https://gf.qytechs.cn/en/scripts/486930-r-what-is-my-cookie-cutter-drawing-software/code
// @version   1.0
// @description Yes! Allows for quick drawing on r/whatismycookiecuutter ! There isn't even a need to exit Reddit! So much easier than having to load an entire drawing program, or trying to download it to your computer!
// @author    Heptatron
// @license MIT
// @match     https://www.reddit.com/*
// @match     https://i.redd.it/*
// @grant    GM_setValue
// @grant    GM_getValue
// ==/UserScript==
 
(function() {
  'use strict';
 
  let canvas, ctx;
  let isDrawing = false;
  let lastX = 0;
  let lastY = 0;
  let history = [];
  let historyIndex = -1;
  let showControls = false;
  let colorHistory = [];
 
  // Default settings
  let settings = {
    color: 'black',
    lineWidth: 2,
    eraser: false,
    circleDistance: 2,
    opacity: 1,
    softness: 5,
    shadow: false,
    touchscreenMode: false
  };
 
  function createCanvas() {
    canvas = document.createElement('canvas');
    canvas.style.position = 'fixed';
    canvas.style.top = '0';
    canvas.style.left = '0';
    canvas.style.pointerEvents = 'none'; // To allow clicking through canvas
    canvas.style.zIndex = '9999'; // Canvas is on top
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    document.body.appendChild(canvas);
    ctx = canvas.getContext('2d');
  }
 
  function createOverlay() {
    const overlay = document.createElement('div');
    overlay.id = 'overlay';
    overlay.style.position = 'fixed';
    overlay.style.top = '0';
    overlay.style.left = '0';
    overlay.style.width = '100%';
    overlay.style.height = '100%';
    overlay.style.backgroundColor = 'rgba(0, 0, 0, 0)';
    overlay.style.zIndex = '9998';
    overlay.style.display = 'none';
    document.body.appendChild(overlay);
  }
 
  function showOverlay() {
    const overlay = document.getElementById('overlay');
    overlay.style.display = 'block';
  }
 
  function hideOverlay() {
    const overlay = document.getElementById('overlay');
    overlay.style.display = 'none';
  }
 
  function startDrawing(e) {
    if ((settings.touchscreenMode || e.ctrlKey) && (e.button === 0 || e.button === 2)) {
      isDrawing = true;
      [lastX, lastY] = [e.clientX, e.clientY];
      if (!settings.eraser) {
        addToHistory();
      }
    }
  }
 
  function draw(e) {
    if (!isDrawing) return;
    if (settings.eraser) {
        ctx.globalCompositeOperation = 'destination-out';
    } else {
        ctx.globalCompositeOperation = 'source-over';
    }
    const deltaX = e.clientX - lastX;
    const deltaY = e.clientY - lastY;
    const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    const angle = Math.atan2(deltaY, deltaX);
 
    for (let i = 0; i < distance; i += settings.circleDistance) {
        const x = lastX + Math.cos(angle) * i;
        const y = lastY + Math.sin(angle) * i;
        ctx.beginPath();
        ctx.arc(x, y, settings.lineWidth / 2, 0, Math.PI * 2);
        ctx.fillStyle = settings.color;
        ctx.globalAlpha = settings.opacity;
 
        if (settings.shadow) {
            ctx.shadowColor = settings.color;
            ctx.shadowBlur = settings.lineWidth / 2;
        } else {
            ctx.shadowColor = 'transparent';
            ctx.shadowBlur = 0;
        }
 
        ctx.fill();
    }
 
    [lastX, lastY] = [e.clientX, e.clientY];
  }
 
  function stopDrawing() {
    isDrawing = false;
  }
 
  function init() {
    createCanvas();
    createOverlay();
    document.addEventListener('mousedown', startDrawing);
    document.addEventListener('mousemove', draw);
    document.addEventListener('mouseup', stopDrawing);
    createSettingsMenu();
    createButtons();
    createDraggableCircle(); // Add draggable circle
    document.addEventListener('keydown', handleKeyPress);
    document.addEventListener('keydown', toggleControls);
    document.addEventListener('keyup', toggleControls);
    loadSettings();
    loadColorHistory();
  }
 
  function createSettingsMenu() {
    const settingsMenu = document.createElement('div');
    settingsMenu.id = 'settingsMenu';
    settingsMenu.style.display = 'none';
    settingsMenu.style.position = 'fixed';
    settingsMenu.style.top = '10px';
    settingsMenu.style.left = '10px';
    settingsMenu.style.backgroundColor = 'rgba(77, 77, 77, 0.7)';
    settingsMenu.style.padding = '10px';
    settingsMenu.style.borderRadius = '5px';
    settingsMenu.style.zIndex = '10000';
    settingsMenu.innerHTML = `
      <h3>Settings</h3>
      <label for="color">Color:</label>
      <input type="color" id="color" value="${settings.color}">
      <button id="saveColor">Save Color</button>
      <br>
      <div id="colorHistoryContainer"></div>
      <button id="clearColorHistory">Clear Color History</button>
      <br>
      <label for="lineWidth">Line Width:</label>
      <input type="range" id="lineWidth" min="0.5" max="100" step="0.5" value="${settings.lineWidth}">
      <br>
      <label for="circleDistance">Circle Distance:</label>
      <input type="number" id="circleDistance" min="1" value="${settings.circleDistance}">
      <br>
      <label for="eraser">Eraser:</label>
      <input type="checkbox" id="eraser">
      <br>
      <label for="opacity">Opacity:</label>
      <input type="range" id="opacity" min="0" max=".5" step="0.01" value="${settings.opacity}">
      <br>
      <label for="softness">Softness:</label>
      <input type="number" id="softness" min="1" value="${settings.softness}">
      <br>
      <label for="shadow">Shadow:</label>
      <input type="checkbox" id="shadow">
      <br>
      <label for="touchscreenMode">Touchscreen Mode:</label> <!-- Updated setting name -->
      <input type="checkbox" id="touchscreenMode"> <!-- Updated setting name -->
    `;
    document.body.appendChild(settingsMenu);
 
    const colorInput = document.getElementById('color');
    colorInput.addEventListener('input', function() {
      settings.color = colorInput.value;
      GM_setValue('color', settings.color);
    });
 
    const lineWidthInput = document.getElementById('lineWidth');
    lineWidthInput.addEventListener('input', function() {
      settings.lineWidth = parseFloat(lineWidthInput.value);
      GM_setValue('lineWidth', settings.lineWidth);
    });
 
    const circleDistanceInput = document.getElementById('circleDistance');
    circleDistanceInput.addEventListener('input', function() {
      settings.circleDistance = parseInt(circleDistanceInput.value);
      GM_setValue('circleDistance', settings.circleDistance);
    });
 
    const eraserCheckbox = document.getElementById('eraser');
    eraserCheckbox.checked = settings.eraser;
    eraserCheckbox.addEventListener('change', function() {
      settings.eraser = eraserCheckbox.checked;
      GM_setValue('eraser', settings.eraser);
    });
 
    const opacityInput = document.getElementById('opacity');
    opacityInput.addEventListener('input', function() {
      settings.opacity = parseFloat(opacityInput.value);
      GM_setValue('opacity', settings.opacity);
      ctx.globalAlpha = settings.opacity;
    });
 
    const softnessInput = document.getElementById('softness');
    softnessInput.addEventListener('input', function() {
      settings.softness = parseInt(softnessInput.value);
      GM_setValue('softness', settings.softness);
    });
 
    const shadowCheckbox = document.getElementById('shadow');
    shadowCheckbox.checked = settings.shadow;
    shadowCheckbox.addEventListener('change', function() {
      settings.shadow = shadowCheckbox.checked;
      GM_setValue('shadow', settings.shadow);
    });
 
    const touchscreenModeCheckbox = document.getElementById('touchscreenMode');
    touchscreenModeCheckbox.checked = settings.touchscreenMode;
    touchscreenModeCheckbox.addEventListener('change', function() {
      settings.touchscreenMode = touchscreenModeCheckbox.checked;
      GM_setValue('touchscreenMode', settings.touchscreenMode);
    });
 
    const saveColorButton = document.getElementById('saveColor');
    saveColorButton.addEventListener('click', saveColor);
 
    const clearColorHistoryButton = document.getElementById('clearColorHistory');
    clearColorHistoryButton.addEventListener('click', clearColorHistory);
  }
 
  function createButtons() {
    const buttonsContainer = document.createElement('div');
    buttonsContainer.id = 'buttonsContainer';
    buttonsContainer.style.display = 'none';
    buttonsContainer.style.position = 'fixed';
    buttonsContainer.style.top = '10px';
    buttonsContainer.style.right = '10px';
    buttonsContainer.style.zIndex = '10000';
 
    const undoButton = document.createElement('button');
    undoButton.textContent = 'Undo (Ctrl + Z)';
    undoButton.addEventListener('click', undo);
    buttonsContainer.appendChild(undoButton);
 
    const redoButton = document.createElement('button');
    redoButton.textContent = 'Redo (Ctrl + Shift + Z)';
    redoButton.addEventListener('click', redo);
    buttonsContainer.appendChild(redoButton);
 
    const clearButton = document.createElement('button');
    clearButton.textContent = 'Clear All';
    clearButton.addEventListener('click', clearCanvas);
    buttonsContainer.appendChild(clearButton);
 
    document.body.appendChild(buttonsContainer);
  }
 
  function createDraggableCircle() {
    const circle = document.createElement('div');
    circle.id = 'draggableCircle';
    circle.style.position = 'fixed';
    circle.style.width = '20px';
    circle.style.height = '20px';
    circle.style.borderRadius = '50%';
    circle.style.backgroundColor = '#888';
    circle.style.cursor = 'move';
    circle.style.zIndex = '10000';
    circle.style.left = '10px';
    circle.style.top = '10px';
    document.body.appendChild(circle);
 
    let isDragging = false;
    let offsetX, offsetY;
 
    circle.addEventListener('mousedown', function(event) {
      if (settings.touchscreenMode) {
        isDrawing = false;
      }
      isDragging = true;
      offsetX = event.clientX - circle.getBoundingClientRect().left;
      offsetY = event.clientY - circle.getBoundingClientRect().top;
    });
 
    document.addEventListener('mousemove', function(event) {
      if (isDragging) {
        const newX = event.clientX - offsetX;
        const newY = event.clientY - offsetY;
        circle.style.left = newX + 'px';
        circle.style.top = newY + 'px';
      }
    });
 
    document.addEventListener('mouseup', function() {
      isDragging = false;
      if (settings.touchscreenMode) {
        isDrawing = false;
      }
    });
 
    circle.addEventListener('click', function() {
      toggleControlsWithTouchscreenMode();
    });
  }
 
  function toggleControlsWithTouchscreenMode() {
    const settingsMenu = document.getElementById('settingsMenu');
    const buttonsContainer = document.getElementById('buttonsContainer');
    if (!showControls) {
      settingsMenu.style.display = 'block';
      buttonsContainer.style.display = 'block';
      showControls = true;
      showOverlay();
      createColorHistoryButtons();
    } else {
      settingsMenu.style.display = 'none';
      buttonsContainer.style.display = 'none';
      showControls = false;
      hideOverlay();
      clearColorHistoryButtons();
    }
  }
 
  function handleKeyPress(e) {
    if (e.ctrlKey && e.key === 'z') {
      if (e.shiftKey) {
        redo();
      } else {
        undo();
      }
    }
  }
 
  function addToHistory() {
    if (historyIndex !== history.length - 1) {
      history = history.slice(0, historyIndex + 1);
    }
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    history.push(imageData);
    historyIndex++;
  }
 
  function undo() {
    if (historyIndex > 0) {
      historyIndex--;
      ctx.putImageData(history[historyIndex], 0, 0);
    }
  }
 
  function redo() {
    if (historyIndex < history.length - 1) {
      historyIndex++;
      ctx.putImageData(history[historyIndex], 0, 0);
    }
  }
 
  function clearCanvas() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    history = [];
    historyIndex = -1;
  }
 
  function loadSettings() {
    const savedColor = GM_getValue('color', settings.color);
    settings.color = savedColor;
    document.getElementById('color').value = savedColor;
 
    const savedLineWidth = parseFloat(GM_getValue('lineWidth', settings.lineWidth));
    settings.lineWidth = savedLineWidth;
    document.getElementById('lineWidth').value = savedLineWidth;
 
    const savedCircleDistance = parseInt(GM_getValue('circleDistance', settings.circleDistance));
    settings.circleDistance = savedCircleDistance;
    document.getElementById('circleDistance').value = savedCircleDistance;
 
    const savedEraser = GM_getValue('eraser', settings.eraser);
    settings.eraser = savedEraser;
    document.getElementById('eraser').checked = savedEraser;
 
    const savedOpacity = parseFloat(GM_getValue('opacity', settings.opacity));
    settings.opacity = savedOpacity;
    document.getElementById('opacity').value = savedOpacity;
 
    const savedSoftness = parseInt(GM_getValue('softness', settings.softness));
    settings.softness = savedSoftness;
    document.getElementById('softness').value = savedSoftness;
 
    const savedShadow = GM_getValue('shadow', settings.shadow);
    settings.shadow = savedShadow;
    document.getElementById('shadow').checked = savedShadow;
 
    const savedTouchscreenMode = GM_getValue('touchscreenMode', settings.touchscreenMode);
    settings.touchscreenMode = savedTouchscreenMode;
    document.getElementById('touchscreenMode').checked = savedTouchscreenMode;
  }
 
  function toggleControls(e) {
    if (e.ctrlKey && e.altKey) {
      toggleControlsWithTouchscreenMode();
    }
  }
 
  function createColorHistoryButtons() {
    const colorHistoryContainer = document.getElementById('colorHistoryContainer');
    colorHistoryContainer.innerHTML = '';
    let row;
    colorHistory.forEach((color, index) => {
      if (index % 10 === 0) {
        row = document.createElement('div');
        row.style.display = 'flex';
        row.style.flexWrap = 'wrap';
        colorHistoryContainer.appendChild(row);
      }
      const colorButton = document.createElement('button');
      colorButton.textContent = '';
      colorButton.style.width = '20px';
      colorButton.style.height = '20px';
      colorButton.style.backgroundColor = color;
      colorButton.style.marginRight = '5px';
      colorButton.style.marginBottom = '5px';
      colorButton.addEventListener('click', () => {
        settings.color = color;
        document.getElementById('color').value = color;
        GM_setValue('color', settings.color);
      });
      row.appendChild(colorButton);
    });
  }
 
  function clearColorHistoryButtons() {
    const colorHistoryContainer = document.getElementById('colorHistoryContainer');
    colorHistoryContainer.innerHTML = '';
  }
 
  function saveColor() {
    const colorInput = document.getElementById('color');
    const color = colorInput.value;
    if (!colorHistory.includes(color)) {
      colorHistory.push(color);
      GM_setValue('colorHistory', JSON.stringify(colorHistory));
      createColorHistoryButtons();
    }
  }
 
  function clearColorHistory() {
    colorHistory = [];
    GM_setValue('colorHistory', JSON.stringify(colorHistory));
    clearColorHistoryButtons();
  }
 
  function loadColorHistory() {
    const savedColorHistory = JSON.parse(GM_getValue('colorHistory', '[]'));
    colorHistory = savedColorHistory;
    createColorHistoryButtons();
  }
 
  init();
 
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址