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