Show floating grain particles in water when grain_mass > 0
35 particles distributed across full pot height, clipped to dynamic water area via water-clip-rect updated alongside water fill level. Particle opacity transitions on grain_mass changes per step. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
This commit is contained in:
+21
@@ -320,6 +320,14 @@ function currentStepWaterMass() {
|
||||
return sudWaterMass;
|
||||
}
|
||||
|
||||
function currentStepGrainMass() {
|
||||
if (sudStepIndex !== null && sudStepIndex >= 0 && sudStepIndex < sudSchedule.length) {
|
||||
const step = sudSchedule[sudStepIndex];
|
||||
if ('grain_mass' in step) return step.grain_mass;
|
||||
}
|
||||
return sudGrainMass;
|
||||
}
|
||||
|
||||
function updatePotVisualization() {
|
||||
// Thermometer mercury column (0°C = y=174, 100°C = y=28, tube length = 146)
|
||||
const tubeBottom = 174;
|
||||
@@ -338,9 +346,14 @@ function updatePotVisualization() {
|
||||
const potInnerBottom = 172;
|
||||
const potInnerH = potInnerBottom - potInnerTop; // 142
|
||||
|
||||
const particlesEl = document.getElementById('grain-particles');
|
||||
const waterClipRect = document.getElementById('water-clip-rect');
|
||||
|
||||
if (sudEmpty || sudVolumen <= 0) {
|
||||
waterFillEl.setAttribute('height', '0');
|
||||
waterWaveEl.setAttribute('d', '');
|
||||
if (waterClipRect) waterClipRect.setAttribute('height', '0');
|
||||
if (particlesEl) particlesEl.style.opacity = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -350,6 +363,14 @@ function updatePotVisualization() {
|
||||
waterFillEl.setAttribute('y', waterTop.toFixed(1));
|
||||
waterFillEl.setAttribute('height', waterH.toFixed(1));
|
||||
waterWaveEl.setAttribute('d', wavePathD(63, 177, waterTop, 3, 28));
|
||||
|
||||
if (waterClipRect) {
|
||||
waterClipRect.setAttribute('y', waterTop.toFixed(1));
|
||||
waterClipRect.setAttribute('height', waterH.toFixed(1));
|
||||
}
|
||||
if (particlesEl) {
|
||||
particlesEl.style.opacity = currentStepGrainMass() > 0 ? '1' : '0';
|
||||
}
|
||||
}
|
||||
|
||||
// --- Status line - mirrors update_status_step_label(). ---
|
||||
|
||||
@@ -55,6 +55,9 @@
|
||||
<clipPath id="tube-clip">
|
||||
<rect x="215" y="28" width="8" height="146"/>
|
||||
</clipPath>
|
||||
<clipPath id="water-clip">
|
||||
<rect id="water-clip-rect" x="63" y="172" width="114" height="0"/>
|
||||
</clipPath>
|
||||
<!-- Flame gradients: solid at base (y=1), transparent at tip (y=0) -->
|
||||
<linearGradient id="fg-red" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop offset="0%" stop-color="#ff2200" stop-opacity="0.95"/>
|
||||
@@ -93,6 +96,45 @@
|
||||
fill="none" stroke="#4a9ed8" stroke-width="2"
|
||||
clip-path="url(#pot-clip)"/>
|
||||
|
||||
<!-- Grain particles (shown when grain_mass > 0, clipped to water area) -->
|
||||
<g id="grain-particles" clip-path="url(#water-clip)" opacity="0">
|
||||
<circle class="grain-p" cx="80" cy="165" r="2.5" style="--fp-dur:3.2s;--fp-del:-0.5s"/>
|
||||
<circle class="grain-p" cx="155" cy="158" r="2" style="--fp-dur:4.0s;--fp-del:-1.2s"/>
|
||||
<circle class="grain-p" cx="105" cy="150" r="2.5" style="--fp-dur:2.8s;--fp-del:-2.1s"/>
|
||||
<circle class="grain-p" cx="140" cy="143" r="2" style="--fp-dur:3.6s;--fp-del:-0.8s"/>
|
||||
<circle class="grain-p" cx="75" cy="135" r="2.5" style="--fp-dur:4.2s;--fp-del:-1.5s"/>
|
||||
<circle class="grain-p" cx="125" cy="125" r="2" style="--fp-dur:3.0s;--fp-del:-2.8s"/>
|
||||
<circle class="grain-p" cx="90" cy="115" r="2.5" style="--fp-dur:3.8s;--fp-del:-0.3s"/>
|
||||
<circle class="grain-p" cx="160" cy="105" r="2" style="--fp-dur:2.6s;--fp-del:-1.9s"/>
|
||||
<circle class="grain-p" cx="110" cy="90" r="2.5" style="--fp-dur:3.4s;--fp-del:-1.1s"/>
|
||||
<circle class="grain-p" cx="82" cy="75" r="2" style="--fp-dur:4.1s;--fp-del:-2.4s"/>
|
||||
<circle class="grain-p" cx="148" cy="68" r="2.5" style="--fp-dur:2.9s;--fp-del:-0.7s"/>
|
||||
<circle class="grain-p" cx="118" cy="55" r="2" style="--fp-dur:3.5s;--fp-del:-3.0s"/>
|
||||
<circle class="grain-p" cx="72" cy="48" r="2.5" style="--fp-dur:4.3s;--fp-del:-1.6s"/>
|
||||
<circle class="grain-p" cx="163" cy="40" r="2" style="--fp-dur:3.1s;--fp-del:-2.2s"/>
|
||||
<circle class="grain-p" cx="118" cy="168" r="2" style="--fp-dur:3.7s;--fp-del:-0.9s"/>
|
||||
<circle class="grain-p" cx="170" cy="162" r="2.5" style="--fp-dur:2.7s;--fp-del:-2.5s"/>
|
||||
<circle class="grain-p" cx="68" cy="155" r="2" style="--fp-dur:4.4s;--fp-del:-1.0s"/>
|
||||
<circle class="grain-p" cx="133" cy="148" r="2.5" style="--fp-dur:3.3s;--fp-del:-2.9s"/>
|
||||
<circle class="grain-p" cx="95" cy="140" r="2" style="--fp-dur:2.5s;--fp-del:-0.4s"/>
|
||||
<circle class="grain-p" cx="165" cy="132" r="2.5" style="--fp-dur:3.9s;--fp-del:-1.7s"/>
|
||||
<circle class="grain-p" cx="78" cy="122" r="2" style="--fp-dur:4.5s;--fp-del:-2.3s"/>
|
||||
<circle class="grain-p" cx="145" cy="112" r="2.5" style="--fp-dur:3.0s;--fp-del:-0.6s"/>
|
||||
<circle class="grain-p" cx="100" cy="100" r="2" style="--fp-dur:2.7s;--fp-del:-1.4s"/>
|
||||
<circle class="grain-p" cx="170" cy="88" r="2.5" style="--fp-dur:4.2s;--fp-del:-2.7s"/>
|
||||
<circle class="grain-p" cx="68" cy="80" r="2" style="--fp-dur:3.6s;--fp-del:-0.2s"/>
|
||||
<circle class="grain-p" cx="133" cy="70" r="2.5" style="--fp-dur:2.8s;--fp-del:-1.8s"/>
|
||||
<circle class="grain-p" cx="95" cy="60" r="2" style="--fp-dur:4.0s;--fp-del:-2.6s"/>
|
||||
<circle class="grain-p" cx="165" cy="52" r="2.5" style="--fp-dur:3.2s;--fp-del:-1.3s"/>
|
||||
<circle class="grain-p" cx="78" cy="42" r="2" style="--fp-dur:2.6s;--fp-del:-3.1s"/>
|
||||
<circle class="grain-p" cx="145" cy="35" r="2.5" style="--fp-dur:3.8s;--fp-del:-0.8s"/>
|
||||
<circle class="grain-p" cx="112" cy="130" r="2" style="--fp-dur:4.1s;--fp-del:-2.0s"/>
|
||||
<circle class="grain-p" cx="88" cy="97" r="2.5" style="--fp-dur:3.4s;--fp-del:-1.5s"/>
|
||||
<circle class="grain-p" cx="155" cy="78" r="2" style="--fp-dur:2.9s;--fp-del:-0.1s"/>
|
||||
<circle class="grain-p" cx="70" cy="62" r="2.5" style="--fp-dur:4.3s;--fp-del:-2.4s"/>
|
||||
<circle class="grain-p" cx="138" cy="45" r="2" style="--fp-dur:3.5s;--fp-del:-1.0s"/>
|
||||
</g>
|
||||
|
||||
<!-- Pot outline (transparent walls) -->
|
||||
<line x1="60" y1="28" x2="60" y2="174" stroke="#bbb" stroke-width="4" stroke-linecap="round"/>
|
||||
<line x1="180" y1="28" x2="180" y2="174" stroke="#bbb" stroke-width="4" stroke-linecap="round"/>
|
||||
|
||||
@@ -153,6 +153,19 @@ header#connection-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Grain particle animation */
|
||||
@keyframes float-p {
|
||||
0%, 100% { transform: translate(0px, 0px); }
|
||||
25% { transform: translate(1.5px, -3px); }
|
||||
50% { transform: translate(-1px, 2px); }
|
||||
75% { transform: translate(2px, -1.5px); }
|
||||
}
|
||||
.grain-p {
|
||||
fill: #c8a060;
|
||||
animation: float-p var(--fp-dur, 3s) ease-in-out infinite var(--fp-del, 0s);
|
||||
}
|
||||
#grain-particles { transition: opacity 0.5s; }
|
||||
|
||||
/* Stirrer animation */
|
||||
@keyframes stir-rotate {
|
||||
to { transform: rotate(360deg); }
|
||||
|
||||
Reference in New Issue
Block a user