M-006 Background complex

How to create a particle background パーティクル背景の作り方

Floating particles in the background. Customize particle count, speed, and colors. 背景に浮遊するパーティクルアニメーション。粒子の数、速度、色をカスタマイズできます。

ライブデモ Live Demo

パーティクル背景 Particle Background
50
20s

概要・用途・特徴Overview, Usage & Features

何ができるかWhat it does

Floating particles in the background. Customize particle count, speed, and colors.

背景に浮遊するパーティクルアニメーション。粒子の数、速度、色をカスタマイズできます。

どこで使うかWhere to use

web application, marketing page

ヒーロー背景、テクノロジー系サイト、ゲームUI、サイエンスフィクション風デザイン

特徴Key features

Canvas-based particle system with customizable particle count, speed, color, and connection lines. Fully interactive — particles react to mouse movement. Lightweight vanilla JS with no dependencies. Degrades gracefully when canvas is unavailable.

Canvasベースのパーティクルシステム。パーティクル数・速度・色・接続線をカスタマイズ可能。マウス動作にインタラクティブに反応。依存なしのバニラJS。Canvasが利用できない場合のグレースフルデグレード対応。

調整可能パラメータ Adjustable Parameters

Parameter Default Description
--particle-countnumber of particles to display
--particle-sizesize of each particle
--particle-speedanimation duration (seconds)
particle colorparticle color and opacity
random positionrandom horizontal and vertical positioning
--translate-xhorizontal movement distance

実装コード Implementation Code

// react/M-006.jsx
import { useEffect, useRef } from "react";
import "./M-006.css";

export default function ParticleBackground({ count = 50 }) {
  const stageRef = useRef(null);

  useEffect(() => {
    const stage = stageRef.current;
    if (!stage) return;

    const particles = [];
    for (let i = 0; i < count; i++) {
      const particle = document.createElement("div");
      particle.className = "particle";
      particle.style.left = Math.random() * 100 + "%";
      particle.style.top = Math.random() * 100 + "%";
      particle.style.animationDelay = Math.random() * 20 + "s";
      particle.style.animationDuration = (15 + Math.random() * 10) + "s";
      particle.style.setProperty("--translate-x", (Math.random() - 0.5) * 200 + "px");
      stage.appendChild(particle);
      particles.push(particle);
    }

    return () => {
      particles.forEach(p => p.remove());
    };
  }, [count]);

  return (
    <div className="particle-stage" ref={stageRef}>
      <div className="particle-content">Content</div>
    </div>
  );
}
/* react/M-006.css */
.particle-stage {
  position: relative;
  overflow: hidden;
  min-height: 300px;
}

.particle {
  position: absolute;
  width: 3px;
  height: 3px;
  background: rgba(255, 255, 255, 0.6);
  border-radius: 50%;
  animation: float 20s linear infinite;
}

@keyframes float {
  0% {
    transform: translateY(50px) translateX(0);
    opacity: 0;
  }
  10%, 90% {
    opacity: 1;
  }
  100% {
    transform: translateY(-50px) translateX(var(--translate-x, 0));
    opacity: 0;
  }
}
import React, { useEffect, useRef, useState } from 'react';
import './M-006.css';

const ParticleBackground = ({ 
  count = 50,
  particleSize = 3,
  speed = 20,
  className = "",
  children 
}) => {
  const stageRef = useRef(null);
  const [particles, setParticles] = useState([]);

  const createParticle = () => {
    return {
      id: Math.random(),
      left: Math.random() * 100,
      top: Math.random() * 100,
      delay: Math.random() * 20,
      duration: 15 + Math.random() * 10,
      translateX: (Math.random() - 0.5) * 200
    };
  };

  const generateParticles = (particleCount) => {
    const newParticles = [];
    for (let i = 0; i < particleCount; i++) {
      newParticles.push(createParticle());
    }
    setParticles(newParticles);
  };

  useEffect(() => {
    generateParticles(count);
  }, [count]);

  return (
    <div 
      ref={stageRef}
      className={`particle-stage ${className}`}
      style={{
        '--particle-size': `${particleSize}px`,
        '--particle-speed': `${speed}s`
      }}
    >
      {particles.map((particle) => (
        <div
          key={particle.id}
          className="particle"
          style={{
            left: `${particle.left}%`,
            top: `${particle.top}%`,
            animationDelay: `${particle.delay}s`,
            animationDuration: `${particle.duration}s`,
            '--translate-x': `${particle.translateX}px`
          }}
        />
      ))}
      <div className="particle-content">
        {children}
      </div>
    </div>
  );
};

