From 842fb23275bd3953e94c554630aad3fd527c477c Mon Sep 17 00:00:00 2001 From: Andre Henriques Date: Wed, 9 Apr 2025 19:29:16 +0100 Subject: [PATCH] Improvements to the cool graph --- .../routes/graphs/CoolGraph/CoolGraph.svelte | 17 ++- .../graphs/CoolGraph/CoolGraphNode.svelte | 130 +++++++++++++----- 2 files changed, 106 insertions(+), 41 deletions(-) diff --git a/site/src/routes/graphs/CoolGraph/CoolGraph.svelte b/site/src/routes/graphs/CoolGraph/CoolGraph.svelte index 613837e..b33be09 100644 --- a/site/src/routes/graphs/CoolGraph/CoolGraph.svelte +++ b/site/src/routes/graphs/CoolGraph/CoolGraph.svelte @@ -4,6 +4,7 @@ import type { PrevType } from './types'; import { type EventsStat } from '../utils'; import CoolGraphNode from './CoolGraphNode.svelte'; + import type { Node } from '../../flow/types'; let { events @@ -30,9 +31,6 @@ return Object.values(appState); }); - let figHeight = $state(0); - let figWidth = $state(0); - type Pos = { x: number; y: number; @@ -163,7 +161,7 @@ return levels; }); - const levelSize = $derived(Math.floor(figWidth / posByDepth.length)); + let hovering: Node | undefined = $state(); {#if statusStore.nodesR && posByDepth.length > 0} @@ -184,9 +182,16 @@ {#snippet children(canvasX, canvasY)} {#each Object.keys(posByDepth[depth]) as nodeId} {@const cur = posByDepth[depth][nodeId]} - {@const node = statusStore.nodesR[nodeId === 'null' ? (null as any) : nodeId]} + {@const node = statusStore.nodesR[nodeId]} {#if node} - + {/if} {/each} {/snippet} diff --git a/site/src/routes/graphs/CoolGraph/CoolGraphNode.svelte b/site/src/routes/graphs/CoolGraph/CoolGraphNode.svelte index d63d3e8..8f84dfe 100644 --- a/site/src/routes/graphs/CoolGraph/CoolGraphNode.svelte +++ b/site/src/routes/graphs/CoolGraph/CoolGraphNode.svelte @@ -10,13 +10,15 @@ canvasX, canvasY, count, - from + from, + hovering = $bindable() }: { node: Node; canvasX: FuncBase; canvasY: FuncBase; from: PrevType; count: number; + hovering: Node | undefined; } = $props(); let fromText = $derived( @@ -31,37 +33,42 @@ to: string; percentage: number; rand: number; - randX: number; - randY: number; + randXStart: number; + randXEnd: number; color: string; }; - let particles: Particle[] = $state([]); - let particlesState: Record = $state({}); + let particlesState: Record = $state({}); + let particleCount = 0; - let MAX_PARTICLES = 200; + let MAX_PARTICLES = 400; function spwanParticle(key: string) { - if (particles.length > MAX_PARTICLES) return; - if (from.cur[key] <= particlesState[key]) { + if (particleCount > MAX_PARTICLES) return; + if (from.cur[key] <= (particlesState[key] ?? []).length) { return; } - particles.push({ + const toAdd: Particle = { percentage: 0, to: key, rand: Math.random(), - randX: Math.random(), - randY: Math.random(), + randXStart: Math.floor(Math.random() * 100), + randXEnd: Math.floor(Math.random() * 100), color: [ 'oklch(68.5% 0.169 237.323)', 'oklch(62.3% 0.214 259.815)', 'oklch(62.7% 0.265 303.9)' ][Math.floor(Math.random() * 3)] - }); - particlesState[key] = (particlesState[key] ?? 0) + 1; + }; + if (particlesState[key]) { + particlesState[key].push(toAdd); + } else { + particlesState[key] = [toAdd]; + } + particleCount++; } - const MAX_ROUNDS = 40; + const MAX_ROUNDS = 200; let timeout: number | undefined = undefined; function t(k: number) { @@ -72,39 +79,64 @@ for (const key of Object.keys(from.cur)) { spwanParticle(key); } - timeout = setTimeout(() => t(k + 1), Math.random() * 1500) as unknown as number; + timeout = setTimeout(() => t(k + 1), Math.random() * 1000) as unknown as number; } $effect(() => { if (timeout !== undefined) clearTimeout(timeout); + // this is used for making this effect block dependednt on form + // eslint-disable-next-line const _ = from; - particles = []; + particlesState = {}; timeout = setTimeout(() => t(0), 10) as unknown as number; return () => { if (timeout !== undefined) clearTimeout(timeout); }; }); + + let showColor = $derived(hovering === undefined || hovering === node);
{ + hovering = node; + }} + onmouseleave={() => { + hovering = undefined; + }} + role="figure" >
-{#each particles as particle} - {@const to = statusStore.nodesR[particle.to]} - {@const f_x = to.x + to.width / 2 + particle.randX} - {@const f_y = to.y - to.height / 2 + particle.randY} - {@const x = (p: number) => f_x + (node.x + node.width / 2 - f_x) * p} - {@const y = (p: number) => f_y + (node.y - node.height / 2 - f_y) * p} + +{#each Object.keys(particlesState) as key} + {@const size = 20} + {@const to = statusStore.nodesR[key]} + {@const f_x = node.x + node.width / 2} + {@const f_y = node.y - node.height / 2} + {@const t_x = to.x + to.width / 2} + {@const t_y = to.y - to.height / 2} + {@const diff = Math.sqrt((f_x - t_x) ** 2 + (f_y - t_y) ** 2)} + {@const theta = Math.atan2(f_x - t_x, f_y - t_y)}
+ class="absolute z-30" + style="height: {(25 / 2) * + diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) + + 25 - + size / 2}px, {canvasY(f_y) + 25 + size / 8}px) rotate({theta}rad);" + > + {#each particlesState[key] as particle} +
+ {/each} + {/each} {#each Object.keys(from.cur) as link} @@ -117,9 +149,11 @@ {@const diff = Math.sqrt((f_x - t_x) ** 2 + (f_y - t_y) ** 2)} {@const theta = Math.atan2(f_x - t_x, f_y - t_y)}
@@ -136,7 +170,9 @@ {@const diff = Math.sqrt((f_x - t_x) ** 2 + (f_y - t_y) ** 2)} {@const theta = Math.atan2(f_x - t_x, f_y - t_y)}
+ {/each} +{/if} + +{#if from.prev && from.prev.prev && from.prev.prev.prev} + {#each Object.keys(from.prev.prev.prev.cur) as link} + {@const size = 2.5} + {@const to = statusStore.nodesR[link]} + {@const f_x = node.x + node.width / 2} + {@const f_y = node.y - node.height / 2} + {@const t_x = to.x + to.width / 2} + {@const t_y = to.y - to.height / 2} + {@const diff = Math.sqrt((f_x - t_x) ** 2 + (f_y - t_y) ** 2)} + {@const theta = Math.atan2(f_x - t_x, f_y - t_y)} +