Drawaria Pixel Converter Mod

Converts the entire Drawaria page, including all elements, to a pixelated style, with a draggable UI and separate enable/disable buttons.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Drawaria Pixel Converter Mod
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Converts the entire Drawaria page, including all elements, to a pixelated style, with a draggable UI and separate enable/disable buttons.
// @author       YouTubeDrawaria
// @match        https://drawaria.online/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// @grant        none
// @license      MIT
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    let pixelSize = 8;
    let isPixelated = false;
    let observer = null;
    let uiContainer = null; // Store the UI container

    function pixelateElement(element) {
        if (!element) return;

        // Check if element is a canvas or an image
        if (element.tagName === 'CANVAS' || element.tagName === 'IMG') {
            pixelateImage(element);

        } else {

            // Recursively pixelate children
            for (const child of element.children) {
                pixelateElement(child);
            }
        }

        //Pixelate text, but only if not already handled by a parent or child element
        if (element.textContent && element.children.length === 0) {
            pixelateText(element);
        }

        //Pixelate background images
        pixelateBackgroundImage(element);

        //pixelate border images
        pixelateBorderImage(element);

    }


    function pixelateImage(element) {
      if (!element || !element.width || !element.height) return;

        const canvas = document.createElement('canvas');
        canvas.width = element.width;
        canvas.height = element.height;
        const ctx = canvas.getContext('2d');
        ctx.imageSmoothingEnabled = false;

        // Draw the original element onto the canvas
        if (element.tagName === "CANVAS"){
          ctx.drawImage(element, 0, 0, element.width, element.height);
        } else if (element.tagName === "IMG" && element.src) {
            //for images, we need to be sure they loaded before drawing
            if (!element.complete) {
              element.onload = () => pixelateImage(element)
              return;
            }
            ctx.drawImage(element, 0, 0, element.width, element.height);
        } else {
          return;
        }

        const pixelatedCanvas = pixelateCanvasData(canvas, ctx);

        if (element.tagName === "CANVAS"){
           const elementCtx = element.getContext('2d');
           elementCtx.clearRect(0, 0, element.width, element.height);
           elementCtx.drawImage(pixelatedCanvas, 0, 0, element.width, element.height);
        }
        else if(element.tagName === "IMG"){
           element.src = pixelatedCanvas.toDataURL(); // Replace image src with pixelated data
        }
    }


    function pixelateCanvasData(canvas, ctx) {
        const width = canvas.width;
        const height = canvas.height;

        const pixelatedCanvas = document.createElement('canvas');
        pixelatedCanvas.width = width;
        pixelatedCanvas.height = height;
        const ctxPixelated = pixelatedCanvas.getContext('2d');
        ctxPixelated.imageSmoothingEnabled = false;


        for (let y = 0; y < height; y += pixelSize) {
            for (let x = 0; x < width; x += pixelSize) {
                const pixelData = ctx.getImageData(x, y, 1, 1).data;
                const color = `rgba(${pixelData[0]}, ${pixelData[1]}, ${pixelData[2]}, ${pixelData[3] / 255})`;
                ctxPixelated.fillStyle = color;
                ctxPixelated.fillRect(x, y, pixelSize, pixelSize);
            }
        }
        return pixelatedCanvas;

    }

    function pixelateText(element){
      element.style.font = `bold ${pixelSize}px monospace`; // Use a monospace font for consistent character width
    }


    function pixelateBackgroundImage(element) {
        let originalBackgroundImage = window.getComputedStyle(element).backgroundImage;
        if (originalBackgroundImage && originalBackgroundImage !== 'none') {
            // Extract URL from 'url("...")'
            const imageUrlMatch = originalBackgroundImage.match(/url\("?(.+?)"?\)/);

            if (imageUrlMatch) {
                const imageUrl = imageUrlMatch[1];
                const img = new Image();
                img.src = imageUrl; // Set the image source

                img.onload = () => {
                  const tempCanvas = document.createElement('canvas');
                  tempCanvas.width = img.width;
                  tempCanvas.height = img.height;
                  const tempCtx = tempCanvas.getContext('2d');
                  tempCtx.drawImage(img, 0, 0);

                  const pixelatedCanvas = pixelateCanvasData(tempCanvas, tempCtx);

                  element.style.backgroundImage = `url(${pixelatedCanvas.toDataURL()})`;
                  element.style.backgroundSize = `${img.width}px ${img.height}px`; //Keep original background image size

                };
                 img.onerror = () => {
                    console.error("Error loading background image:", imageUrl);
                };
            }
        }
    }

     function pixelateBorderImage(element){
        let originalBorderImage = window.getComputedStyle(element).borderImageSource;

        if (originalBorderImage && originalBorderImage !== 'none') {
             // Extract URL from 'url("...")'
            const imageUrlMatch = originalBorderImage.match(/url\("?(.+?)"?\)/);
            if(imageUrlMatch){
              const imageUrl = imageUrlMatch[1];
              const img = new Image();
              img.crossOrigin = "anonymous"; // Attempt to avoid CORS issues
              img.src = imageUrl;
              img.onload = () => {
                const tempCanvas = document.createElement('canvas');
                tempCanvas.width = img.width;
                tempCanvas.height = img.height;
                const tempCtx = tempCanvas.getContext('2d');
                tempCtx.drawImage(img, 0, 0);

                const pixelatedCanvas = pixelateCanvasData(tempCanvas, tempCtx);
                element.style.borderImageSource = `url(${pixelatedCanvas.toDataURL()})`;

              }
               img.onerror = () => {
                    console.error("Error loading border image:", imageUrl);
                };
            }

        }
    }

    function applyPixelation() {
        if (isPixelated) {
            pixelateElement(document.body);
        }
    }


    function enablePixelation() {
      if(!isPixelated){
        isPixelated = true;
        applyPixelation();
        startObserving();
      }
    }

    function disablePixelation(){
      if(isPixelated){
        isPixelated = false;
        stopObserving();
        location.reload(); //refresh page
      }
    }


    function createUI() {
      uiContainer = document.createElement('div');
      uiContainer.id = "pixelationUI";
      uiContainer.style.position = 'fixed';
      uiContainer.style.top = '20px';
      uiContainer.style.right = '20px';
      uiContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; // Semi-transparent black
      uiContainer.style.color = 'white';
      uiContainer.style.padding = '15px';
      uiContainer.style.borderRadius = '10px';
      uiContainer.style.zIndex = '10000';
      uiContainer.style.fontFamily = 'sans-serif';
      uiContainer.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.5)';
      uiContainer.style.userSelect = 'none'; // Prevent text selection
      uiContainer.style.cursor = 'move';   // Indicate draggability

      // --- Title Bar (for dragging) ---
      const titleBar = document.createElement('div');
      titleBar.style.cursor = 'move';
      titleBar.style.fontWeight = 'bold';
      titleBar.style.marginBottom = '10px';
      titleBar.style.borderBottom = '1px solid rgba(255, 255, 255, 0.3)';
      titleBar.style.paddingBottom = '5px';
      titleBar.textContent = 'Pixelator Controls';
      uiContainer.appendChild(titleBar);

        // --- Button container ---
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex'; // Arrange buttons horizontally
        buttonContainer.style.gap = '10px';     // Spacing between buttons
        buttonContainer.style.marginBottom = '10px';
        uiContainer.appendChild(buttonContainer);

      // --- Enable Button ---
        const enableButton = document.createElement('button');
        enableButton.id = "pixelationEnableButton";
        enableButton.textContent = 'Enable Pixelation';
        enableButton.style.backgroundColor = '#4CAF50'; // Green
        enableButton.style.color = 'white';
        enableButton.style.border = 'none';
        enableButton.style.padding = '8px 16px';
        enableButton.style.borderRadius = '5px';
        enableButton.style.cursor = 'pointer';
        enableButton.style.flex = '1'; // Equal width
        enableButton.addEventListener('click', enablePixelation);
        buttonContainer.appendChild(enableButton); // Add to button container

      // --- Disable Button ---
        const disableButton = document.createElement('button');
        disableButton.id = "pixelationDisableButton";
        disableButton.textContent = 'Disable Pixelation';
        disableButton.style.backgroundColor = '#f44336'; // Red
        disableButton.style.color = 'white';
        disableButton.style.border = 'none';
        disableButton.style.padding = '8px 16px';
        disableButton.style.borderRadius = '5px';
        disableButton.style.cursor = 'pointer';
        disableButton.style.flex = '1';  // Equal width
        disableButton.addEventListener('click', disablePixelation);
        buttonContainer.appendChild(disableButton); // Add to button container


      // --- Pixel Size Input ---
      const pixelSizeContainer = document.createElement('div');
      pixelSizeContainer.style.marginBottom = '10px';
      pixelSizeContainer.style.display = 'flex';          // Use flexbox
      pixelSizeContainer.style.alignItems = 'center';    // Center vertically
      pixelSizeContainer.style.justifyContent = 'space-between';  // Space out

      const pixelSizeLabel = document.createElement('label');
      pixelSizeLabel.textContent = 'Pixel Size:';
      pixelSizeLabel.style.marginRight = '10px';
      pixelSizeContainer.appendChild(pixelSizeLabel);

      const pixelSizeInput = document.createElement('input');
      pixelSizeInput.type = 'number';
      pixelSizeInput.value = pixelSize;
      pixelSizeInput.min = '1';
      pixelSizeInput.style.width = '60px';
      pixelSizeInput.style.padding = '5px';
      pixelSizeInput.style.borderRadius = '5px';
      pixelSizeInput.style.border = '1px solid #666';
      pixelSizeInput.style.backgroundColor = '#333';
      pixelSizeInput.style.color = 'white';
      pixelSizeInput.addEventListener('change', (event) => {
          const newValue = parseInt(event.target.value, 10);
          if (!isNaN(newValue) && newValue > 0) {
              pixelSize = newValue;
              applyPixelation();
          }
      });
      pixelSizeContainer.appendChild(pixelSizeInput);
      uiContainer.appendChild(pixelSizeContainer);


      // --- Close Button ---
      const closeButton = document.createElement('button');
      closeButton.textContent = '×';
      closeButton.style.position = 'absolute';
      closeButton.style.top = '5px';
      closeButton.style.right = '5px';
      closeButton.style.backgroundColor = 'transparent';
      closeButton.style.color = 'white';
      closeButton.style.border = 'none';
      closeButton.style.fontSize = '18px';
      closeButton.style.cursor = 'pointer';
      closeButton.addEventListener('click', () => {
          uiContainer.remove(); // Remove the whole UI
          stopObserving(); //stop observing to prevent errors when pixelating
      });
      uiContainer.appendChild(closeButton);

      document.body.appendChild(uiContainer);
      makeDraggable(uiContainer);
    }

    function makeDraggable(element) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        // Use the title bar as the drag handle
        const dragHandle = element.querySelector('div'); // Get the first div (title bar)
        dragHandle.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            // Get the mouse cursor position at startup:
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            // Call a function whenever the cursor moves:
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            // Calculate the new cursor position:
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            // Set the element's new position:
            element.style.top = (element.offsetTop - pos2) + "px";
            element.style.left = (element.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            // Stop moving when mouse button is released:
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }



    function startObserving() {
        if (observer) return;

        observer = new MutationObserver(mutations => {
            applyPixelation();
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            characterData: true,
        });
    }

    function stopObserving() {
        if (observer) {
            observer.disconnect();
            observer = null;
        }
    }


    createUI();

})();