// Demo component for showcase
const ParticleBackgroundDemo = () => {
  const [settings, setSettings] = useState({
    count: 50,
    particleSize: 3,
    speed: 20
  });

  const handleSettingChange = (key, value) => {
    setSettings(prev => ({
      ...prev,
      [key]: value
    }));
  };

  return (
    <div className="particle-demo">
      <div className="demo-stage">
        <ParticleBackground 
          count={settings.count}
          particleSize={settings.particleSize}
          speed={settings.speed}
        >
          <h2>Particle Background</h2>
          <p>Beautiful floating particles animation</p>
        </ParticleBackground>
      </div>
      
      <div className="controls">
        <div className="control-group">
          <label htmlFor="count-slider">
            Particle Count: {settings.count}
          </label>
          <input
            id="count-slider"
            type="range"
            min="20"
            max="100"
            step="10"
            value={settings.count}
            onChange={(e) => handleSettingChange('count', parseInt(e.target.value))}
          />
        </div>
        
        <div className="control-group">
          <label htmlFor="size-slider">
            Particle Size: {settings.particleSize}px
          </label>
          <input
            id="size-slider"
            type="range"
            min="1"
            max="6"
            step="1"
            value={settings.particleSize}
            onChange={(e) => handleSettingChange('particleSize', parseInt(e.target.value))}
          />
        </div>
        
        <div className="control-group">
          <label htmlFor="speed-slider">
            Animation Speed: {settings.speed}s
          </label>
          <input
            id="speed-slider"
            type="range"
            min="10"
            max="30"
            step="5"
            value={settings.speed}
            onChange={(e) => handleSettingChange('speed', parseInt(e.target.value))}
          />
        </div>
      </div>
    </div>
  );
};

export default ParticleBackground;
export { ParticleBackgroundDemo };
/* M-006 Particle Background Styles */

.particle-demo {
  width: 100%;
  max-width: 1000px;
  margin: 0 auto;
  padding: 2rem;
}

.demo-stage {
  margin-bottom: 2rem;
}

.particle-stage {
  border-radius: 18px;
  padding: 0;
  background: #050714;
  border: 1px solid rgba(255, 255, 255, 0.1);
  min-height: 300px;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  
  /* CSS Custom Properties for customization */
  --particle-size: 3px;
  --particle-speed: 20s;
}

.particle-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 30% 50%, rgba(92, 106, 196, 0.15), transparent 50%),
              radial-gradient(circle at 70% 50%, rgba(34, 211, 238, 0.15), transparent 50%);
  z-index: 0;
}

.particle {
  position: absolute;
  width: var(--particle-size);
  height: var(--particle-size);
  background: rgba(255, 255, 255, 0.6);
  border-radius: 50%;
  animation: float var(--particle-speed) linear infinite;
  opacity: 0;
}

@keyframes float {
  0% {
    transform: translateY(50px) translateX(0);
    opacity: 0;
  }
  10% {
    opacity: 1;
  }
  90% {
    opacity: 1;
  }
  100% {
    transform: translateY(-50px) translateX(var(--translate-x, 0));
    opacity: 0;
  }
}

.particle-content {
  position: relative;
  z-index: 1;
  color: #f5f6ff;
  font-size: 24px;
  font-weight: 600;
  text-align: center;
  padding: 40px;
}

.particle-content h2 {
  margin: 0 0 1rem 0;
  font-size: 2rem;
  font-weight: 700;
}

.particle-content p {
  margin: 0;
  opacity: 0.8;
  font-size: 1.1rem;
  font-weight: 400;
}

/* Controls Styles */
.controls {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1.5rem;
  padding: 1.5rem;
  background: #f8fafc;
  border-radius: 12px;
  border: 1px solid #e2e8f0;
}

.control-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.control-group label {
  font-weight: 600;
  color: #374151;
  font-size: 0.875rem;
}

