// ==UserScript== // @name Drawaria.online Canvas Effects // @namespace http://tampermonkey.net/ // @version 1.0 // @description An ADVANCED effects menu for Drawaria.online Canvas, with many options and libraries! // @author YouTubeDrawaria // @match https://drawaria.online/* // @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online // @grant none // @license MIT // @require https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js // @require https://cdn.jsdelivr.net/npm/matter-js@0.19.0/build/matter.min.js // @require https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/FontLoader.js // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/527147/Drawariaonline%20Canvas%20Effects.user.js // @updateURL https://update.greasyfork.icu/scripts/527147/Drawariaonline%20Canvas%20Effects.meta.js // ==/UserScript== (function() { 'use strict'; // --- LIBRARY SETUP --- // Three.js: Scene, camera, renderer, and a container for 3D objects. let scene, camera, renderer, threeContainer; let threeObjects = []; // Array to keep track of Three.js objects // GSAP: Global timeline to control animations (optional, but useful). // const tl = gsap.timeline({ paused: true }); // A global timeline is not used in this example // Matter.js: Physics engine. let engine, world; let canvas; // Reference to the Drawaria canvas. // --- UTILITY FUNCTIONS (MODIFIED) --- function initThreeJS() { if (scene) return; // Avoid re-initializing if it already exists. if (!threeContainer) { // Check if threeContainer exists before trying to append threeContainer = document.createElement('div'); threeContainer.style.cssText = ` position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; /* VERY IMPORTANT: Allows clicks to "pass through" to the Drawaria canvas. */ z-index: 500; /* Above the Drawaria canvas, but below the menu. */ `; document.body.appendChild(threeContainer); } if (!scene) { scene = new THREE.Scene(); } if (!camera) { camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z = 5; // Adjust as needed. } if (!renderer) { renderer = new THREE.WebGLRenderer({ alpha: true }); // alpha: true is CRUCIAL for transparency. renderer.setSize(window.innerWidth, window.innerHeight); threeContainer.appendChild(renderer.domElement); } // Basic ambient light (so objects are not completely black). if (!scene.getObjectByName('ambientLight')) { // Check if ambient light already exists const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); ambientLight.name = 'ambientLight'; // Name it for easy checking scene.add(ambientLight); } // Directional light. if (!scene.getObjectByName('directionalLight')) { // Check if directional light already exists const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(1, 1, 1); // Direction of light. directionalLight.name = 'directionalLight'; // Name it scene.add(directionalLight); } // Resize Three.js when the window is resized. window.addEventListener('resize', onWindowResize, false); animateThreeJS(); // Start the rendering loop. } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function animateThreeJS() { requestAnimationFrame(animateThreeJS); // Basic rotation of *all* Three.js objects (to demonstrate it's working). threeObjects.forEach(obj => { obj.rotation.x += 0.01; obj.rotation.y += 0.01; }); renderer.render(scene, camera); } function initMatterJS() { if (engine) return; engine = Matter.Engine.create(); world = engine.world; // Gravity settings (you can modify) engine.world.gravity.y = 1; // Gravity downwards Matter.Engine.run(engine); } function getCanvas(){ if(!canvas) { canvas = document.querySelector('canvas'); } return canvas; } // --- EFFECTS (IMPLEMENTATIONS) --- function createText3D(text, options = {}) { initThreeJS(); // Make sure Three.js is initialized. const loader = new THREE.FontLoader(); loader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', (font) => { const textGeometry = new THREE.TextGeometry(text, { font: font, size: options.size || 0.5, height: options.height || 0.1, curveSegments: options.curveSegments || 12, bevelEnabled: options.bevelEnabled || false, bevelThickness: options.bevelThickness || 0.03, bevelSize: options.bevelSize || 0.02, bevelOffset: options.bevelOffset || 0, bevelSegments: options.bevelSegments || 5 }); const textMaterial = new THREE.MeshPhongMaterial({ color: options.color || 0xff0000 }); const textMesh = new THREE.Mesh(textGeometry, textMaterial); // Position, rotation, etc., using options. textMesh.position.x = options.x || 0; textMesh.position.y = options.y || 0; textMesh.position.z = options.z || 0; if (options.rotationX) textMesh.rotation.x = options.rotationX; if (options.rotationY) textMesh.rotation.y = options.rotationY; if (options.rotationZ) textMesh.rotation.z = options.rotationZ; scene.add(textMesh); threeObjects.push(textMesh); // Add to the array for global animation. // Animation with GSAP (optional, but recommended). if(options.animate){ gsap.to(textMesh.position, { duration: options.duration || 2, y: options.toY || 2, repeat: options.repeat || -1, yoyo: options.yoyo !== undefined ? options.yoyo : true, // Control yoyo with options. ease: options.ease || "power2.inOut" }); } }); } let movingTexts = []; // Array to store references to moving texts let movingGraphicsInterval; // For moving graphics animation interval let graphics3DObjects = []; // Array for 3D graphics objects function createMovingText(text, options = {}) { const textElement = document.createElement('div'); textElement.textContent = text; textElement.style.cssText = ` position: absolute; top: ${options.y || '50%'}; left: ${options.x || '50%'}; transform: translate(-50%, -50%); color: ${options.color || 'white'}; font-size: ${options.fontSize || '24px'}; pointer-events: none; /* Important so that it doesn't interfere with drawing. */ z-index: 500; `; document.body.appendChild(textElement); // Store a reference to the GSAP timeline to be able to control it later. const tl = gsap.to(textElement, { duration: options.duration || 3, x: options.toX || '+=200', // Move 200px to the right (relative). y: options.toY || '+=0', // You can move in Y as well. rotation: options.rotation || 360, repeat: options.repeat || -1, // Repeat infinitely. yoyo: options.yoyo !== undefined ? options.yoyo : true, // Control yoyo with options. ease: options.ease || "power1.inOut", onComplete: () => { if (options.removeOnComplete) { textElement.remove(); // Remove the element on completion (optional). } // Remove the reference from the array when the animation completes. movingTexts = movingTexts.filter(item => item.element !== textElement); } }); // Save the reference to the element and the timeline. movingTexts.push({ element: textElement, timeline: tl }); } function createParticleExplosion(options = {}) { initThreeJS(); // Make sure Three.js is ready. initMatterJS(); const numParticles = options.numParticles || 50; const particleSize = options.size || 0.05; const color = options.color || 0xffffff; const duration = options.duration || 2; const spread = options.spread || 1; // Controls how much particles spread out. const particles = []; for (let i = 0; i < numParticles; i++) { // Geometry and material for each particle (Three.js). const geometry = new THREE.SphereGeometry(particleSize, 8, 8); const material = new THREE.MeshBasicMaterial({ color: color }); const particle = new THREE.Mesh(geometry, material); // Initial position (center of the explosion). particle.position.set( (options.x || 0), (options.y || 0), (options.z || 0) ); scene.add(particle); threeObjects.push(particle); // For global rotation in animateThreeJS. particles.push(particle); // Matter.js const body = Matter.Bodies.circle( (options.x || 0) *100 , // Adjust the scale to the Matter.js world -(options.y || 0)* 100, // Adjust the scale to the Matter.js world particleSize * 100, {restitution: 0.6, friction: 0.1} ); // Apply an initial force (explosion). const force = (options.force || 0.5) * spread; const angle = Math.random() * Math.PI * 2; Matter.Body.applyForce(body, body.position, { x: Math.cos(angle) * force, y: Math.sin(angle) * force }); Matter.World.add(world, body); // Animation with GSAP to fade out and scale down. gsap.to(particle.scale, { duration: duration, x: 0, y: 0, z: 0, ease: "power1.out", onUpdate: () => { // Synchronize the Three.js position with Matter.js. particle.position.x = body.position.x / 100; particle.position.y = -body.position.y / 100; particle.position.z = (options.z || 0); }, onComplete: () => { // Clean up the particle from Three.js and Matter.js. scene.remove(particle); threeObjects.splice(threeObjects.indexOf(particle), 1); Matter.World.remove(world, body); } }); } } function applyFilterEffect(effectId, isActive) { const canvas = getCanvas(); //Get the canvas safely if (!canvas) return; if (isActive) { canvas.classList.add(effectId); // Add the class } else { canvas.classList.remove(effectId); // Remove the class } } // --- Placeholder Effects (Implement basic alerts or console logs for unimplemented effects) --- function applyPlaceholderEffect(effectId, isActive) { if (isActive) { console.log(`Effect ${effectId} ACTIVATED (Placeholder)`); // Or alert(`Efecto ${effectId} ACTIVATED (Placeholder)`); const canvas = getCanvas(); if(canvas) canvas.classList.add('placeholder-effect-indicator'); // Add visual indicator } else { console.log(`Effect ${effectId} DEACTIVATED (Placeholder)`); // Or alert(`Efecto ${effectId} DESACTIVADO (Placeholder)`); const canvas = getCanvas(); if(canvas) canvas.classList.remove('placeholder-effect-indicator'); // Remove visual indicator } } function createMovingGraphics(isActive) { if (isActive) { initThreeJS(); const geometry = new THREE.BoxGeometry(1, 1, 1); const material = new THREE.MeshBasicMaterial({ color: 0x00ffff }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); threeObjects.push(cube); graphics3DObjects.push(cube); // Add to separate array for graphics // Basic animation using setInterval (can be replaced with GSAP for smoother animation) movingGraphicsInterval = setInterval(() => { cube.position.x = Math.sin(Date.now() * 0.001) * 2; // Move horizontally cube.position.y = Math.cos(Date.now() * 0.001) * 1.5; // Move vertically }, 16); // Roughly 60 FPS } else { clearInterval(movingGraphicsInterval); graphics3DObjects.forEach(obj => { scene.remove(obj); threeObjects.splice(threeObjects.indexOf(obj), 1); }); graphics3DObjects = []; } } function createGraphics3D(isActive) { if (isActive) { initThreeJS(); const geometry = new THREE.SphereGeometry(1, 32, 32); const material = new THREE.MeshStandardMaterial({ color: 0xff8800, metalness: 0.5, roughness: 0.1 }); // More interesting material const sphere = new THREE.Mesh(geometry, material); scene.add(sphere); threeObjects.push(sphere); graphics3DObjects.push(sphere); // Add to graphics objects array sphere.position.set(2, 0, 0); // Position it to the side } else { graphics3DObjects.forEach(obj => { scene.remove(obj); threeObjects.splice(threeObjects.indexOf(obj), 1); }); graphics3DObjects = []; } } function createCharacterAnimation(isActive) { applyPlaceholderEffect("character-animation", isActive); // Placeholder for now. Complex to implement without assets/models } function createObjectAnimation(isActive) { applyPlaceholderEffect("object-animation", isActive); // Placeholder, could be animations on existing 3D objects } function createTextAnimation(isActive) { applyPlaceholderEffect("text-animation", isActive); // Placeholder, could be more complex text animations than moving text } function createGraphicsAnimation(isActive) { applyPlaceholderEffect("graphics-animation", isActive); // Placeholder, could be animations of 3D graphics objects } function applyARGeneral(isActive) { applyPlaceholderEffect("ar-general", isActive); // AR General Placeholder - very complex, needs device access and tracking } function applyARText(isActive) { applyPlaceholderEffect("ar-text", isActive); // AR Text Placeholder - requires AR setup } function applyARGraphics(isActive) { applyPlaceholderEffect("ar-graphics", isActive); // AR Graphics Placeholder - requires AR setup } // --- Menu Structure (HTML) --- const menuHTML = `
`; // --- Utility Functions --- // Creates a group of checkboxes for an effects category. function createEffectGroup(categoryName, effects) { let groupHTML = `