Improvements to the cool graph
This commit is contained in:
parent
8a38d407c4
commit
842fb23275
@ -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();
|
||||
</script>
|
||||
|
||||
{#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}
|
||||
<CoolGraphNode {canvasX} {canvasY} {node} from={cur.from} count={cur.count} />
|
||||
<CoolGraphNode
|
||||
bind:hovering
|
||||
{canvasX}
|
||||
{canvasY}
|
||||
{node}
|
||||
from={cur.from}
|
||||
count={cur.count}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{/snippet}
|
||||
|
@ -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<string, number> = $state({});
|
||||
let particlesState: Record<string, Particle[]> = $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);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="shadow-sm shadow-violet-500 border rounded-full min-w-[50px] min-h-[50px] grid place-items-center bg-white z-40 absolute"
|
||||
style="left: {canvasX(node.x + node.width / 2)}px; top: {canvasY(node.y - node.height / 2)}px;"
|
||||
title={`${node?.name ?? ''}: ${count}\n${fromText}`}
|
||||
onmouseenter={() => {
|
||||
hovering = node;
|
||||
}}
|
||||
onmouseleave={() => {
|
||||
hovering = undefined;
|
||||
}}
|
||||
role="figure"
|
||||
>
|
||||
<span class={`bi bi-${node?.icon ?? ''}`}> </span>
|
||||
</div>
|
||||
{#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)}
|
||||
<div
|
||||
class="animateMove absolute w-2 h-2 text-black rounded-full shadow-lg z-20"
|
||||
style="--start-x: {canvasX(x(0)) + 25}px; --start-y: {canvasY(y(0)) + 25}px; --end-x: {canvasX(
|
||||
x(1)
|
||||
) + 25}px; --end-y: {canvasY(y(1)) + 25}px; background: {particle.color};"
|
||||
></div>
|
||||
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}
|
||||
<div
|
||||
class="animateMove absolute w-2 h-2 text-black rounded-full shadow-lg"
|
||||
style="--start-x: {particle.randXStart}%; --end-x: {particle.randXEnd}%; background: {!showColor
|
||||
? '#18181818'
|
||||
: particle.color};"
|
||||
></div>
|
||||
{/each}
|
||||
</div>
|
||||
{/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)}
|
||||
<div
|
||||
class="absolute bg-gradient-to-t from-blue-400/70 to-blue-200/70 z-10"
|
||||
style="height: {(25 / 2) *
|
||||
diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) +
|
||||
class="absolute {showColor
|
||||
? 'bg-gradient-to-t from-blue-400/70 to-blue-200/70'
|
||||
: 'bg-gray-800/20'} z-10"
|
||||
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);"
|
||||
></div>
|
||||
@ -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)}
|
||||
<div
|
||||
class="absolute bg-gradient-to-t from-violet-400/70 to-violet-200/70"
|
||||
class="absolute {showColor
|
||||
? 'bg-gradient-to-t from-violet-400/70 to-violet-200/70'
|
||||
: 'bg-gray-600/20'}"
|
||||
style="height: {(25 / 2) *
|
||||
diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) +
|
||||
25 -
|
||||
@ -156,7 +192,31 @@
|
||||
{@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)}
|
||||
<div
|
||||
class="absolute bg-gradient-to-t from-violet-400/50 to-violet-200/50"
|
||||
class="absolute {showColor
|
||||
? 'bg-gradient-to-t from-violet-400/50 to-violet-200/50'
|
||||
: 'bg-gray-400/20'}"
|
||||
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 / 4}px) rotate({theta}rad); z-index: 2"
|
||||
></div>
|
||||
{/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)}
|
||||
<div
|
||||
class="absolute {showColor
|
||||
? 'bg-gradient-to-t from-violet-200/50 to-violet-100/50'
|
||||
: 'bg-gray-400/20'}"
|
||||
style="height: {(25 / 2) *
|
||||
diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) +
|
||||
25 -
|
||||
@ -167,15 +227,15 @@
|
||||
|
||||
<style>
|
||||
.animateMove {
|
||||
animation: animateMoves 5s ease-in-out infinite;
|
||||
animation: animateMoves 5s ease-in infinite;
|
||||
}
|
||||
@keyframes animateMoves {
|
||||
0% {
|
||||
top: var(--start-y);
|
||||
top: 100%;
|
||||
left: var(--start-x);
|
||||
}
|
||||
100% {
|
||||
top: var(--end-y);
|
||||
top: 0%;
|
||||
left: var(--end-x);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user