パーティクル接続(星座)
近い粒子同士を線で結び星座のように見せる定番表現。マウス付近の粒子も結線します。ヒーロー背景やローディング演出に最適です。
ライブデモ
使用例(お題: SaaS FlowDesk)
この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- FlowDesk:星座パーティクル背景のSaaSヒーロー -->
<section class="fd-hero">
<!-- 主役:接続パーティクル背景 -->
<canvas class="fd-hero__bg" id="fdConstellation"></canvas>
<!-- 前景UI:ナビ+ヒーローコピー+CTA -->
<header class="fd-nav">
<span class="fd-nav__logo"><i></i>FlowDesk</span>
<nav class="fd-nav__menu">
<a href="#">機能</a>
<a href="#">料金</a>
<a href="#">導入事例</a>
<a class="fd-nav__cta" href="#">無料で始める</a>
</nav>
</header>
<div class="fd-hero__inner">
<span class="fd-hero__tag">TEAM WORKFLOW PLATFORM</span>
<h1 class="fd-hero__title">チームの点と点を、<br>ひとつの流れに。</h1>
<p class="fd-hero__lead">散らばったタスク・ファイル・会話をリアルタイムでつなぐ、次世代のコラボレーション基盤。</p>
<div class="fd-hero__actions">
<a class="fd-btn fd-btn--main" href="#">14日間 無料トライアル</a>
<a class="fd-btn fd-btn--ghost" href="#">デモを見る</a>
</div>
<p class="fd-hero__note">クレジットカード不要・3,200社が導入</p>
</div>
</section>
CSS
/* FlowDesk:星座背景のSaaSヒーロー */
:root {
--navy: #0f1b34;
--blue: #4f7cff;
--white: #ffffff;
}
* { box-sizing: border-box; }
body {
margin: 0;
height: 400px;
font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", system-ui, sans-serif;
background: var(--navy);
overflow: hidden;
}
.fd-hero {
position: relative;
height: 400px;
overflow: hidden;
background:
radial-gradient(900px 420px at 75% -10%, #1c2c52, transparent),
linear-gradient(160deg, #0f1b34, #0a1226);
}
/* 主役:背景いっぱいの接続パーティクル */
.fd-hero__bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
/* ナビ */
.fd-nav {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 24px;
}
.fd-nav__logo {
display: flex;
align-items: center;
gap: 8px;
font-size: 17px;
font-weight: 800;
letter-spacing: 0.02em;
color: var(--white);
}
.fd-nav__logo i {
width: 16px;
height: 16px;
border-radius: 5px;
background: linear-gradient(135deg, var(--blue), #7aa0ff);
box-shadow: 0 0 14px rgba(79,124,255,0.6);
}
.fd-nav__menu { display: flex; align-items: center; gap: 20px; }
.fd-nav__menu a {
font-size: 13px;
color: rgba(255,255,255,0.78);
text-decoration: none;
transition: color 0.2s ease;
}
.fd-nav__menu a:hover { color: var(--white); }
.fd-nav__cta {
padding: 8px 16px;
border-radius: 8px;
background: var(--blue);
color: var(--white) !important;
font-weight: 700;
box-shadow: 0 6px 18px rgba(79,124,255,0.4);
}
/* ヒーロー本文 */
.fd-hero__inner {
position: relative;
z-index: 2;
padding: 34px 28px;
max-width: 460px;
}
.fd-hero__tag {
display: inline-block;
font-size: 10px;
letter-spacing: 0.28em;
color: #8fb0ff;
font-weight: 700;
}
.fd-hero__title {
margin: 14px 0 14px;
font-size: 30px;
line-height: 1.35;
font-weight: 800;
color: var(--white);
}
.fd-hero__lead {
margin: 0 0 22px;
font-size: 13.5px;
line-height: 1.8;
color: rgba(255,255,255,0.78);
}
.fd-hero__actions { display: flex; gap: 12px; flex-wrap: wrap; }
.fd-btn {
display: inline-block;
padding: 11px 20px;
border-radius: 9px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
transition: transform 0.2s ease;
}
.fd-btn--main {
background: var(--blue);
color: var(--white);
box-shadow: 0 10px 24px rgba(79,124,255,0.45);
}
.fd-btn--ghost {
background: rgba(255,255,255,0.08);
color: var(--white);
border: 1px solid rgba(255,255,255,0.18);
}
.fd-btn:hover { transform: translateY(-2px); }
.fd-hero__note {
margin: 16px 0 0;
font-size: 11.5px;
color: rgba(255,255,255,0.5);
}
@media (prefers-reduced-motion: reduce) {
.fd-btn { transition: none; }
}
JavaScript
// FlowDesk:星座風パーティクル接続をヒーロー背景に(青系で配色)
(() => {
const canvas = document.getElementById('fdConstellation');
if (!canvas) return; // null安全
const ctx = canvas.getContext('2d');
if (!ctx) return;
let w = 0, h = 0, particles = [], raf = 0, running = true;
const dpr = Math.min(window.devicePixelRatio || 1, 2);
const mouse = { x: -9999, y: -9999 };
// コンテナ全体にフィット(高解像度対応)
function resize() {
const r = canvas.getBoundingClientRect();
w = r.width; h = r.height;
canvas.width = Math.max(1, w * dpr);
canvas.height = Math.max(1, h * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
// 画面サイズに応じた粒子生成
function makeParticles() {
const count = Math.max(36, Math.min(90, Math.floor((w * h) / 11000)));
particles = Array.from({ length: count }, () => ({
x: Math.random() * w,
y: Math.random() * h,
vx: (Math.random() - 0.5) * 0.35,
vy: (Math.random() - 0.5) * 0.35,
r: Math.random() * 1.5 + 0.6
}));
}
resize();
makeParticles();
function step() {
ctx.clearRect(0, 0, w, h);
// 粒子の更新と描画(ノード)
for (const p of particles) {
p.x += p.vx; p.y += p.vy;
if (p.x < 0 || p.x > w) p.vx *= -1;
if (p.y < 0 || p.y > h) p.vy *= -1;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(150,180,255,0.85)';
ctx.fill();
}
// 近い粒子同士を青い線で結ぶ
for (let i = 0; i < particles.length; i++) {
const a = particles[i];
for (let j = i + 1; j < particles.length; j++) {
const b = particles[j];
const dist = Math.hypot(a.x - b.x, a.y - b.y);
if (dist < 118) {
ctx.strokeStyle = `rgba(79,124,255,${(1 - dist / 118) * 0.45})`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.stroke();
}
}
// マウス周辺も結線(インタラクティブ)
const dm = Math.hypot(a.x - mouse.x, a.y - mouse.y);
if (dm < 150) {
ctx.strokeStyle = `rgba(122,160,255,${(1 - dm / 150) * 0.6})`;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
}
}
raf = requestAnimationFrame(step);
}
// ループ制御(多重起動防止)
function start() {
if (running) return;
running = true;
raf = requestAnimationFrame(step);
}
function stop() {
running = false;
cancelAnimationFrame(raf);
}
// マウス座標(キャンバス基準)
canvas.addEventListener('pointermove', (e) => {
const r = canvas.getBoundingClientRect();
mouse.x = e.clientX - r.left;
mouse.y = e.clientY - r.top;
});
canvas.addEventListener('pointerleave', () => { mouse.x = mouse.y = -9999; });
window.addEventListener('resize', () => { resize(); makeParticles(); });
// タブ非表示で停止、復帰で再開
document.addEventListener('visibilitychange', () => {
document.hidden ? stop() : start();
});
running = false;
start();
})();
コード
HTML
<!-- パーティクル接続(星座) -->
<div class="stage">
<canvas id="constellationCanvas"></canvas>
<div class="label">Constellation</div>
</div>
CSS
/* ステージ全体(背景は深い夜空のグラデ) */
:root {
--bg-1: #0a0e27;
--bg-2: #1a1f4a;
--dot: 200, 220, 255;
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
font-family: "Segoe UI", system-ui, sans-serif;
background: var(--bg-1);
}
.stage {
position: relative;
width: 100%;
height: 360px;
overflow: hidden;
background:
radial-gradient(1200px 500px at 70% -10%, var(--bg-2), transparent),
linear-gradient(160deg, var(--bg-1), #05071a);
}
#constellationCanvas {
display: block;
width: 100%;
height: 100%;
}
/* 右下の控えめなラベル */
.label {
position: absolute;
right: 16px;
bottom: 12px;
color: rgba(var(--dot), .55);
font-size: 12px;
letter-spacing: .35em;
text-transform: uppercase;
pointer-events: none;
}
JavaScript
// 星座風パーティクル接続デモ
(() => {
const canvas = document.getElementById('constellationCanvas');
if (!canvas) return; // null安全
const ctx = canvas.getContext('2d');
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
let w = 0, h = 0, dpr = Math.min(window.devicePixelRatio || 1, 2);
const mouse = { x: -9999, y: -9999 };
// キャンバスを親要素に合わせてリサイズ(高解像度対応)
function resize() {
const r = canvas.getBoundingClientRect();
w = r.width; h = r.height;
canvas.width = w * dpr;
canvas.height = h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
// 画面サイズに応じた粒子数
function makeParticles() {
const count = Math.max(40, Math.min(110, Math.floor((w * h) / 9000)));
return Array.from({ length: count }, () => ({
x: Math.random() * w,
y: Math.random() * h,
vx: (Math.random() - 0.5) * 0.4,
vy: (Math.random() - 0.5) * 0.4,
r: Math.random() * 1.6 + 0.6
}));
}
resize();
let particles = makeParticles();
function step() {
ctx.clearRect(0, 0, w, h);
// 粒子の更新と描画
for (const p of particles) {
if (!reduced) { p.x += p.vx; p.y += p.vy; }
if (p.x < 0 || p.x > w) p.vx *= -1;
if (p.y < 0 || p.y > h) p.vy *= -1;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(200,220,255,0.9)';
ctx.fill();
}
// 近い粒子同士を線で結ぶ(距離で透明度変化)
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const a = particles[i], b = particles[j];
const dx = a.x - b.x, dy = a.y - b.y;
const dist = Math.hypot(dx, dy);
if (dist < 120) {
ctx.strokeStyle = `rgba(120,170,255,${(1 - dist / 120) * 0.5})`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.stroke();
}
}
// マウス周辺の粒子も結ぶ
const dmx = particles[i].x - mouse.x, dmy = particles[i].y - mouse.y;
const dm = Math.hypot(dmx, dmy);
if (dm < 150) {
ctx.strokeStyle = `rgba(255,200,140,${(1 - dm / 150) * 0.7})`;
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
}
}
requestAnimationFrame(step);
}
// マウス座標を取得(親基準)
canvas.addEventListener('pointermove', (e) => {
const r = canvas.getBoundingClientRect();
mouse.x = e.clientX - r.left;
mouse.y = e.clientY - r.top;
});
canvas.addEventListener('pointerleave', () => { mouse.x = mouse.y = -9999; });
window.addEventListener('resize', () => { resize(); particles = makeParticles(); });
requestAnimationFrame(step);
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「パーティクル接続(星座)」の効果を追加してください。
# 追加してほしい効果
パーティクル接続(星座)(Canvas エフェクト)
近い粒子同士を線で結び星座のように見せる定番表現。マウス付近の粒子も結線します。ヒーロー背景やローディング演出に最適です。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- パーティクル接続(星座) -->
<div class="stage">
<canvas id="constellationCanvas"></canvas>
<div class="label">Constellation</div>
</div>
【CSS】
/* ステージ全体(背景は深い夜空のグラデ) */
:root {
--bg-1: #0a0e27;
--bg-2: #1a1f4a;
--dot: 200, 220, 255;
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
font-family: "Segoe UI", system-ui, sans-serif;
background: var(--bg-1);
}
.stage {
position: relative;
width: 100%;
height: 360px;
overflow: hidden;
background:
radial-gradient(1200px 500px at 70% -10%, var(--bg-2), transparent),
linear-gradient(160deg, var(--bg-1), #05071a);
}
#constellationCanvas {
display: block;
width: 100%;
height: 100%;
}
/* 右下の控えめなラベル */
.label {
position: absolute;
right: 16px;
bottom: 12px;
color: rgba(var(--dot), .55);
font-size: 12px;
letter-spacing: .35em;
text-transform: uppercase;
pointer-events: none;
}
【JavaScript】
// 星座風パーティクル接続デモ
(() => {
const canvas = document.getElementById('constellationCanvas');
if (!canvas) return; // null安全
const ctx = canvas.getContext('2d');
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
let w = 0, h = 0, dpr = Math.min(window.devicePixelRatio || 1, 2);
const mouse = { x: -9999, y: -9999 };
// キャンバスを親要素に合わせてリサイズ(高解像度対応)
function resize() {
const r = canvas.getBoundingClientRect();
w = r.width; h = r.height;
canvas.width = w * dpr;
canvas.height = h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
// 画面サイズに応じた粒子数
function makeParticles() {
const count = Math.max(40, Math.min(110, Math.floor((w * h) / 9000)));
return Array.from({ length: count }, () => ({
x: Math.random() * w,
y: Math.random() * h,
vx: (Math.random() - 0.5) * 0.4,
vy: (Math.random() - 0.5) * 0.4,
r: Math.random() * 1.6 + 0.6
}));
}
resize();
let particles = makeParticles();
function step() {
ctx.clearRect(0, 0, w, h);
// 粒子の更新と描画
for (const p of particles) {
if (!reduced) { p.x += p.vx; p.y += p.vy; }
if (p.x < 0 || p.x > w) p.vx *= -1;
if (p.y < 0 || p.y > h) p.vy *= -1;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(200,220,255,0.9)';
ctx.fill();
}
// 近い粒子同士を線で結ぶ(距離で透明度変化)
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const a = particles[i], b = particles[j];
const dx = a.x - b.x, dy = a.y - b.y;
const dist = Math.hypot(dx, dy);
if (dist < 120) {
ctx.strokeStyle = `rgba(120,170,255,${(1 - dist / 120) * 0.5})`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.stroke();
}
}
// マウス周辺の粒子も結ぶ
const dmx = particles[i].x - mouse.x, dmy = particles[i].y - mouse.y;
const dm = Math.hypot(dmx, dmy);
if (dm < 150) {
ctx.strokeStyle = `rgba(255,200,140,${(1 - dm / 150) * 0.7})`;
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
}
}
requestAnimationFrame(step);
}
// マウス座標を取得(親基準)
canvas.addEventListener('pointermove', (e) => {
const r = canvas.getBoundingClientRect();
mouse.x = e.clientX - r.left;
mouse.y = e.clientY - r.top;
});
canvas.addEventListener('pointerleave', () => { mouse.x = mouse.y = -9999; });
window.addEventListener('resize', () => { resize(); particles = makeParticles(); });
requestAnimationFrame(step);
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。