canvas实现鼠标跟随粒子连线圈圈冒泡交互动画效果代码
代码语言:html
所属分类:动画
代码描述:canvas实现鼠标跟随粒子连线圈圈冒泡交互动画效果代码
代码标签: canvas 鼠标 跟随 粒子 连线 圈圈 冒泡 交互 动画
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> body, html { margin: 0; padding: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body translate="no"> <canvas id="canvas"></canvas> <script > // Grab the canvas element from the DOM const canvas = document.getElementById("canvas"); // Get a 2D drawing context from the canvas const ctx = canvas.getContext("2d"); // Arrays to hold various particle types // (General particles, fireworks, dusty background, and ripples) const particles = []; const fireworkParticles = []; const dustParticles = []; const ripples = []; const techRipples = []; // A simple mouse state object to track the user's cursor const mouse = (() => { let state = { x: null, y: null }; return { get x() { return state.x; }, get y() { return state.y; }, set({ x, y }) { // Update the mouse position whenever the user moves the cursor state = { x, y }; }, reset() { // Clear mouse position when it leaves the canvas state = { x: null, y: null }; } }; })(); // Some global state variables for background shifting and frame counting let backgroundHue = 0; let frameCount = 0; let autoDrift = true; // If true, particles gently drift on their own // Dynamically adjust the number of particles based on canvas size function adjustParticleCount() { const particleConfig = { heightConditions: [200, 300, 400, 500, 600], widthConditions: [450, 600, 900, 1200, 1600], particlesForHeight: [40, 60, 70, 90, 110], particlesForWidth: [40, 50, 70, 90, 110] }; let numParticles = 130; // Check the height and pick a suitable particle count for (let i = 0; i < particleConfig.heightConditions.length; i++) { if (canvas.height < particleConfig.heightConditions[i]) { numParticles = particleConfig.particlesForHeight[i]; break; } } // Check the width and try to lower the particle count if needed for (let i = 0; i < particleConfig.widthConditions.length; i++) { if (canvas.width < particleConfig.widthConditions[i]) { numParticles = Math.min( numParticles, particleConfig.particlesForWidth[i]); break; } } return numParticles; } // Particle class handles both "normal" and "firework" particles // I ended up combining them to avoid duplicating similar code class Particle { constructor(x, y, isFirework = false) { const baseSpeed = isFirework ? Math.random() * 2 + 1 // fireworks move faster : Math.random() * 0.5 + 0.3; // regular particles move slowly // Assign various properties to give each particle some randomness Object.assign(this, { isFirework, x, y, vx: Math.cos(Math.random() * Math.PI * 2) * baseSpeed, vy: Math.sin(Math.random() * Math.PI * 2) * baseSpeed, size: isFirework ? Math.random() * 2 + 2 : Math.random() * 3 + 1, hue: Math.random() * 360, alpha: 1, sizeDirection: Math.random() < 0.5 ? -1 : 1, trail: [] }); } update(mouse) { // Calculate distance from mouse to apply interactive forces (if any) const dist = mouse.x !== null ? (mouse.x - this.x) ** 2 + (mouse.y - this.y) ** 2 : 0; if (!this.isFirework) { // Apply a force pushing particles away or toward the mouse if it's on screen const force = dist && dist < 22500 ? (22500 - dist) / 22500 : 0; // If mouse is not present and autoDrift is true, particles gently meander if (mouse.x === null && autoDrift) { this.vx += (Math.random() - 0.5) * 0.03; this.vy += (Math.random() - 0.5) * 0.03; } if (dist) { const sqrtDist = Math.sqrt(dist); // Slightly nudge particles toward the mouse position this.vx += (mouse.x - this.x) / sqrtDist * force * 0.1; this.vy += (mouse.y - this.y) / sqrtDist * force * 0.1; } // Dampen velocities a bit so they don't run off too wildly this.vx *= mouse.x !== null ? 0.99 : 0.998; this.vy *= mouse.y !== null ? 0.99 : 0.998; } else { // Firework particles fade out over time this.alpha -= 0.02; } // Update particle position this.x += this.vx; this.y += this.vy; // Bounce particles off canvas edges with a bit of energy loss if (this.x <= 0 || this.x >= canvas.width - 1) this.vx *= -0.9; if (this.y < 0 || this.y > canvas.height) this.vy *= -0.9; // Make the particle pulse in size just a bit this.size += this.sizeDirection * 0.1; if (this.size > 4 || this.size < 1) this.sizeDirection *= -1; // Cycle through hue to create a shifting color effect this.hue = (this.hue + 0.3) % 360; // Leave a trail of previous positions to create a motion blur effect if ( frameCount % 2 === 0 && ( Math.abs(this.vx) > 0.1 || Math.abs(this.vy) > 0.1)) { this.trail.push({ x: this.x, y: this.y, hue: this.hue, alpha: this.alpha }); if (this.trail.length > 15) this.trail.shift(); } } draw(ctx) { // Draw a gradient-based circle to represent the particle const gradient = ctx.createRadialGradient( this.x, this.y, 0, this.x, this.y, this.size); gradient.addColorStop( 0, `hsla(${this.hue}, 80%, 60%, ${Math.max(this.alpha, 0)})`); gradient.addColorStop( 1, `hsla(${this.hue + 30}, 80%, 30%, ${Math.max(this.alpha, 0)})`); ctx.fillStyle = gradient; // Add a slight glow if the screen is large ctx.shadowBlur = canvas.width > 900 ? 10 : 0; ctx.shadowColor = `hsl(${this.hue}, 80%, 60%)`; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; // Draw the particle's trail as a faint line if (this.trail.length > 1) { ctx.beginPath(); ctx.lineWidth = 1.5; for (let i = 0; i < this.trail.length - 1; i++) { const { x: x1, y: y1, hue: h1, alpha: a1 } = this.trail[i]; const { x: x2, y: y2 } = this.trail[i + 1]; ctx.strokeStyle = `hsla(${h1}, 80%, 60%, ${Math.max(a1, 0)})`; .........完整代码请登录后点击上方下载按钮下载查看
网友评论0