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 { PrevType } from './types'; | ||||||
| 	import { type EventsStat } from '../utils'; | 	import { type EventsStat } from '../utils'; | ||||||
| 	import CoolGraphNode from './CoolGraphNode.svelte'; | 	import CoolGraphNode from './CoolGraphNode.svelte'; | ||||||
|  | 	import type { Node } from '../../flow/types'; | ||||||
| 
 | 
 | ||||||
| 	let { | 	let { | ||||||
| 		events | 		events | ||||||
| @ -30,9 +31,6 @@ | |||||||
| 		return Object.values(appState); | 		return Object.values(appState); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	let figHeight = $state(0); |  | ||||||
| 	let figWidth = $state(0); |  | ||||||
| 
 |  | ||||||
| 	type Pos = { | 	type Pos = { | ||||||
| 		x: number; | 		x: number; | ||||||
| 		y: number; | 		y: number; | ||||||
| @ -163,7 +161,7 @@ | |||||||
| 		return levels; | 		return levels; | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	const levelSize = $derived(Math.floor(figWidth / posByDepth.length)); | 	let hovering: Node | undefined = $state(); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| {#if statusStore.nodesR && posByDepth.length > 0} | {#if statusStore.nodesR && posByDepth.length > 0} | ||||||
| @ -184,9 +182,16 @@ | |||||||
| 				{#snippet children(canvasX, canvasY)} | 				{#snippet children(canvasX, canvasY)} | ||||||
| 					{#each Object.keys(posByDepth[depth]) as nodeId} | 					{#each Object.keys(posByDepth[depth]) as nodeId} | ||||||
| 						{@const cur = posByDepth[depth][nodeId]} | 						{@const cur = posByDepth[depth][nodeId]} | ||||||
| 						{@const node = statusStore.nodesR[nodeId === 'null' ? (null as any) : nodeId]} | 						{@const node = statusStore.nodesR[nodeId]} | ||||||
| 						{#if node} | 						{#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} | 						{/if} | ||||||
| 					{/each} | 					{/each} | ||||||
| 				{/snippet} | 				{/snippet} | ||||||
|  | |||||||
| @ -10,13 +10,15 @@ | |||||||
| 		canvasX, | 		canvasX, | ||||||
| 		canvasY, | 		canvasY, | ||||||
| 		count, | 		count, | ||||||
| 		from | 		from, | ||||||
|  | 		hovering = $bindable() | ||||||
| 	}: { | 	}: { | ||||||
| 		node: Node; | 		node: Node; | ||||||
| 		canvasX: FuncBase; | 		canvasX: FuncBase; | ||||||
| 		canvasY: FuncBase; | 		canvasY: FuncBase; | ||||||
| 		from: PrevType; | 		from: PrevType; | ||||||
| 		count: number; | 		count: number; | ||||||
|  | 		hovering: Node | undefined; | ||||||
| 	} = $props(); | 	} = $props(); | ||||||
| 
 | 
 | ||||||
| 	let fromText = $derived( | 	let fromText = $derived( | ||||||
| @ -31,37 +33,42 @@ | |||||||
| 		to: string; | 		to: string; | ||||||
| 		percentage: number; | 		percentage: number; | ||||||
| 		rand: number; | 		rand: number; | ||||||
| 		randX: number; | 		randXStart: number; | ||||||
| 		randY: number; | 		randXEnd: number; | ||||||
| 		color: string; | 		color: string; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	let particles: Particle[] = $state([]); | 	let particlesState: Record<string, Particle[]> = $state({}); | ||||||
| 	let particlesState: Record<string, number> = $state({}); | 	let particleCount = 0; | ||||||
| 
 | 
 | ||||||
| 	let MAX_PARTICLES = 200; | 	let MAX_PARTICLES = 400; | ||||||
| 
 | 
 | ||||||
| 	function spwanParticle(key: string) { | 	function spwanParticle(key: string) { | ||||||
| 		if (particles.length > MAX_PARTICLES) return; | 		if (particleCount > MAX_PARTICLES) return; | ||||||
| 		if (from.cur[key] <= particlesState[key]) { | 		if (from.cur[key] <= (particlesState[key] ?? []).length) { | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		particles.push({ | 		const toAdd: Particle = { | ||||||
| 			percentage: 0, | 			percentage: 0, | ||||||
| 			to: key, | 			to: key, | ||||||
| 			rand: Math.random(), | 			rand: Math.random(), | ||||||
| 			randX: Math.random(), | 			randXStart: Math.floor(Math.random() * 100), | ||||||
| 			randY: Math.random(), | 			randXEnd: Math.floor(Math.random() * 100), | ||||||
| 			color: [ | 			color: [ | ||||||
| 				'oklch(68.5% 0.169 237.323)', | 				'oklch(68.5% 0.169 237.323)', | ||||||
| 				'oklch(62.3% 0.214 259.815)', | 				'oklch(62.3% 0.214 259.815)', | ||||||
| 				'oklch(62.7% 0.265 303.9)' | 				'oklch(62.7% 0.265 303.9)' | ||||||
| 			][Math.floor(Math.random() * 3)] | 			][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; | 	let timeout: number | undefined = undefined; | ||||||
| 
 | 
 | ||||||
| 	function t(k: number) { | 	function t(k: number) { | ||||||
| @ -72,39 +79,64 @@ | |||||||
| 		for (const key of Object.keys(from.cur)) { | 		for (const key of Object.keys(from.cur)) { | ||||||
| 			spwanParticle(key); | 			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(() => { | 	$effect(() => { | ||||||
| 		if (timeout !== undefined) clearTimeout(timeout); | 		if (timeout !== undefined) clearTimeout(timeout); | ||||||
|  | 		// this is used for making this effect block dependednt on form | ||||||
|  | 		// eslint-disable-next-line | ||||||
| 		const _ = from; | 		const _ = from; | ||||||
| 		particles = []; | 		particlesState = {}; | ||||||
| 		timeout = setTimeout(() => t(0), 10) as unknown as number; | 		timeout = setTimeout(() => t(0), 10) as unknown as number; | ||||||
| 		return () => { | 		return () => { | ||||||
| 			if (timeout !== undefined) clearTimeout(timeout); | 			if (timeout !== undefined) clearTimeout(timeout); | ||||||
| 		}; | 		}; | ||||||
| 	}); | 	}); | ||||||
|  | 
 | ||||||
|  | 	let showColor = $derived(hovering === undefined || hovering === node); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <div | <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" | 	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;" | 	style="left: {canvasX(node.x + node.width / 2)}px; top: {canvasY(node.y - node.height / 2)}px;" | ||||||
| 	title={`${node?.name ?? ''}: ${count}\n${fromText}`} | 	title={`${node?.name ?? ''}: ${count}\n${fromText}`} | ||||||
|  | 	onmouseenter={() => { | ||||||
|  | 		hovering = node; | ||||||
|  | 	}} | ||||||
|  | 	onmouseleave={() => { | ||||||
|  | 		hovering = undefined; | ||||||
|  | 	}} | ||||||
|  | 	role="figure" | ||||||
| > | > | ||||||
| 	<span class={`bi bi-${node?.icon ?? ''}`}> </span> | 	<span class={`bi bi-${node?.icon ?? ''}`}> </span> | ||||||
| </div> | </div> | ||||||
| {#each particles as particle} | 
 | ||||||
| 	{@const to = statusStore.nodesR[particle.to]} | {#each Object.keys(particlesState) as key} | ||||||
| 	{@const f_x = to.x + to.width / 2 + particle.randX} | 	{@const size = 20} | ||||||
| 	{@const f_y = to.y - to.height / 2 + particle.randY} | 	{@const to = statusStore.nodesR[key]} | ||||||
| 	{@const x = (p: number) => f_x + (node.x + node.width / 2 - f_x) * p} | 	{@const f_x = node.x + node.width / 2} | ||||||
| 	{@const y = (p: number) => f_y + (node.y - node.height / 2 - f_y) * p} | 	{@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 | 	<div | ||||||
| 		class="animateMove absolute w-2 h-2 text-black rounded-full shadow-lg z-20" | 		class="absolute z-30" | ||||||
| 		style="--start-x: {canvasX(x(0)) + 25}px; --start-y: {canvasY(y(0)) + 25}px; --end-x: {canvasX( | 		style="height: {(25 / 2) * | ||||||
| 			x(1) | 			diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) + | ||||||
| 		) + 25}px; --end-y: {canvasY(y(1)) + 25}px; background: {particle.color};" | 			25 - | ||||||
| 	></div> | 			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} | ||||||
| 
 | 
 | ||||||
| {#each Object.keys(from.cur) as link} | {#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 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)} | 	{@const theta = Math.atan2(f_x - t_x, f_y - t_y)} | ||||||
| 	<div | 	<div | ||||||
| 		class="absolute bg-gradient-to-t from-blue-400/70 to-blue-200/70 z-10" | 		class="absolute {showColor | ||||||
| 		style="height: {(25 / 2) * | 			? 'bg-gradient-to-t from-blue-400/70 to-blue-200/70' | ||||||
| 			diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) + | 			: '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 - | 			25 - | ||||||
| 			size / 2}px, {canvasY(f_y) + 25 + size / 8}px) rotate({theta}rad);" | 			size / 2}px, {canvasY(f_y) + 25 + size / 8}px) rotate({theta}rad);" | ||||||
| 	></div> | 	></div> | ||||||
| @ -136,7 +170,9 @@ | |||||||
| 		{@const diff = Math.sqrt((f_x - t_x) ** 2 + (f_y - t_y) ** 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)} | 		{@const theta = Math.atan2(f_x - t_x, f_y - t_y)} | ||||||
| 		<div | 		<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) * | 			style="height: {(25 / 2) * | ||||||
| 				diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) + | 				diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) + | ||||||
| 				25 - | 				25 - | ||||||
| @ -156,7 +192,31 @@ | |||||||
| 		{@const diff = Math.sqrt((f_x - t_x) ** 2 + (f_y - t_y) ** 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)} | 		{@const theta = Math.atan2(f_x - t_x, f_y - t_y)} | ||||||
| 		<div | 		<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) * | 			style="height: {(25 / 2) * | ||||||
| 				diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) + | 				diff}px; width: {size}px; transform-origin: top center; transform: translate({canvasX(f_x) + | ||||||
| 				25 - | 				25 - | ||||||
| @ -167,15 +227,15 @@ | |||||||
| 
 | 
 | ||||||
| <style> | <style> | ||||||
| 	.animateMove { | 	.animateMove { | ||||||
| 		animation: animateMoves 5s ease-in-out infinite; | 		animation: animateMoves 5s ease-in infinite; | ||||||
| 	} | 	} | ||||||
| 	@keyframes animateMoves { | 	@keyframes animateMoves { | ||||||
| 		0% { | 		0% { | ||||||
| 			top: var(--start-y); | 			top: 100%; | ||||||
| 			left: var(--start-x); | 			left: var(--start-x); | ||||||
| 		} | 		} | ||||||
| 		100% { | 		100% { | ||||||
| 			top: var(--end-y); | 			top: 0%; | ||||||
| 			left: var(--end-x); | 			left: var(--end-x); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user