您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Real-time generative art system with particle reactions, procedural patterns, and interactive visual effects for drawaria.online!
// ==UserScript== // @name Drawaria Generative Art & Particle Reactor // @namespace http://tampermonkey.net/ // @version 1.0 // @description Real-time generative art system with particle reactions, procedural patterns, and interactive visual effects for drawaria.online! // @author YouTubeDrawaria // @match https://drawaria.online/* // @grant none // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online // ==/UserScript== (function() { 'use strict'; // Generative art system state const artState = { mode: 'reactive', generationSpeed: 50, particleCount: 1000, colorPalette: 'rainbow', pattern: 'fibonacci', reactToDrawing: true, autoGenerate: false, brushReaction: true, geometryMode: 'organic', flowField: { enabled: false, strength: 1, scale: 0.01 } }; // Particle class for reactive effects class ReactiveParticle { constructor(x, y, sourceData = null) { this.x = x || Math.random() * window.innerWidth; this.y = y || Math.random() * window.innerHeight; this.originalX = this.x; this.originalY = this.y; this.vx = (Math.random() - 0.5) * 2; this.vy = (Math.random() - 0.5) * 2; this.size = Math.random() * 4 + 1; this.life = 1.0; this.decay = Math.random() * 0.01 + 0.005; this.hue = Math.random() * 360; this.brightness = Math.random() * 50 + 50; this.reactRadius = Math.random() * 100 + 50; this.sourceData = sourceData; this.angle = Math.random() * Math.PI * 2; this.speed = Math.random() * 2 + 0.5; } update(mouseX, mouseY, flowField) { // Flow field influence if (artState.flowField.enabled && flowField) { const fieldX = Math.floor(this.x * artState.flowField.scale); const fieldY = Math.floor(this.y * artState.flowField.scale); const index = (fieldX + fieldY * Math.floor(window.innerWidth * artState.flowField.scale)) % flowField.length; if (flowField[index]) { this.vx += Math.cos(flowField[index]) * artState.flowField.strength * 0.1; this.vy += Math.sin(flowField[index]) * artState.flowField.strength * 0.1; } } // Mouse reaction if (artState.brushReaction && mouseX !== undefined && mouseY !== undefined) { const dx = this.x - mouseX; const dy = this.y - mouseY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < this.reactRadius) { const force = (this.reactRadius - distance) / this.reactRadius; this.vx += (dx / distance) * force * 0.5; this.vy += (dy / distance) * force * 0.5; this.hue = (this.hue + 2) % 360; this.size = Math.min(this.size * 1.1, 8); } } // Update position this.x += this.vx; this.y += this.vy; // Apply friction this.vx *= 0.98; this.vy *= 0.98; // Boundary behavior if (this.x < 0 || this.x > window.innerWidth) this.vx *= -0.8; if (this.y < 0 || this.y > window.innerHeight) this.vy *= -0.8; this.x = Math.max(0, Math.min(window.innerWidth, this.x)); this.y = Math.max(0, Math.min(window.innerHeight, this.y)); // Life decay this.life -= this.decay; this.size *= 0.999; return this.life > 0 && this.size > 0.1; } draw(ctx) { ctx.save(); ctx.globalAlpha = this.life; const color = this.getColor(); ctx.fillStyle = color; ctx.strokeStyle = color; ctx.beginPath(); if (artState.geometryMode === 'organic') { ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); } else if (artState.geometryMode === 'geometric') { const sides = 6; const radius = this.size; ctx.beginPath(); for (let i = 0; i < sides; i++) { const angle = (i / sides) * Math.PI * 2; const x = this.x + Math.cos(angle) * radius; const y = this.y + Math.sin(angle) * radius; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.closePath(); ctx.fill(); } else if (artState.geometryMode === 'lines') { ctx.lineWidth = this.size * 0.5; ctx.beginPath(); ctx.moveTo(this.x, this.y); ctx.lineTo(this.x + this.vx * 10, this.y + this.vy * 10); ctx.stroke(); } ctx.restore(); } getColor() { switch(artState.colorPalette) { case 'rainbow': return `hsl(${this.hue}, 70%, ${this.brightness}%)`; case 'fire': const fireHue = Math.max(0, Math.min(60, this.hue % 60)); return `hsl(${fireHue}, 90%, ${this.brightness}%)`; case 'ocean': const oceanHue = 180 + (this.hue % 120); return `hsl(${oceanHue}, 80%, ${this.brightness}%)`; case 'sunset': const sunsetHue = 300 + (this.hue % 120); return `hsl(${sunsetHue}, 85%, ${this.brightness}%)`; case 'monochrome': return `hsl(0, 0%, ${this.brightness}%)`; default: return `hsl(${this.hue}, 70%, ${this.brightness}%)`; } } } // Generative pattern generators class PatternGenerator { static fibonacci(ctx, centerX, centerY, time) { const goldenAngle = Math.PI * (3 - Math.sqrt(5)); const numPoints = 200; for (let i = 0; i < numPoints; i++) { const angle = i * goldenAngle + time * 0.001; const radius = Math.sqrt(i) * 3; const x = centerX + Math.cos(angle) * radius; const y = centerY + Math.sin(angle) * radius; const hue = (i * 137.5 + time * 0.1) % 360; const size = Math.sin(i * 0.1 + time * 0.01) * 2 + 3; ctx.fillStyle = `hsl(${hue}, 70%, 60%)`; ctx.beginPath(); ctx.arc(x, y, size, 0, Math.PI * 2); ctx.fill(); } } static mandala(ctx, centerX, centerY, time) { const layers = 8; const petals = 16; for (let layer = 1; layer <= layers; layer++) { const radius = layer * 20 + Math.sin(time * 0.001) * 10; for (let petal = 0; petal < petals; petal++) { const angle = (petal / petals) * Math.PI * 2 + time * 0.0005 * layer; const x = centerX + Math.cos(angle) * radius; const y = centerY + Math.sin(angle) * radius; const hue = (layer * 45 + petal * 22.5 + time * 0.05) % 360; const alpha = 0.7 - (layer * 0.08); ctx.fillStyle = `hsla(${hue}, 80%, 60%, ${alpha})`; ctx.beginPath(); ctx.arc(x, y, 8 - layer, 0, Math.PI * 2); ctx.fill(); } } } static voronoi(ctx, width, height, time) { const sites = []; const numSites = 12; for (let i = 0; i < numSites; i++) { const angle = (i / numSites) * Math.PI * 2 + time * 0.0003; const radius = Math.sin(time * 0.0002 + i) * 100 + 200; sites.push({ x: width/2 + Math.cos(angle) * radius, y: height/2 + Math.sin(angle) * radius, hue: (i * 30 + time * 0.02) % 360 }); } const imageData = ctx.createImageData(width/4, height/4); const data = imageData.data; for (let y = 0; y < height/4; y++) { for (let x = 0; x < width/4; x++) { let minDist = Infinity; let closestSite = sites; for (const site of sites) { const dist = Math.sqrt((x*4 - site.x)**2 + (y*4 - site.y)**2); if (dist < minDist) { minDist = dist; closestSite = site; } } const index = (y * width/4 + x) * 4; const rgb = PatternGenerator.hslToRgb(closestSite.hue, 70, 60); data[index] = rgb; data[index + 1] = rgb[1]; data[index + 2] = rgb[2]; data[index + 3] = 150; } } ctx.putImageData(imageData, 0, 0); } static hslToRgb(h, s, l) { h /= 360; s /= 100; l /= 100; const a = s * Math.min(l, 1 - l); const f = n => { const k = (n + h * 12) % 12; return l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); }; return [Math.floor(f(0) * 255), Math.floor(f(8) * 255), Math.floor(f(4) * 255)]; } } // Create control panel function createGenerativePanel() { const panel = document.createElement('div'); panel.innerHTML = ` <div id="generative-panel" style=" position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; padding: 25px; box-shadow: 0 20px 40px rgba(0,0,0,0.3); color: white; font-family: 'Segoe UI', sans-serif; z-index: 10000; min-width: 600px; backdrop-filter: blur(15px); border: 1px solid rgba(255,255,255,0.2); "> <h2 style="margin: 0 0 25px 0; text-align: center; color: #ffffff;">🎨 Generative Art Studio</h2> <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px;"> <!-- Art Generation --> <div class="control-section"> <h3 style="color: #e0e6ff; margin: 0 0 15px 0; font-size: 14px;">🌀 GENERATION</h3> <div style="margin: 10px 0;"> <label>Pattern:</label> <select id="pattern-type" style="width: 100%; padding: 5px; margin: 5px 0; border-radius: 5px;"> <option value="fibonacci">🌻 Fibonacci</option> <option value="mandala">🕉️ Mandala</option> <option value="voronoi">🔷 Voronoi</option> <option value="particles">✨ Particles</option> </select> </div> <div style="margin: 10px 0;"> <label>Speed: <span id="speed-display">50</span></label> <input type="range" id="generation-speed" min="1" max="100" value="50" style="width: 100%;"> </div> <button id="auto-generate" style="width: 100%; padding: 8px; margin: 5px 0;">🔄 Auto Generate</button> </div> <!-- Particle System --> <div class="control-section"> <h3 style="color: #e0e6ff; margin: 0 0 15px 0; font-size: 14px;">⚛️ PARTICLES</h3> <div style="margin: 10px 0;"> <label>Count: <span id="particle-count-display">1000</span></label> <input type="range" id="particle-count" min="100" max="3000" value="1000" style="width: 100%;"> </div> <div style="margin: 10px 0;"> <label>Geometry:</label> <select id="geometry-mode" style="width: 100%; padding: 5px; margin: 5px 0; border-radius: 5px;"> <option value="organic">⭕ Organic</option> <option value="geometric">🔶 Geometric</option> <option value="lines">📏 Lines</option> </select> </div> <button id="brush-reaction" style="width: 100%; padding: 8px; margin: 5px 0;">🖌️ Brush Reaction</button> </div> <!-- Visual Effects --> <div class="control-section"> <h3 style="color: #e0e6ff; margin: 0 0 15px 0; font-size: 14px;">🎭 EFFECTS</h3> <div style="margin: 10px 0;"> <label>Color Palette:</label> <select id="color-palette" style="width: 100%; padding: 5px; margin: 5px 0; border-radius: 5px;"> <option value="rainbow">🌈 Rainbow</option> <option value="fire">🔥 Fire</option> <option value="ocean">🌊 Ocean</option> <option value="sunset">🌅 Sunset</option> <option value="monochrome">⚫ Mono</option> </select> </div> <button id="flow-field" style="width: 100%; padding: 8px; margin: 5px 0;">🌊 Flow Field</button> <button id="clear-canvas" style="width: 100%; padding: 8px; margin: 5px 0;">🗑️ Clear</button> </div> </div> <!-- Quick Presets --> <div style="margin-top: 20px;"> <h3 style="color: #e0e6ff; margin: 0 0 15px 0; font-size: 14px; text-align: center;">⚡ QUICK PRESETS</h3> <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;"> <button class="art-preset" data-preset="psychedelic">🌀 Psychedelic</button> <button class="art-preset" data-preset="zen">☯️ Zen Garden</button> <button class="art-preset" data-preset="cosmic">🌌 Cosmic</button> <button class="art-preset" data-preset="organic">🌿 Organic</button> </div> </div> </div> `; // Add styles const style = document.createElement('style'); style.textContent = ` #generative-panel button, #generative-panel select { background: rgba(255, 255, 255, 0.15); border: 1px solid rgba(255, 255, 255, 0.3); color: white; border-radius: 8px; cursor: pointer; transition: all 0.3s ease; } #generative-panel button:hover { background: rgba(255, 255, 255, 0.25); transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.2); } .art-preset { padding: 8px 4px !important; font-size: 12px !important; } #generative-panel input[type="range"] { background: rgba(255, 255, 255, 0.2); height: 6px; border-radius: 3px; } .control-section { background: rgba(255, 255, 255, 0.1); padding: 15px; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.1); } `; document.head.appendChild(style); document.body.appendChild(panel); return panel; } // Main generative art engine class GenerativeEngine { constructor() { this.canvas = this.createCanvas(); this.ctx = this.canvas.getContext('2d'); this.particles = []; this.flowField = []; this.mouseX = 0; this.mouseY = 0; this.startTime = Date.now(); this.backgroundCanvas = this.createBackgroundCanvas(); this.backgroundCtx = this.backgroundCanvas.getContext('2d'); this.setupEventListeners(); this.generateFlowField(); this.initializeParticles(); this.animate(); } createCanvas() { const canvas = document.createElement('canvas'); canvas.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 9997; mix-blend-mode: screen; `; canvas.width = window.innerWidth; canvas.height = window.innerHeight; document.body.appendChild(canvas); return canvas; } createBackgroundCanvas() { const canvas = document.createElement('canvas'); canvas.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 9996; opacity: 0.7; `; canvas.width = window.innerWidth; canvas.height = window.innerHeight; document.body.appendChild(canvas); return canvas; } setupEventListeners() { document.addEventListener('mousemove', (e) => { this.mouseX = e.clientX; this.mouseY = e.clientY; }); window.addEventListener('resize', () => { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; this.backgroundCanvas.width = window.innerWidth; this.backgroundCanvas.height = window.innerHeight; this.generateFlowField(); }); } generateFlowField() { const cols = Math.floor(window.innerWidth * artState.flowField.scale); const rows = Math.floor(window.innerHeight * artState.flowField.scale); this.flowField = []; for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { const angle = Math.cos(x * 0.1) + Math.sin(y * 0.1); this.flowField.push(angle); } } } initializeParticles() { this.particles = []; for (let i = 0; i < artState.particleCount; i++) { this.particles.push(new ReactiveParticle()); } } spawnParticlesAroundMouse() { if (artState.brushReaction) { for (let i = 0; i < 5; i++) { const angle = Math.random() * Math.PI * 2; const distance = Math.random() * 50; const x = this.mouseX + Math.cos(angle) * distance; const y = this.mouseY + Math.sin(angle) * distance; this.particles.push(new ReactiveParticle(x, y)); } // Remove excess particles if (this.particles.length > artState.particleCount * 2) { this.particles.splice(0, this.particles.length - artState.particleCount); } } } updateParticles() { const flowField = artState.flowField.enabled ? this.flowField : null; this.particles = this.particles.filter(particle => particle.update(this.mouseX, this.mouseY, flowField) ); // Maintain minimum particle count while (this.particles.length < artState.particleCount) { this.particles.push(new ReactiveParticle()); } } drawGenerativePattern() { if (!artState.autoGenerate) return; const time = Date.now() - this.startTime; const centerX = window.innerWidth / 2; const centerY = window.innerHeight / 2; this.backgroundCtx.globalAlpha = 0.05; this.backgroundCtx.fillStyle = '#000000'; this.backgroundCtx.fillRect(0, 0, this.backgroundCanvas.width, this.backgroundCanvas.height); this.backgroundCtx.globalAlpha = 1; switch(artState.pattern) { case 'fibonacci': PatternGenerator.fibonacci(this.backgroundCtx, centerX, centerY, time); break; case 'mandala': PatternGenerator.mandala(this.backgroundCtx, centerX, centerY, time); break; case 'voronoi': PatternGenerator.voronoi(this.backgroundCtx, this.backgroundCanvas.width, this.backgroundCanvas.height, time); break; } } animate() { // Clear main canvas this.ctx.fillStyle = 'rgba(0, 0, 0, 0.02)'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // Generate background patterns this.drawGenerativePattern(); // Update and draw particles this.updateParticles(); this.particles.forEach(particle => particle.draw(this.ctx)); // Spawn particles on mouse movement if (Math.random() < 0.3) { this.spawnParticlesAroundMouse(); } requestAnimationFrame(() => this.animate()); } clearAll() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.backgroundCtx.clearRect(0, 0, this.backgroundCanvas.width, this.backgroundCanvas.height); this.particles = []; } applyPreset(presetName) { switch(presetName) { case 'psychedelic': artState.pattern = 'mandala'; artState.colorPalette = 'rainbow'; artState.geometryMode = 'organic'; artState.flowField.enabled = true; artState.autoGenerate = true; break; case 'zen': artState.pattern = 'fibonacci'; artState.colorPalette = 'monochrome'; artState.geometryMode = 'lines'; artState.flowField.enabled = false; artState.autoGenerate = true; break; case 'cosmic': artState.pattern = 'voronoi'; artState.colorPalette = 'sunset'; artState.geometryMode = 'geometric'; artState.flowField.enabled = true; artState.autoGenerate = true; break; case 'organic': artState.pattern = 'particles'; artState.colorPalette = 'ocean'; artState.geometryMode = 'organic'; artState.flowField.enabled = true; artState.autoGenerate = false; break; } this.updateUI(); } updateUI() { document.getElementById('pattern-type').value = artState.pattern; document.getElementById('color-palette').value = artState.colorPalette; document.getElementById('geometry-mode').value = artState.geometryMode; } } // Initialize the system function init() { const panel = createGenerativePanel(); const engine = new GenerativeEngine(); // Event listeners document.getElementById('pattern-type').onchange = (e) => { artState.pattern = e.target.value; }; document.getElementById('generation-speed').oninput = (e) => { artState.generationSpeed = parseInt(e.target.value); document.getElementById('speed-display').textContent = e.target.value; }; document.getElementById('particle-count').oninput = (e) => { artState.particleCount = parseInt(e.target.value); document.getElementById('particle-count-display').textContent = e.target.value; }; document.getElementById('color-palette').onchange = (e) => { artState.colorPalette = e.target.value; }; document.getElementById('geometry-mode').onchange = (e) => { artState.geometryMode = e.target.value; }; document.getElementById('auto-generate').onclick = () => { artState.autoGenerate = !artState.autoGenerate; document.getElementById('auto-generate').style.background = artState.autoGenerate ? 'rgba(46, 213, 115, 0.3)' : 'rgba(255, 255, 255, 0.15)'; }; document.getElementById('brush-reaction').onclick = () => { artState.brushReaction = !artState.brushReaction; document.getElementById('brush-reaction').style.background = artState.brushReaction ? 'rgba(46, 213, 115, 0.3)' : 'rgba(255, 255, 255, 0.15)'; }; document.getElementById('flow-field').onclick = () => { artState.flowField.enabled = !artState.flowField.enabled; document.getElementById('flow-field').style.background = artState.flowField.enabled ? 'rgba(46, 213, 115, 0.3)' : 'rgba(255, 255, 255, 0.15)'; engine.generateFlowField(); }; document.getElementById('clear-canvas').onclick = () => { engine.clearAll(); }; // Preset buttons document.querySelectorAll('.art-preset').forEach(btn => { btn.onclick = () => { engine.applyPreset(btn.dataset.preset); }; }); // Welcome animation setTimeout(() => { const welcome = document.createElement('div'); welcome.innerHTML = ` <div style=" position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: linear-gradient(45deg, #667eea, #764ba2); color: white; padding: 20px 40px; border-radius: 15px; text-align: center; z-index: 10001; box-shadow: 0 15px 35px rgba(0,0,0,0.3); animation: artWelcome 6s ease-in-out forwards; "> <h3>🎨 Generative Art Studio Activated! 🎨</h3> <p>✨ Watch particles react to your drawing!</p> <p>🌀 Try the psychedelic preset for a trip!</p> </div> `; const welcomeStyle = document.createElement('style'); welcomeStyle.textContent = ` @keyframes artWelcome { 0% { opacity: 0; transform: translateX(-50%) translateY(100px); } 15% { opacity: 1; transform: translateX(-50%) translateY(0); } 85% { opacity: 1; transform: translateX(-50%) translateY(0); } 100% { opacity: 0; transform: translateX(-50%) translateY(-100px); } } `; document.head.appendChild(welcomeStyle); document.body.appendChild(welcome); setTimeout(() => welcome.remove(), 6000); }, 2000); console.log('🎨 Generative Art System initialized!'); } // Start when ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();