.control-group input[type="range"] {
  width: 100%;
  height: 6px;
  background: #e2e8f0;
  border-radius: 3px;
  outline: none;
  cursor: pointer;
}

.control-group input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 18px;
  height: 18px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.control-group input[type="range"]::-moz-range-thumb {
  width: 18px;
  height: 18px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* Dark theme support */
@media (prefers-color-scheme: dark) {
  .controls {
    background: #1f2937;
    border-color: #374151;
  }
  
  .control-group label {
    color: #f3f4f6;
  }
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .particle-demo {
    padding: 1rem;
  }
  
  .particle-stage {
    min-height: 250px;
  }
  
  .particle-content {
    padding: 24px;
  }
  
  .particle-content h2 {
    font-size: 1.5rem;
  }
  
  .particle-content p {
    font-size: 1rem;
  }
  
  .controls {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
}

/* High contrast mode support */
@media (prefers-contrast: high) {
  .particle-stage {
    border: 2px solid #fff;
  }
  
  .control-group input[type="range"]::-webkit-slider-thumb {
    border: 3px solid #000;
  }
}

仕組みとカスタマイズHow It Works & Customization

仕組みHow it works

A requestAnimationFrame loop draws and moves particle objects on an HTML canvas element each frame. Each particle has an x/y position, velocity vector, and size. When two particles come within a threshold distance, a semi-transparent connecting line is drawn between them. Mouse position influences nearby particle trajectories via an attraction/repulsion force.

requestAnimationFrameループでHTMLキャンバス上のパーティクルオブジェクトを毎フレーム描画・移動。各パーティクルはx/y座標、速度ベクトル、サイズを持つ。閾値距離内の2つのパーティクル間に半透明の接続線を描画。マウス位置が近傍パーティクルの軌跡に引力/斥力として影響。

カスタマイズ方法Customization

Reduce particleCount for better performance on mobile. Change lineColor and dotColor to match brand identity. Adjust the distance threshold for connection lines to increase or decrease the web density. Set interactive: false to disable mouse reaction.

モバイルのパフォーマンス向上のためparticleCountを減らす。lineColorとdotColorをブランドカラーに変更。接続線の距離閾値でウェブの密度を調整。interactive:falseでマウス反応を無効化。

注意点Caveats

Canvas animations are CPU-intensive. Always provide a reduced-motion fallback (static background or simplified version). Avoid rendering too many particles (>200) on mobile devices. Consider pausing animation when the tab is not visible using the Page Visibility API.

Canvasアニメーションははたして CPU 負荷が高い。静的背景やシンプル版など必ずreduced-motionフォールバックを提供。モバイルでは200個以上のパーティクルを避ける。Page Visibility APIでタブ非表示時にアニメーションを一時停止することを検討。

よくある質問 Frequently Asked Questions

How to customize the particle background? Particle Backgroundをカスタマイズするには?

Modify the CSS custom properties and class styles defined in the code section. Key adjustable values include colors, sizes, durations, and spacing. See the Adjustable Parameters section for specific variables.

コードセクションで定義されているCSSカスタムプロパティとクラススタイルを変更してください。色、サイズ、時間、間隔が主な調整可能値です。具体的な変数は調整可能パラメータセクションを参照してください。

How to use the particle background in React? ReactでParticle Backgroundを使うには?

Import the provided React component and its CSS file. The component accepts props for customization. Check the React code section for the full implementation and available props.

提供されているReactコンポーネントとCSSファイルをインポートしてください。コンポーネントのpropsでカスタマイズできます。完全な実装と利用可能なpropsはReactコードセクションを参照してください。

What are the performance implications of particle background? Particle Backgroundのパフォーマンスへの影響は?

This implementation uses CSS transforms and opacity for animations, which are GPU-accelerated. It's lightweight and doesn't cause layout thrashing. Consider using prefers-reduced-motion for accessibility.

この実装はCSSのtransformとopacityを使用しており、GPUアクセラレーションされます。軽量でレイアウトスラッシングを引き起こしません。アクセシビリティのためにprefers-reduced-motionの使用を検討してください。

AIへの指示テンプレート AI Prompt Template

以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.