您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds pixel selection, copy, paste, and delete tool to Drawaria.online (client-side only).
// ==UserScript== // @name Drawaria.online Selection Tool // @namespace http://tampermonkey.net/ // @version 1.0 // @description Adds pixel selection, copy, paste, and delete tool to Drawaria.online (client-side only). // @author YouTubeDrawaria // @match https://drawaria.online/* // @grant GM_addStyle // @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online // @run-at document-idle // @license MIT // ==/UserScript== (function() { 'use_strict'; let gameCanvas = null; let ctx = null; let overlayCanvas = null; let overlayCtx = null; let isSelecting = false; let selectionActive = false; let startX, startY, currentX, currentY; let selectionRect = { x: 0, y: 0, w: 0, h: 0 }; let copiedImageData = null; let currentToolIsSelect = false; let selectionContextMenu = null; let selectToolButtonElement = null; GM_addStyle(` #selectionToolButton { cursor: pointer; padding: 5px; margin: 2px; border: 1px solid #ccc; background-color: #f0f0f0; display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; /* Ajustar a los botones de Drawaria */ box-sizing: border-box; font-size: 20px; user-select: none; } #selectionToolButton.active { background-color: #a0e0a0 !important; border-color: #508050 !important; } #selectionContextMenu { position: fixed; /* Usar fixed para posicionar relativo al viewport */ display: none; background: white; border: 1px solid #ccc; box-shadow: 2px 2px 5px rgba(0,0,0,0.2); padding: 8px; z-index: 10001; font-size: 14px; } #selectionContextMenu button { display: block; width: 100%; margin-bottom: 5px; padding: 5px; border: 1px solid #ddd; background-color: #f8f8f8; cursor: pointer; text-align: left; } #selectionContextMenu button:hover { background-color: #e8e8e8; } #selectionContextMenu button:disabled { color: #aaa; cursor: default; background-color: #f0f0f0; } `); function updateOverlayPositionAndSize() { if (!gameCanvas || !overlayCanvas) return; const rect = gameCanvas.getBoundingClientRect(); overlayCanvas.style.position = 'fixed'; // Usar fixed para que coincida con getBoundingClientRect overlayCanvas.style.left = rect.left + 'px'; overlayCanvas.style.top = rect.top + 'px'; overlayCanvas.style.width = rect.width + 'px'; overlayCanvas.style.height = rect.height + 'px'; // Sincronizar el tamaño del canvas interno con el tamaño renderizado del gameCanvas // Esto es importante si el gameCanvas es escalado por CSS. overlayCanvas.width = gameCanvas.offsetWidth; overlayCanvas.height = gameCanvas.offsetHeight; if (overlayCtx) { // Limpiar si el contexto ya existe overlayCtx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); if (selectionActive && selectionRect.w > 0 && selectionRect.h > 0) { // Redibujar la selección si está activa, ya que el canvas se recreó/redimensionó overlayCtx.strokeStyle = 'rgba(0, 0, 0, 0.8)'; overlayCtx.lineWidth = 1; overlayCtx.setLineDash([3, 3]); overlayCtx.strokeRect(selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h); } } } function initializeTool() { gameCanvas = document.getElementById('canvas'); const controlsParent = document.getElementById('drawcontrols'); if (!gameCanvas || !controlsParent) return false; if (!ctx) ctx = gameCanvas.getContext('2d', { willReadFrequently: true }); if (!overlayCanvas) { overlayCanvas = document.createElement('canvas'); overlayCanvas.id = 'selectionOverlay'; overlayCanvas.style.pointerEvents = 'none'; overlayCanvas.style.zIndex = '3'; // Encima del gameCanvas document.body.appendChild(overlayCanvas); overlayCtx = overlayCanvas.getContext('2d'); setupMouseListeners(); } updateOverlayPositionAndSize(); if (!selectionContextMenu) setupContextMenu(); if (!addSelectButton(controlsParent)) return false; window.addEventListener('resize', updateOverlayPositionAndSize); // Escuchar scroll en el contenedor del canvas si es relevante (Drawaria no parece tenerlo a nivel de canvas) // gameCanvas.parentElement.addEventListener('scroll', updateOverlayPositionAndSize); return true; } function addSelectButton(controlsParent) { let controlsContainer = controlsParent.querySelector('.tools-left'); // Contenedor específico if (!controlsContainer) { // Fallback más general si el anterior no se encuentra const groups = controlsParent.querySelectorAll('div[data-ctrlgroup]'); if (groups.length > 0) { controlsContainer = groups[0]; // Añadir al primer grupo de herramientas } else { controlsContainer = controlsParent; // Último fallback } } if (selectToolButtonElement && selectToolButtonElement.parentElement === controlsContainer) { return true; } if (selectToolButtonElement && selectToolButtonElement.parentElement) { selectToolButtonElement.parentElement.removeChild(selectToolButtonElement); // Quitar del lugar antiguo si existe } if (!selectToolButtonElement) { selectToolButtonElement = document.createElement('div'); selectToolButtonElement.innerHTML = '✂️'; selectToolButtonElement.title = 'Herramienta de Selección'; selectToolButtonElement.id = 'selectionToolButton'; selectToolButtonElement.onclick = toggleSelectTool; // Función separada para el toggle } controlsContainer.appendChild(selectToolButtonElement); return true; } function toggleSelectTool() { currentToolIsSelect = !currentToolIsSelect; if (currentToolIsSelect) { selectToolButtonElement.classList.add('active'); if (overlayCanvas) overlayCanvas.style.pointerEvents = 'auto'; document.addEventListener('mousedown', handleDocumentMouseDown, true); // Captura para ejecutarse primero } else { selectToolButtonElement.classList.remove('active'); if (overlayCanvas) overlayCanvas.style.pointerEvents = 'none'; clearSelectionRectangle(); if(selectionContextMenu) selectionContextMenu.style.display = 'none'; document.removeEventListener('mousedown', handleDocumentMouseDown, true); } console.log("Herramienta de selección activa:", currentToolIsSelect); } function handleDocumentMouseDown(e) { if (!currentToolIsSelect) return; // Si el clic es en el botón de selección, overlay, o menú, no hacer nada aquí if (e.target === selectToolButtonElement || e.target === overlayCanvas || (selectionContextMenu && selectionContextMenu.contains(e.target))) { return; } // Si el clic es en otra parte (p.ej. otra herramienta del juego), desactivar la selección. console.log("Document click detected, deselecting tool."); toggleSelectTool(); // Esto cambiará currentToolIsSelect a false y limpiará } function setupMouseListeners() { // ... (igual que en la versión 0.3, asegurándose de usar overlayCanvas.getBoundingClientRect() para las coords) const oldOverlay = overlayCanvas; overlayCanvas = oldOverlay.cloneNode(true); oldOverlay.parentNode.replaceChild(overlayCanvas, oldOverlay); overlayCtx = overlayCanvas.getContext('2d'); overlayCanvas.addEventListener('mousedown', function(e) { if (!currentToolIsSelect) return; if (selectionContextMenu && selectionContextMenu.contains(e.target)) return; const rect = overlayCanvas.getBoundingClientRect(); startX = e.clientX - rect.left; startY = e.clientY - rect.top; isSelecting = true; selectionActive = false; // Una nueva selección se está iniciando // No limpiar copiedImageData aquí, para permitir pegar después de una nueva selección (si no se copió nada nuevo) if (selectionContextMenu) selectionContextMenu.style.display = 'none'; }); overlayCanvas.addEventListener('mousemove', function(e) { if (!currentToolIsSelect || !isSelecting) return; const rect = overlayCanvas.getBoundingClientRect(); currentX = e.clientX - rect.left; currentY = e.clientY - rect.top; drawSelectionRectangle(); }); overlayCanvas.addEventListener('mouseup', function(e) { if (!currentToolIsSelect || !isSelecting) return; isSelecting = false; // Definir el rectángulo de selección final selectionRect.x = Math.min(startX, currentX); selectionRect.y = Math.min(startY, currentY); selectionRect.w = Math.abs(currentX - startX); selectionRect.h = Math.abs(currentY - startY); if (selectionRect.w < 2 || selectionRect.h < 2) { // Ignorar selecciones muy pequeñas clearSelectionRectangle(); // Esto pone selectionActive = false } else { selectionActive = true; // Solo activar si la selección es válida console.log("Selección hecha:", selectionRect); } }); overlayCanvas.addEventListener('contextmenu', function(e) { if (!currentToolIsSelect || !selectionActive || !selectionRect || selectionRect.w === 0 || selectionRect.h === 0) { if(selectionContextMenu) selectionContextMenu.style.display = 'none'; return; } e.preventDefault(); if (!selectionContextMenu) setupContextMenu(); selectionContextMenu.style.left = e.clientX + 'px'; selectionContextMenu.style.top = e.clientY + 'px'; selectionContextMenu.style.display = 'block'; document.getElementById('ctxPaste').disabled = !copiedImageData; }); } function drawSelectionRectangle() { // ... (igual que en la versión 0.3) if (!overlayCtx || !overlayCanvas) return; overlayCtx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); overlayCtx.strokeStyle = 'rgba(0, 0, 0, 0.8)'; overlayCtx.lineWidth = 1; // Línea delgada para más precisión visual overlayCtx.setLineDash([2, 2]); // Puntos más pequeños overlayCtx.strokeRect(startX, startY, currentX - startX, currentY - startY); } function clearSelectionRectangle() { // ... (igual que en la versión 0.3) if (!overlayCtx || !overlayCanvas) return; overlayCtx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); selectionActive = false; selectionRect = { x: 0, y: 0, w: 0, h: 0 }; } function setupContextMenu() { // ... (igual que en la versión 0.3, pero asegurar posicionamiento 'fixed') if (document.getElementById('selectionContextMenu')) return; selectionContextMenu = document.createElement('div'); selectionContextMenu.id = 'selectionContextMenu'; // Estilos ahora son GM_addStyle selectionContextMenu.innerHTML = ` <button id="ctxCopy">Copiar</button><button id="ctxPaste" disabled>Pegar</button> <button id="ctxDelete">Eliminar</button><hr style="margin: 5px 0;"> <button id="ctxClearSel">Limpiar Selección</button>`; document.body.appendChild(selectionContextMenu); document.getElementById('ctxCopy').onclick = function() { if (selectionActive && selectionRect.w > 0 && selectionRect.h > 0 && ctx) { try { copiedImageData = ctx.getImageData(selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h); } catch (err) { console.error("Error copying:", err); alert("Error al copiar: " + err.message); copiedImageData = null; } } selectionContextMenu.style.display = 'none'; }; document.getElementById('ctxPaste').onclick = function() { if (copiedImageData && overlayCanvas && ctx) { alert("Haz clic en el canvas donde quieras pegar."); overlayCanvas.style.pointerEvents = 'auto'; overlayCanvas.style.cursor = 'crosshair'; const pasteHandler = function(e) { const rect = overlayCanvas.getBoundingClientRect(); const pasteX = e.clientX - rect.left - copiedImageData.width / 2; const pasteY = e.clientY - rect.top - copiedImageData.height / 2; ctx.putImageData(copiedImageData, pasteX, pasteY); overlayCanvas.removeEventListener('click', pasteHandler); overlayCanvas.style.cursor = ''; overlayCanvas.style.pointerEvents = currentToolIsSelect ? 'auto' : 'none'; }; overlayCanvas.addEventListener('click', pasteHandler, { once: true }); } selectionContextMenu.style.display = 'none'; }; document.getElementById('ctxDelete').onclick = function() { if (selectionActive && selectionRect.w > 0 && selectionRect.h > 0 && ctx) { ctx.fillStyle = 'white'; ctx.fillRect(selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h); clearSelectionRectangle(); } selectionContextMenu.style.display = 'none'; }; document.getElementById('ctxClearSel').onclick = function() { clearSelectionRectangle(); selectionContextMenu.style.display = 'none'; }; document.addEventListener('mousedown', function(e) { // Cambiado a mousedown if (selectionContextMenu && selectionContextMenu.style.display === 'block' && !selectionContextMenu.contains(e.target) && e.target !== overlayCanvas) { selectionContextMenu.style.display = 'none'; } }, true); // Usar captura } // --- Lógica de inicialización --- let initializationAttempts = 0; const MAX_INIT_ATTEMPTS = 20; // Más intentos let toolFullyInitialized = false; const observer = new MutationObserver((mutationsList, obs) => { if (!toolFullyInitialized) { toolFullyInitialized = initializeTool(); } else { updateOverlayPositionAndSize(); // Mantener overlay alineado const controlsParent = document.getElementById('drawcontrols'); if (controlsParent && selectToolButtonElement && !selectToolButtonElement.parentElement) { // Si el botón fue removido del DOM por alguna razón, intentar re-añadirlo addSelectButton(controlsParent); } else if (controlsParent && selectToolButtonElement && selectToolButtonElement.parentElement.id !== controlsParent.id && !controlsParent.contains(selectToolButtonElement)) { // Si el padre cambió o ya no lo contiene, intentar re-añadirlo addSelectButton(controlsParent); } } }); observer.observe(document.body, { childList: true, subtree: true }); function delayedInitializeCheck() { if (toolFullyInitialized || initializationAttempts >= MAX_INIT_ATTEMPTS) { if (!toolFullyInitialized) console.error("Drawaria Tool: Failed to init."); return; } initializationAttempts++; toolFullyInitialized = initializeTool(); if (!toolFullyInitialized) setTimeout(delayedInitializeCheck, 500); } setTimeout(delayedInitializeCheck, 500); // Reducido el primer delay })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址