/* IMPORT */ import {$, render, useInterval, useAnimationLoop, useMemo} from 'voby'; import type {Observable, ObservableReadonly} from 'voby'; /* HELPERS */ const RADIUS = 25; /* MAIN */ const useSeconds = (): Observable => { const seconds = $(0); useInterval ( () => { // Artificially long blocking delay const future = performance.now () + 0.8; while ( performance.now () < future ) {} seconds ( ( seconds () % 9 ) + 1 ); }, 1000 ); return seconds; }; const useElapsed = (): Observable => { const elapsed = $(0); const start = Date.now (); useAnimationLoop ( () => elapsed ( Date.now () - start ) ); return elapsed; }; const useScale = ( elapsed: Observable ): ObservableReadonly => { return useMemo ( () => { const e = elapsed () / 1000 % 10; return 1 + ( e > 5 ? 10 - e : e ) / 10; }); }; const Dot = ({ x, y, s, text }: { x: number, y: number, s: number, text: Observable }): JSX.Element => { const hovering = $(false); const onMouseEnter = () => hovering ( true ); const onMouseLeave = () => hovering ( false ); s = s * 1.3; const width = s; const height = s; const left = x; const top = y; const borderRadius = s / 2; const lineHeight = `${s}px`; const background = () => hovering () ? '#ffff00' : '#61dafb'; const style = { width, height, left, top, borderRadius, lineHeight, background }; return (
{() => hovering () ? `**${text ()}**` : text ()}
); }; const Triangle = ({ x, y, s, seconds }: { x: number, y: number; s: number, seconds: Observable }): JSX.Element => { if ( s <= RADIUS ) { return ; } else { s = s / 2; return ( <> ); } }; const SierpinskiTriangle = (): JSX.Element => { const seconds = useSeconds (); const elapsed = useElapsed (); const scale = useScale ( elapsed ); const transform = () => `scaleX(${scale () / 3}) scaleY(.5) translateZ(0.1px)`; return (
); }; /* RENDER */ render ( , document.getElementById ( 'app' ) );