@yeonseong/magic-loading 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# magic-loading
|
|
2
|
+
|
|
3
|
+
Turn boring loading spinners into unique fantasy magic circles.
|
|
4
|
+
|
|
5
|
+
Each text string deterministically generates a distinct magic circle with runes, alchemical symbols, concentric rings, and polygons — all rendered as resolution-independent SVG.
|
|
6
|
+
|
|
7
|
+
[Live Demo](https://yeonseong-lee.github.io/magic-loading/)
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Deterministic** — same text always produces the same magic circle
|
|
14
|
+
- **Framework-agnostic** — Web Component + Programmatic API
|
|
15
|
+
- **Lightweight** — ~5KB gzipped, zero dependencies
|
|
16
|
+
- **SVG-based** — crisp at any resolution
|
|
17
|
+
- **Animated** — determinate progress, indeterminate pulsing, burst completion
|
|
18
|
+
- **Customizable** — CSS custom properties for color override
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @yeonseong/magic-loading
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Import
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
// ESM
|
|
32
|
+
import '@yeonseong/magic-loading';
|
|
33
|
+
|
|
34
|
+
// CommonJS
|
|
35
|
+
require('@yeonseong/magic-loading');
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Use as Web Component
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<magic-loading text="Loading..." progress="0.5" size="120"></magic-loading>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
That's it! The component auto-registers on import.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
### Web Component
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<script type="module">
|
|
54
|
+
import '@yeonseong/magic-loading';
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<!-- Determinate: progress bar style (0~1) -->
|
|
58
|
+
<magic-loading text="Uploading file" progress="0.7" size="150"></magic-loading>
|
|
59
|
+
|
|
60
|
+
<!-- Indeterminate: pulsating animation (omit progress) -->
|
|
61
|
+
<magic-loading text="Connecting..." size="150"></magic-loading>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Attributes
|
|
65
|
+
|
|
66
|
+
| Attribute | Type | Default | Description |
|
|
67
|
+
|-----------|------|---------|-------------|
|
|
68
|
+
| `text` | `string` | — | Text to hash into a magic circle (required) |
|
|
69
|
+
| `progress` | `number \| null` | `null` | Progress 0–1. Omit for indeterminate mode |
|
|
70
|
+
| `size` | `number` | `120` | Width/height in pixels |
|
|
71
|
+
|
|
72
|
+
- **`text`** — Different text produces a completely different magic circle. Use it to visually distinguish loading states (e.g. `"Uploading"` vs `"Processing"`).
|
|
73
|
+
- **`progress`** — Set `0` to `1` to animate layers sequentially. Omit entirely for a looping pulse animation.
|
|
74
|
+
- **`size`** — SVG scales cleanly to any size.
|
|
75
|
+
|
|
76
|
+
#### Methods
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
const el = document.querySelector('magic-loading');
|
|
80
|
+
|
|
81
|
+
// Trigger burst completion animation
|
|
82
|
+
await el.complete();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Dynamic Updates
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
const el = document.querySelector('magic-loading');
|
|
89
|
+
|
|
90
|
+
// Update progress
|
|
91
|
+
el.setAttribute('progress', '0.8');
|
|
92
|
+
|
|
93
|
+
// Change text → regenerates the entire circle
|
|
94
|
+
el.setAttribute('text', 'Almost done...');
|
|
95
|
+
|
|
96
|
+
// Switch to indeterminate mode
|
|
97
|
+
el.removeAttribute('progress');
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Programmatic API
|
|
101
|
+
|
|
102
|
+
For more control, use `create()`:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
import { create } from '@yeonseong/magic-loading';
|
|
106
|
+
|
|
107
|
+
// Mount into a container element
|
|
108
|
+
const loader = create(document.getElementById('my-container'), {
|
|
109
|
+
text: 'Loading...',
|
|
110
|
+
size: 160,
|
|
111
|
+
progress: 0, // optional: omit for indeterminate
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Update progress (e.g. from fetch/upload callback)
|
|
115
|
+
loader.setProgress(0.5);
|
|
116
|
+
|
|
117
|
+
// Change text (regenerates the circle)
|
|
118
|
+
loader.setText('Almost done...');
|
|
119
|
+
|
|
120
|
+
// Burst completion animation (returns a Promise)
|
|
121
|
+
await loader.complete();
|
|
122
|
+
|
|
123
|
+
// Remove from DOM
|
|
124
|
+
loader.destroy();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### CDN (no build step)
|
|
128
|
+
|
|
129
|
+
```html
|
|
130
|
+
<script src="https://unpkg.com/@yeonseong/magic-loading/dist/index.global.js"></script>
|
|
131
|
+
|
|
132
|
+
<div id="loader"></div>
|
|
133
|
+
|
|
134
|
+
<script>
|
|
135
|
+
const loader = MagicLoading.create(
|
|
136
|
+
document.getElementById('loader'),
|
|
137
|
+
{ text: 'Hello', size: 120 }
|
|
138
|
+
);
|
|
139
|
+
</script>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Framework Examples
|
|
143
|
+
|
|
144
|
+
#### React
|
|
145
|
+
|
|
146
|
+
```jsx
|
|
147
|
+
import '@yeonseong/magic-loading';
|
|
148
|
+
|
|
149
|
+
function Loader({ text, progress }) {
|
|
150
|
+
return <magic-loading text={text} progress={progress} size={120} />;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Vue
|
|
155
|
+
|
|
156
|
+
```vue
|
|
157
|
+
<script setup>
|
|
158
|
+
import '@yeonseong/magic-loading';
|
|
159
|
+
</script>
|
|
160
|
+
|
|
161
|
+
<template>
|
|
162
|
+
<magic-loading :text="text" :progress="progress" size="120" />
|
|
163
|
+
</template>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Svelte
|
|
167
|
+
|
|
168
|
+
```svelte
|
|
169
|
+
<script>
|
|
170
|
+
import '@yeonseong/magic-loading';
|
|
171
|
+
export let text;
|
|
172
|
+
export let progress;
|
|
173
|
+
</script>
|
|
174
|
+
|
|
175
|
+
<magic-loading {text} {progress} size="120" />
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Customization
|
|
179
|
+
|
|
180
|
+
Override colors with CSS custom properties:
|
|
181
|
+
|
|
182
|
+
```css
|
|
183
|
+
magic-loading {
|
|
184
|
+
--ml-color-primary: #ff6b6b;
|
|
185
|
+
--ml-color-secondary: #ffd93d;
|
|
186
|
+
--ml-color-glow: #ff6b6b;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
| Property | Default | Description |
|
|
191
|
+
|----------|---------|-------------|
|
|
192
|
+
| `--ml-color-primary` | Auto (from text hash) | Ring strokes, glyphs, center symbol |
|
|
193
|
+
| `--ml-color-secondary` | Auto (from text hash) | Inner polygon strokes |
|
|
194
|
+
| `--ml-color-glow` | Auto (from text hash) | Glow filter color |
|
|
195
|
+
|
|
196
|
+
By default, colors are deterministically derived from the text hash — each text gets its own unique color palette. Use CSS custom properties to override when you need brand-consistent colors.
|
|
197
|
+
|
|
198
|
+
### Scoped overrides
|
|
199
|
+
|
|
200
|
+
```css
|
|
201
|
+
/* All loaders on the page */
|
|
202
|
+
magic-loading {
|
|
203
|
+
--ml-color-primary: #00ffcc;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* Only loaders inside .dark-panel */
|
|
207
|
+
.dark-panel magic-loading {
|
|
208
|
+
--ml-color-primary: #ff00ff;
|
|
209
|
+
--ml-color-secondary: #8800ff;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Animation Modes
|
|
214
|
+
|
|
215
|
+
### Determinate (`progress` set)
|
|
216
|
+
|
|
217
|
+
Layers activate sequentially as progress increases from 0 to 1:
|
|
218
|
+
|
|
219
|
+
| Progress | Effect |
|
|
220
|
+
|----------|--------|
|
|
221
|
+
| 0% | All layers dim (0.15 opacity), slow rotation |
|
|
222
|
+
| ~25% | Outer ring fully visible |
|
|
223
|
+
| ~50% | Second ring activates, glow intensifies |
|
|
224
|
+
| ~75% | Inner polygon appears |
|
|
225
|
+
| 100% | Everything at full brightness, fast rotation |
|
|
226
|
+
|
|
227
|
+
### Indeterminate (`progress` omitted)
|
|
228
|
+
|
|
229
|
+
All layers pulse with a sine-wave rhythm. Constant rotation, gentle glow oscillation.
|
|
230
|
+
|
|
231
|
+
### Burst (`.complete()`)
|
|
232
|
+
|
|
233
|
+
A ~0.5s flash — all layers max brightness, rotation accelerates, glow flares to 3x, then fades out.
|
|
234
|
+
|
|
235
|
+
## How It Works
|
|
236
|
+
|
|
237
|
+
1. Input text is hashed with FNV-1a to produce deterministic parameters
|
|
238
|
+
2. Parameters control: ring count, polygon sides, glyph set (runes / alchemy / zodiac / geometric), colors, rotation directions
|
|
239
|
+
3. SVG is generated with concentric rings, glyphs, inner polygons, and a central symbol
|
|
240
|
+
4. Animation adapts to progress — layers activate sequentially, rotation accelerates, glow intensifies
|
|
241
|
+
|
|
242
|
+
## Glyph Sets
|
|
243
|
+
|
|
244
|
+
| Set | Symbols |
|
|
245
|
+
|-----|---------|
|
|
246
|
+
| Elder Futhark | ᚠ ᚢ ᚦ ᚨ ᚱ ᚲ ᚷ ᚹ ... |
|
|
247
|
+
| Alchemy | ☉ ☽ ☿ ♀ ♂ ♃ ♄ △ ... |
|
|
248
|
+
| Zodiac | ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ... |
|
|
249
|
+
| Geometric | ✡ ⬡ ◈ △ ☆ ✧ ◇ ◉ ... |
|
|
250
|
+
|
|
251
|
+
The glyph set is automatically selected based on text hash. Each text produces a unique combination.
|
|
252
|
+
|
|
253
|
+
## License
|
|
254
|
+
|
|
255
|
+
MIT
|
package/package.json
CHANGED