| |
| |
| |
| <script> |
| export let size = 140; |
| export let color = '#DF9076'; |
| |
| $: cx = size / 2; |
| $: cy = size / 2; |
| $: outerR = size * 0.36; |
| $: nodeR = size * 0.058; |
| $: ringR = size * 0.13; |
| $: holeR = size * 0.07; |
| $: sw = size * 0.026; |
| |
| $: nodes = Array.from({ length: 6 }, (_, i) => { |
| const angle = (Math.PI / 3) * i - Math.PI / 6; |
| return { |
| x: cx + outerR * Math.cos(angle), |
| y: cy + outerR * Math.sin(angle), |
| delay: `${i * 0.14}s`, |
| }; |
| }); |
| |
| $: hexPoints = nodes.map(n => `${n.x.toFixed(2)},${n.y.toFixed(2)}`).join(' '); |
| |
| |
| $: hexPerim = 6 * outerR; |
| $: dashLen = hexPerim * 0.22; |
| $: dashGap = hexPerim - dashLen; |
| |
| |
| $: spokeLen = outerR; |
| |
| |
| $: ringCirc = 2 * Math.PI * ringR; |
| </script> |
|
|
| <div class="mac-loader" style="width:{size}px; height:{size}px;"> |
| <svg |
| width={size} |
| height={size} |
| viewBox="0 0 {size} {size}" |
| fill="none" |
| xmlns="http://www.w3.org/2000/svg" |
| > |
| |
| <polygon |
| points={hexPoints} |
| stroke={color} |
| stroke-width={sw * 0.6} |
| fill="none" |
| stroke-opacity="0.15" |
| /> |
|
|
| |
| <polygon |
| class="hex-sweep" |
| points={hexPoints} |
| stroke={color} |
| stroke-width={sw * 0.9} |
| fill="none" |
| stroke-dasharray="{dashLen} {dashGap}" |
| style="--perim: {hexPerim}; animation-duration: 2.4s;" |
| /> |
|
|
| |
| {#each nodes as n, i} |
| <line |
| class="spoke" |
| x1={cx} y1={cy} |
| x2={n.x} y2={n.y} |
| stroke={color} |
| stroke-width={sw * 0.5} |
| stroke-opacity="0.2" |
| stroke-linecap="round" |
| /> |
| |
| <line |
| class="spoke-pulse" |
| x1={cx} y1={cy} |
| x2={n.x} y2={n.y} |
| stroke={color} |
| stroke-width={sw * 0.7} |
| stroke-linecap="round" |
| stroke-dasharray="{spokeLen * 0.18} {spokeLen}" |
| style="animation-delay: {n.delay}; --spoke-len: {spokeLen};" |
| /> |
| {/each} |
|
|
| |
| {#each nodes as n, i} |
| |
| <circle |
| class="node-glow" |
| cx={n.x} cy={n.y} |
| r={nodeR * 1.9} |
| fill={color} |
| fill-opacity="0" |
| style="animation-delay: {n.delay};" |
| /> |
| |
| <circle |
| class="node-dot" |
| cx={n.x} cy={n.y} |
| r={nodeR} |
| fill={color} |
| fill-opacity="0.4" |
| style="animation-delay: {n.delay};" |
| /> |
| {/each} |
|
|
| |
| <circle |
| cx={cx} cy={cy} |
| r={ringR} |
| stroke={color} |
| stroke-width={sw * 0.7} |
| fill="none" |
| stroke-opacity="0.15" |
| /> |
| <circle |
| class="center-arc" |
| cx={cx} cy={cy} |
| r={ringR} |
| stroke={color} |
| stroke-width={sw * 1.1} |
| fill="none" |
| stroke-dasharray="{ringCirc * 0.3} {ringCirc * 0.7}" |
| style="--circ: {ringCirc};" |
| /> |
|
|
| |
| <circle |
| class="center-core" |
| cx={cx} cy={cy} |
| r={holeR} |
| fill={color} |
| /> |
| </svg> |
| </div> |
|
|
| <style> |
| .mac-loader { |
| display: inline-flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| |
| .hex-sweep { |
| transform-origin: 50% 50%; |
| animation: hexSweep 2.4s linear infinite; |
| } |
| @keyframes hexSweep { |
| from { stroke-dashoffset: 0; } |
| to { stroke-dashoffset: calc(var(--perim) * -1px); } |
| } |
| |
| |
| .spoke-pulse { |
| animation: spokePulse 1.8s ease-in-out infinite; |
| } |
| @keyframes spokePulse { |
| 0% { stroke-dashoffset: 0; stroke-opacity: 0; } |
| 10% { stroke-opacity: 0.9; } |
| 80% { stroke-dashoffset: calc(var(--spoke-len) * -1px); stroke-opacity: 0; } |
| 100% { stroke-dashoffset: calc(var(--spoke-len) * -1px); stroke-opacity: 0; } |
| } |
| |
| |
| .node-glow { |
| animation: nodeGlow 1.8s ease-in-out infinite; |
| } |
| @keyframes nodeGlow { |
| 0%, 100% { fill-opacity: 0; r: 0; } |
| 40% { fill-opacity: 0.18; } |
| 60% { fill-opacity: 0.08; } |
| } |
| |
| |
| .node-dot { |
| animation: nodeDot 1.8s ease-in-out infinite; |
| } |
| @keyframes nodeDot { |
| 0%, 100% { fill-opacity: 0.25; } |
| 50% { fill-opacity: 1; } |
| } |
| |
| |
| .center-arc { |
| transform-origin: 50% 50%; |
| animation: centerSpin 1.2s linear infinite; |
| } |
| @keyframes centerSpin { |
| from { transform: rotate(-90deg); } |
| to { transform: rotate(270deg); } |
| } |
| |
| |
| .center-core { |
| animation: corePulse 1.8s ease-in-out infinite; |
| } |
| @keyframes corePulse { |
| 0%, 100% { fill-opacity: 0.7; transform: scale(1); } |
| 50% { fill-opacity: 1; transform: scale(1.15); } |
| } |
| </style> |
|
|