File size: 2,887 Bytes
fc69895
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<script lang="ts">
	import { onDestroy } from "svelte";

	let { animating = false, classNames = "" } = $props();

	let blobAnim: SVGAnimateElement | undefined = $state();
	let svgEl: SVGSVGElement | undefined = $state();

	// Only trigger begin/end on transitions, and pause when not animating
	let prevAnimating: boolean | undefined = undefined;
	let prevBlobAnim: SVGAnimateElement | undefined = undefined;

	$effect(() => {
		if (!blobAnim) return;
		const blobChanged = blobAnim !== prevBlobAnim;
		const animChanged = animating !== prevAnimating;
		if (!(blobChanged || animChanged)) return;

		if (animating) {
			// Resume animations and start once
			svgEl?.unpauseAnimations?.();
			blobAnim.beginElement();
		} else {
			// Stop current run and pause so it cannot restart from queued begins
			blobAnim.endElement();
			svgEl?.pauseAnimations?.();
		}
		prevAnimating = animating;
		prevBlobAnim = blobAnim;
	});

	onDestroy(() => {
		blobAnim?.endElement();
		svgEl?.pauseAnimations?.();
	});
</script>

<svg
	bind:this={svgEl}
	class={classNames}
	id="ball"
	width="1em"
	height="1em"
	viewBox="0 0 12 12"
	fill="none"
	xmlns="http://www.w3.org/2000/svg"
	aria-label="Ball mask"
>
	<g clip-path="url(#a)">
		<!-- circular mask -->
		<path d="M12 6A6 6 0 1 0 0 6a6 6 0 0 0 12 0Z" fill="#fff" />
		<mask id="b" style="mask-type:luminance" x="0" y="0" width="12" height="12">
			<path d="M12 6A6 6 0 1 0 0 6a6 6 0 0 0 12 0Z" fill="#fff" />
		</mask>

		<!-- the blurred black shape inside the circular mask -->
		<g filter="url(#c)" mask="url(#b)">
			<!-- BASE state (normalized to absolute L commands) -->
			<path id="blob" fill="#000" d="M11 1 L8 -4 L3 -8 L-6 6 L3 12 L7 11 L6 2 L11 1 Z">
				<!-- MORPH: base -> mid -> far -> mid -> base -->
				<animate
					bind:this={blobAnim}
					attributeName="d"
					begin="indefinite"
					end="indefinite"
					dur="3.2s"
					repeatCount="indefinite"
					fill="remove"
					calcMode="spline"
					keyTimes="0; .33; .66; .9; 1"
					keySplines="
            .4 0 .2 1;
            .4 0 .2 1;
            .4 0 .2 1;
            .4 0 .2 1"
					values="
            M11 1 L8 -4 L3 -8 L-6 6 L3 12 L7 11 L6 2 L11 1 Z;
            M11 1 L8 -4 L3 -8 L-6 6 L3 12 L5 9  L7 4  L11 1 Z;
            M11 1 L8 -4 L3 -8 L-6 6 L3 12 L3 6  L5 1  L11 1 Z;
            M11 1 L8 -4 L3 -8 L-6 6 L3 12 L5 9  L7 4  L11 1 Z;
            M11 1 L8 -4 L3 -8 L-6 6 L3 12 L7 11 L6 2 L11 1 Z"
				/>
			</path>
		</g>
	</g>

	<defs>
		<clipPath id="a"><path fill="#fff" d="M0 0h12v12H0z" /></clipPath>
		<filter
			id="c"
			x="-9.4"
			y="-10.8"
			width="23.8"
			height="26"
			filterUnits="userSpaceOnUse"
			color-interpolation-filters="sRGB"
		>
			<feFlood flood-opacity="0" result="BackgroundImageFix" />
			<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
			<feGaussianBlur stdDeviation="1.6" />
		</filter>
	</defs>
</svg>