All checks were successful
deploy / build-and-deploy (push) Successful in 31s
138 lines
3.6 KiB
HTML
138 lines
3.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Particle Network — demo</title>
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
body {
|
|
background: #040e1f;
|
|
color: #a0c4f0;
|
|
font-family: system-ui, sans-serif;
|
|
font-size: 13px;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 16px;
|
|
padding: 24px;
|
|
}
|
|
|
|
.canvases {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px;
|
|
width: 100%;
|
|
max-width: 960px;
|
|
}
|
|
|
|
.canvas-wrap {
|
|
position: relative;
|
|
border: 0.5px solid rgba(80, 140, 220, 0.25);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
canvas { display: block; width: 100%; }
|
|
|
|
.label {
|
|
position: absolute;
|
|
top: 10px; left: 12px;
|
|
font-size: 11px;
|
|
color: rgba(120, 180, 255, 0.45);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.controls {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px 20px;
|
|
align-items: center;
|
|
width: 100%;
|
|
max-width: 960px;
|
|
}
|
|
|
|
.controls label { color: rgba(160, 200, 255, 0.6); }
|
|
.controls input[type=range] { width: 100px; accent-color: #5090d0; }
|
|
|
|
.dim-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
width: 100%;
|
|
}
|
|
.dim-row input { flex: 1; max-width: 300px; }
|
|
.dim-row span { min-width: 36px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="canvases">
|
|
<div class="canvas-wrap">
|
|
<canvas id="mainCanvas"></canvas>
|
|
<div class="label">particle network</div>
|
|
</div>
|
|
<div class="canvas-wrap">
|
|
<canvas id="fieldCanvas"></canvas>
|
|
<div class="label">noise field</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="controls">
|
|
<label>nodes</label>
|
|
<input type="range" id="sCount" min="20" max="100" value="55" step="1">
|
|
|
|
<label>reach</label>
|
|
<input type="range" id="sReach" min="60" max="260" value="140" step="1">
|
|
|
|
<label>flow</label>
|
|
<input type="range" id="sSpeed" min="1" max="10" value="2" step="1">
|
|
|
|
<label>scale</label>
|
|
<input type="range" id="sScale" min="1" max="10" value="4" step="1">
|
|
|
|
<div class="dim-row">
|
|
<label>dim at minimum</label>
|
|
<input type="range" id="sDim" min="0" max="100" value="60" step="1">
|
|
<span id="sDimOut">60%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="module">
|
|
import { ParticleNetwork } from './particleNetwork.js';
|
|
|
|
const net = new ParticleNetwork(
|
|
document.getElementById('mainCanvas'),
|
|
document.getElementById('fieldCanvas'),
|
|
{ count: 55, reach: 140, flowSpeed: 2, fieldScale: 4, dimAtMin: 0.60 }
|
|
);
|
|
|
|
net.start();
|
|
|
|
// Wire up controls
|
|
document.getElementById('sCount').addEventListener('input', e =>
|
|
net.setOption('count', parseInt(e.target.value)));
|
|
|
|
document.getElementById('sReach').addEventListener('input', e =>
|
|
net.setOption('reach', parseInt(e.target.value)));
|
|
|
|
document.getElementById('sSpeed').addEventListener('input', e =>
|
|
net.setOption('flowSpeed', parseFloat(e.target.value)));
|
|
|
|
document.getElementById('sScale').addEventListener('input', e =>
|
|
net.setOption('fieldScale', parseFloat(e.target.value)));
|
|
|
|
const dimSlider = document.getElementById('sDim');
|
|
const dimOut = document.getElementById('sDimOut');
|
|
dimSlider.addEventListener('input', e => {
|
|
const v = parseInt(e.target.value);
|
|
dimOut.textContent = v + '%';
|
|
net.setOption('dimAtMin', v / 100);
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|