@andresclua/perlindom 0.0.1 → 0.0.3
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/CHANGELOG.md +70 -0
- package/dist/PerlinDOM.es.js +76 -53
- package/dist/PerlinDOM.umd.js +1 -1
- package/docs/examples.html +6 -0
- package/package.json +1 -1
- package/readme.md +269 -189
- package/src/index.html +169 -7
- package/src/js/PerlinDom.js +115 -21
- package/src/examples.html +0 -515
- package/src/js/PerlinSVG.js +0 -190
package/src/examples.html
DELETED
|
@@ -1,515 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>PerlinDOM Examples</title>
|
|
7
|
-
<style>
|
|
8
|
-
body {
|
|
9
|
-
font-family: 'Arial', sans-serif;
|
|
10
|
-
line-height: 1.6;
|
|
11
|
-
color: #333;
|
|
12
|
-
max-width: 1200px;
|
|
13
|
-
margin: 0 auto;
|
|
14
|
-
padding: 20px;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
h1, h2 {
|
|
18
|
-
color: #2c3e50;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.example-container {
|
|
22
|
-
margin-bottom: 40px;
|
|
23
|
-
border: 1px solid #ddd;
|
|
24
|
-
border-radius: 8px;
|
|
25
|
-
padding: 20px;
|
|
26
|
-
position: relative;
|
|
27
|
-
overflow: hidden;
|
|
28
|
-
height: 300px;
|
|
29
|
-
background-color: #f9f9f9;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.code-block {
|
|
33
|
-
background-color: #f5f5f5;
|
|
34
|
-
padding: 15px;
|
|
35
|
-
border-radius: 5px;
|
|
36
|
-
overflow-x: auto;
|
|
37
|
-
margin-bottom: 20px;
|
|
38
|
-
font-family: monospace;
|
|
39
|
-
font-size: 14px;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/* Example 1: Basic Animation */
|
|
43
|
-
.basic-element {
|
|
44
|
-
width: 80px;
|
|
45
|
-
height: 80px;
|
|
46
|
-
background-color: #3498db;
|
|
47
|
-
border-radius: 50%;
|
|
48
|
-
position: absolute;
|
|
49
|
-
left: calc(50% - 40px); /* 50% - half width */
|
|
50
|
-
top: calc(50% - 40px); /* 50% - half height */
|
|
51
|
-
filter: blur(5px);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/* Example 2: Horizontal Only */
|
|
55
|
-
.horizontal-element {
|
|
56
|
-
width: 60px;
|
|
57
|
-
height: 60px;
|
|
58
|
-
background-color: #e74c3c;
|
|
59
|
-
border-radius: 10px;
|
|
60
|
-
position: absolute;
|
|
61
|
-
left: calc(50% - 30px); /* 50% - half width */
|
|
62
|
-
top: calc(50% - 30px); /* 50% - half height */
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/* Example 3: Multiple Elements */
|
|
66
|
-
.particle {
|
|
67
|
-
width: 230px;
|
|
68
|
-
height: 230px;
|
|
69
|
-
background:linear-gradient(to bottom right, #8e44ad, #3498db);
|
|
70
|
-
border-radius: 50%;
|
|
71
|
-
position: absolute;
|
|
72
|
-
/* opacity: 0.2; */
|
|
73
|
-
filter: blur(10px);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/* Example 4: Background Effect */
|
|
77
|
-
.bg-container {
|
|
78
|
-
background-color: #34495e;
|
|
79
|
-
position: relative;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.bg-element {
|
|
83
|
-
position: absolute;
|
|
84
|
-
border-radius: 50%;
|
|
85
|
-
background-color: rgba(255, 255, 255, 0.1);
|
|
86
|
-
filter: blur(5px);
|
|
87
|
-
transform: scale(3.2);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.bg-element-1 {
|
|
91
|
-
width: 100px;
|
|
92
|
-
height: 100px;
|
|
93
|
-
left: 20%;
|
|
94
|
-
top: 30%;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.bg-element-2 {
|
|
98
|
-
width: 150px;
|
|
99
|
-
height: 150px;
|
|
100
|
-
left: 60%;
|
|
101
|
-
top: 50%;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.bg-element-3 {
|
|
105
|
-
width: 80px;
|
|
106
|
-
height: 80px;
|
|
107
|
-
left: 40%;
|
|
108
|
-
top: 70%;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
.content {
|
|
112
|
-
position: relative;
|
|
113
|
-
z-index: 10;
|
|
114
|
-
color: white;
|
|
115
|
-
text-align: center;
|
|
116
|
-
padding-top: 120px;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/* Example 5: Pause Play Element */
|
|
120
|
-
.pause-play-container {
|
|
121
|
-
position: relative;
|
|
122
|
-
cursor: pointer;
|
|
123
|
-
height: 200px;
|
|
124
|
-
border: 1px dashed #ccc;
|
|
125
|
-
border-radius: 8px;
|
|
126
|
-
margin-top: 20px;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.pause-play-element {
|
|
130
|
-
width: 70px;
|
|
131
|
-
height: 70px;
|
|
132
|
-
background-color: #f39c12;
|
|
133
|
-
border-radius: 50%;
|
|
134
|
-
position: absolute;
|
|
135
|
-
left: calc(50% - 35px);
|
|
136
|
-
top: calc(50% - 35px);
|
|
137
|
-
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.status-display {
|
|
141
|
-
position: absolute;
|
|
142
|
-
bottom: 20px;
|
|
143
|
-
left: 0;
|
|
144
|
-
width: 100%;
|
|
145
|
-
text-align: center;
|
|
146
|
-
font-weight: bold;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
.status-indicator {
|
|
150
|
-
color: #2980b9;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
.instruction {
|
|
154
|
-
font-size: 14px;
|
|
155
|
-
color: #7f8c8d;
|
|
156
|
-
margin-top: 5px;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.pause-play-settings {
|
|
160
|
-
margin-bottom: 20px;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
.setting-description {
|
|
164
|
-
font-size: 12px;
|
|
165
|
-
color: #7f8c8d;
|
|
166
|
-
margin-top: 5px;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.lerp-value {
|
|
170
|
-
font-weight: bold;
|
|
171
|
-
color: #e67e22;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/* Example 6: Interactive Element */
|
|
175
|
-
.interactive-container {
|
|
176
|
-
display: flex;
|
|
177
|
-
justify-content: space-between;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.controls {
|
|
181
|
-
width: 40%;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.preview {
|
|
185
|
-
width: 55%;
|
|
186
|
-
height: 200px;
|
|
187
|
-
border: 1px solid #ddd;
|
|
188
|
-
border-radius: 5px;
|
|
189
|
-
position: relative;
|
|
190
|
-
overflow: hidden;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.interactive-element {
|
|
194
|
-
width: 50px;
|
|
195
|
-
height: 50px;
|
|
196
|
-
background-color: #9b59b6;
|
|
197
|
-
border-radius: 50%;
|
|
198
|
-
position: absolute;
|
|
199
|
-
left: calc(50% - 25px); /* 50% - half width */
|
|
200
|
-
top: calc(50% - 25px); /* 50% - half height */
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.control-group {
|
|
204
|
-
margin-bottom: 15px;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
label {
|
|
208
|
-
display: block;
|
|
209
|
-
margin-bottom: 5px;
|
|
210
|
-
font-weight: bold;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
input[type="range"] {
|
|
214
|
-
width: 100%;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.value-display {
|
|
218
|
-
font-size: 12px;
|
|
219
|
-
color: #666;
|
|
220
|
-
}
|
|
221
|
-
</style>
|
|
222
|
-
</head>
|
|
223
|
-
<body>
|
|
224
|
-
<h1>PerlinDOM Examples</h1>
|
|
225
|
-
<p>This page demonstrates various ways to use the PerlinDOM library for creating smooth, organic animations.</p>
|
|
226
|
-
|
|
227
|
-
<!-- Example 1: Basic Animation -->
|
|
228
|
-
<h2>1. Basic Animation</h2>
|
|
229
|
-
<div class="code-block">
|
|
230
|
-
const basicAnimation = new PerlinDOM({<br>
|
|
231
|
-
element: document.querySelector('.basic-element'),<br>
|
|
232
|
-
x: { min: -50, max: 50 },<br>
|
|
233
|
-
y: { min: -50, max: 50 },<br>
|
|
234
|
-
speed: 0.01,<br>
|
|
235
|
-
seed: 123<br>
|
|
236
|
-
});
|
|
237
|
-
</div>
|
|
238
|
-
<div class="example-container">
|
|
239
|
-
<div class="basic-element"></div>
|
|
240
|
-
</div>
|
|
241
|
-
|
|
242
|
-
<!-- Example 2: Horizontal Only -->
|
|
243
|
-
<h2>2. Horizontal-Only Movement</h2>
|
|
244
|
-
<div class="code-block">
|
|
245
|
-
const horizontalAnimation = new PerlinDOM({<br>
|
|
246
|
-
element: document.querySelector('.horizontal-element'),<br>
|
|
247
|
-
x: { min: -100, max: 100 },<br>
|
|
248
|
-
y: null, // No vertical movement<br>
|
|
249
|
-
speed: 0.02<br>
|
|
250
|
-
});
|
|
251
|
-
</div>
|
|
252
|
-
<div class="example-container">
|
|
253
|
-
<div class="horizontal-element"></div>
|
|
254
|
-
</div>
|
|
255
|
-
|
|
256
|
-
<!-- Example 3: Multiple Elements -->
|
|
257
|
-
<h2>3. Multiple Elements with Different Seeds</h2>
|
|
258
|
-
<div class="code-block">
|
|
259
|
-
document.querySelectorAll('.particle').forEach((el, index) => {<br>
|
|
260
|
-
new PerlinDOM({<br>
|
|
261
|
-
element: el,<br>
|
|
262
|
-
x: { min: -100, max: 100 },<br>
|
|
263
|
-
y: { min: -100, max: 100 },<br>
|
|
264
|
-
speed: 0.01,<br>
|
|
265
|
-
seed: index * 100 // Different seed for each element<br>
|
|
266
|
-
});<br>
|
|
267
|
-
});
|
|
268
|
-
</div>
|
|
269
|
-
<div class="example-container">
|
|
270
|
-
<div class="particle" style="left: 30%; top: 30%"></div>
|
|
271
|
-
<div class="particle" style="left: 50%; top: 50%"></div>
|
|
272
|
-
<div class="particle" style="left: 70%; top: 70%"></div>
|
|
273
|
-
<div class="particle" style="left: 30%; top: 70%"></div>
|
|
274
|
-
<div class="particle" style="left: 70%; top: 30%"></div>
|
|
275
|
-
</div>
|
|
276
|
-
|
|
277
|
-
<!-- Example 4: Background Effect -->
|
|
278
|
-
<h2>4. Creating a Background Effect</h2>
|
|
279
|
-
<div class="code-block">
|
|
280
|
-
document.querySelectorAll('.bg-element').forEach(el => {<br>
|
|
281
|
-
new PerlinDOM({<br>
|
|
282
|
-
element: el,<br>
|
|
283
|
-
x: { min: -10, max: 10 },<br>
|
|
284
|
-
y: { min: -10, max: 10 },<br>
|
|
285
|
-
speed: 0.002, // Very slow movement<br>
|
|
286
|
-
seed: Math.random() * 2000<br>
|
|
287
|
-
});<br>
|
|
288
|
-
});
|
|
289
|
-
</div>
|
|
290
|
-
<div class="example-container bg-container">
|
|
291
|
-
<div class="bg-element bg-element-1"></div>
|
|
292
|
-
<div class="bg-element bg-element-2"></div>
|
|
293
|
-
<div class="bg-element bg-element-3"></div>
|
|
294
|
-
<div class="content">
|
|
295
|
-
<h3>Subtle Background Animation</h3>
|
|
296
|
-
<p>Perfect for creating depth in UI elements</p>
|
|
297
|
-
</div>
|
|
298
|
-
</div>
|
|
299
|
-
|
|
300
|
-
<!-- Example 5: Pause and Play with Mouse Events -->
|
|
301
|
-
<h2>5. Smooth Pause and Play with Mouse Events</h2>
|
|
302
|
-
<div class="code-block">
|
|
303
|
-
const pausePlayAnimation = new PerlinDOM({<br>
|
|
304
|
-
element: document.querySelector('.pause-play-element'),<br>
|
|
305
|
-
x: { min: -80, max: 80 },<br>
|
|
306
|
-
y: { min: -80, max: 80 },<br>
|
|
307
|
-
speed: 0.01,<br>
|
|
308
|
-
seed: 42,<br>
|
|
309
|
-
lerpSpeed: 0.05 // Controls how smooth the transition is<br>
|
|
310
|
-
});<br><br>
|
|
311
|
-
|
|
312
|
-
// Pause on mouse leave<br>
|
|
313
|
-
document.querySelector('.pause-play-container').addEventListener('mouseleave', () => {<br>
|
|
314
|
-
pausePlayAnimation.pause();<br>
|
|
315
|
-
document.querySelector('.status-indicator').textContent = 'Pausing...';<br>
|
|
316
|
-
});<br><br>
|
|
317
|
-
|
|
318
|
-
// Play on mouse enter<br>
|
|
319
|
-
document.querySelector('.pause-play-container').addEventListener('mouseenter', () => {<br>
|
|
320
|
-
pausePlayAnimation.play();<br>
|
|
321
|
-
document.querySelector('.status-indicator').textContent = 'Playing...';<br>
|
|
322
|
-
});<br><br>
|
|
323
|
-
|
|
324
|
-
// Update lerpSpeed when slider changes<br>
|
|
325
|
-
document.getElementById('lerp-speed').addEventListener('input', (e) => {<br>
|
|
326
|
-
const newSpeed = parseFloat(e.target.value);<br>
|
|
327
|
-
pausePlayAnimation.lerpSpeed = newSpeed;<br>
|
|
328
|
-
document.querySelector('.lerp-value').textContent = newSpeed.toFixed(2);<br>
|
|
329
|
-
});
|
|
330
|
-
</div>
|
|
331
|
-
<div class="example-container">
|
|
332
|
-
<div class="pause-play-settings">
|
|
333
|
-
<label for="lerp-speed">Transition Speed: <span class="lerp-value">0.05</span></label>
|
|
334
|
-
<input type="range" id="lerp-speed" min="0.01" max="0.2" step="0.01" value="0.05">
|
|
335
|
-
<p class="setting-description">Lower values = smoother, slower transitions</p>
|
|
336
|
-
</div>
|
|
337
|
-
<div class="pause-play-container">
|
|
338
|
-
<div class="pause-play-element"></div>
|
|
339
|
-
<div class="status-display">
|
|
340
|
-
Status: <span class="status-indicator">Playing</span>
|
|
341
|
-
<p class="instruction">Hover over this orange circle to play, move mouse out to pause</p>
|
|
342
|
-
</div>
|
|
343
|
-
</div>
|
|
344
|
-
</div>
|
|
345
|
-
|
|
346
|
-
<!-- Example 6: Interactive Controls -->
|
|
347
|
-
<h2>6. Interactive Controls</h2>
|
|
348
|
-
<div class="code-block">
|
|
349
|
-
// Create the animation with initial settings<br>
|
|
350
|
-
const interactiveAnimation = new PerlinDOM({<br>
|
|
351
|
-
element: document.querySelector('.interactive-element'),<br>
|
|
352
|
-
x: { min: -50, max: 50 },<br>
|
|
353
|
-
y: { min: -50, max: 50 },<br>
|
|
354
|
-
speed: 0.01,<br>
|
|
355
|
-
seed: 0<br>
|
|
356
|
-
});<br><br>
|
|
357
|
-
|
|
358
|
-
// Update settings based on user input<br>
|
|
359
|
-
speedControl.addEventListener('input', () => {<br>
|
|
360
|
-
// Stop current animation<br>
|
|
361
|
-
interactiveAnimation.destroy();<br>
|
|
362
|
-
<br>
|
|
363
|
-
// Reinitialize with new settings<br>
|
|
364
|
-
interactiveAnimation.speed = parseFloat(speedControl.value);<br>
|
|
365
|
-
interactiveAnimation.init(seedControl.value);<br>
|
|
366
|
-
});
|
|
367
|
-
</div>
|
|
368
|
-
<div class="example-container">
|
|
369
|
-
<div class="interactive-container">
|
|
370
|
-
<div class="controls">
|
|
371
|
-
<div class="control-group">
|
|
372
|
-
<label for="x-range">X Range:</label>
|
|
373
|
-
<input type="range" id="x-range" min="0" max="100" value="50">
|
|
374
|
-
<div class="value-display">-50 to 50</div>
|
|
375
|
-
</div>
|
|
376
|
-
|
|
377
|
-
<div class="control-group">
|
|
378
|
-
<label for="y-range">Y Range:</label>
|
|
379
|
-
<input type="range" id="y-range" min="0" max="100" value="50">
|
|
380
|
-
<div class="value-display">-50 to 50</div>
|
|
381
|
-
</div>
|
|
382
|
-
|
|
383
|
-
<div class="control-group">
|
|
384
|
-
<label for="speed">Speed:</label>
|
|
385
|
-
<input type="range" id="speed" min="0.001" max="0.05" step="0.001" value="0.01">
|
|
386
|
-
<div class="value-display">0.01</div>
|
|
387
|
-
</div>
|
|
388
|
-
|
|
389
|
-
<div class="control-group">
|
|
390
|
-
<label for="seed">Seed:</label>
|
|
391
|
-
<input type="range" id="seed" min="0" max="1000" step="1" value="0">
|
|
392
|
-
<div class="value-display">0</div>
|
|
393
|
-
</div>
|
|
394
|
-
</div>
|
|
395
|
-
|
|
396
|
-
<div class="preview">
|
|
397
|
-
<div class="interactive-element"></div>
|
|
398
|
-
</div>
|
|
399
|
-
</div>
|
|
400
|
-
</div>
|
|
401
|
-
|
|
402
|
-
<!-- Load PerlinDOM library -->
|
|
403
|
-
<script type="module">
|
|
404
|
-
import PerlinDOM from './js/PerlinDom.js';
|
|
405
|
-
|
|
406
|
-
// Example 1: Basic Animation
|
|
407
|
-
const basicAnimation = new PerlinDOM({
|
|
408
|
-
element: document.querySelector('.basic-element'),
|
|
409
|
-
x: { min: -50, max: 50 },
|
|
410
|
-
y: { min: -50, max: 50 },
|
|
411
|
-
speed: 0.01,
|
|
412
|
-
seed: 123
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
// Example 2: Horizontal Only
|
|
416
|
-
const horizontalAnimation = new PerlinDOM({
|
|
417
|
-
element: document.querySelector('.horizontal-element'),
|
|
418
|
-
x: { min: -100, max: 100 },
|
|
419
|
-
y: null, // No vertical movement
|
|
420
|
-
speed: 0.02
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Example 3: Multiple Elements
|
|
424
|
-
document.querySelectorAll('.particle').forEach((el, index) => {
|
|
425
|
-
new PerlinDOM({
|
|
426
|
-
element: el,
|
|
427
|
-
x: { min: -100, max: 100 + index * 20 }, // Slightly wider range for each element
|
|
428
|
-
y: { min: -100, max: 120}, // Random vertical range
|
|
429
|
-
speed: index * 0.001, // Slightly different speed for each element
|
|
430
|
-
seed: index * 16700 // Different seed for each element
|
|
431
|
-
});
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
// Example 4: Background Effect
|
|
435
|
-
document.querySelectorAll('.bg-element').forEach(el => {
|
|
436
|
-
new PerlinDOM({
|
|
437
|
-
element: el,
|
|
438
|
-
x: { min: -110, max: 110 },
|
|
439
|
-
y: { min: -110, max: 110 },
|
|
440
|
-
speed: 0.002, // Very slow movement
|
|
441
|
-
seed: Math.random() * 2000
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
// Example 5: Pause and Play with Mouse Events
|
|
446
|
-
const pausePlayAnimation = new PerlinDOM({
|
|
447
|
-
element: document.querySelector('.pause-play-element'),
|
|
448
|
-
x: { min: -80, max: 80 },
|
|
449
|
-
y: { min: -180, max: 80 },
|
|
450
|
-
speed: 0.01,
|
|
451
|
-
seed: 42,
|
|
452
|
-
lerpSpeed: 0.05 // Controls how smooth the transition is
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
// Pause on mouse leave
|
|
456
|
-
document.querySelector('.pause-play-container').addEventListener('mouseleave', () => {
|
|
457
|
-
pausePlayAnimation.pause();
|
|
458
|
-
document.querySelector('.status-indicator').textContent = 'Pausing...';
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
// Play on mouse enter
|
|
462
|
-
document.querySelector('.pause-play-container').addEventListener('mouseenter', () => {
|
|
463
|
-
pausePlayAnimation.play();
|
|
464
|
-
document.querySelector('.status-indicator').textContent = 'Playing...';
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
// Update lerpSpeed when slider changes
|
|
468
|
-
document.getElementById('lerp-speed').addEventListener('input', (e) => {
|
|
469
|
-
const newSpeed = parseFloat(e.target.value);
|
|
470
|
-
pausePlayAnimation.lerpSpeed = newSpeed;
|
|
471
|
-
document.querySelector('.lerp-value').textContent = newSpeed.toFixed(2);
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// Example 6: Interactive Controls
|
|
475
|
-
let interactiveAnimation;
|
|
476
|
-
|
|
477
|
-
function initInteractiveAnimation() {
|
|
478
|
-
// Destroy previous instance if it exists
|
|
479
|
-
if (interactiveAnimation) {
|
|
480
|
-
interactiveAnimation.destroy();
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Get current values
|
|
484
|
-
const xRange = parseInt(document.getElementById('x-range').value);
|
|
485
|
-
const yRange = parseInt(document.getElementById('y-range').value);
|
|
486
|
-
const speed = parseFloat(document.getElementById('speed').value);
|
|
487
|
-
const seed = parseInt(document.getElementById('seed').value);
|
|
488
|
-
|
|
489
|
-
// Create new instance
|
|
490
|
-
interactiveAnimation = new PerlinDOM({
|
|
491
|
-
element: document.querySelector('.interactive-element'),
|
|
492
|
-
x: { min: -xRange, max: xRange },
|
|
493
|
-
y: { min: -yRange, max: yRange },
|
|
494
|
-
speed: speed,
|
|
495
|
-
seed: seed
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
// Update displays
|
|
499
|
-
document.querySelector('#x-range + .value-display').textContent = `-${xRange} to ${xRange}`;
|
|
500
|
-
document.querySelector('#y-range + .value-display').textContent = `-${yRange} to ${yRange}`;
|
|
501
|
-
document.querySelector('#speed + .value-display').textContent = speed.toFixed(3);
|
|
502
|
-
document.querySelector('#seed + .value-display').textContent = seed;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// Initialize
|
|
506
|
-
initInteractiveAnimation();
|
|
507
|
-
|
|
508
|
-
// Add event listeners
|
|
509
|
-
document.getElementById('x-range').addEventListener('input', initInteractiveAnimation);
|
|
510
|
-
document.getElementById('y-range').addEventListener('input', initInteractiveAnimation);
|
|
511
|
-
document.getElementById('speed').addEventListener('input', initInteractiveAnimation);
|
|
512
|
-
document.getElementById('seed').addEventListener('input', initInteractiveAnimation);
|
|
513
|
-
</script>
|
|
514
|
-
</body>
|
|
515
|
-
</html>
|
package/src/js/PerlinSVG.js
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import PerlinDOM from './PerlinDom.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PerlinSVG - Clase para animar paths SVG usando ruido Perlin
|
|
5
|
-
*
|
|
6
|
-
* Esta clase extiende PerlinDOM para proporcionar animaciones orgánicas
|
|
7
|
-
* a elementos SVG path, creando efectos de ondulación fluidos y naturales.
|
|
8
|
-
*/
|
|
9
|
-
class PerlinSVG extends PerlinDOM {
|
|
10
|
-
/**
|
|
11
|
-
* Constructor de PerlinSVG
|
|
12
|
-
*
|
|
13
|
-
* @param {Object} options - Opciones de configuración
|
|
14
|
-
* @param {Element|Element[]} options.element - El/los elemento(s) SVG path a animar
|
|
15
|
-
* @param {number} options.numPoints - Número de puntos de control para cada path (más puntos = curvas más detalladas)
|
|
16
|
-
* @param {number} options.amplitude - Amplitud máxima de la animación (altura de las ondas en píxeles)
|
|
17
|
-
* @param {boolean} options.isOpen - Estado inicial (true = abierto/visible, false = cerrado/oculto)
|
|
18
|
-
* @param {number} options.speed - Velocidad de la animación (valores más altos = movimiento más rápido)
|
|
19
|
-
* @param {number} options.seed - Semilla para el generador de ruido Perlin (misma semilla = mismo patrón)
|
|
20
|
-
* @param {number} options.lerpSpeed - Velocidad de interpolación entre estados (valores más altos = transiciones más rápidas)
|
|
21
|
-
* @param {number} options.delayPointsMax - Retraso máximo aleatorio para cada punto (crea efectos de cascada)
|
|
22
|
-
* @param {number} options.delayPerPath - Retraso entre capas/paths consecutivos (crea efecto escalonado)
|
|
23
|
-
*/
|
|
24
|
-
constructor({
|
|
25
|
-
element, // El/los elemento(s) SVG path a animar
|
|
26
|
-
numPoints = 10, // Número de puntos de control (más puntos = curvas más detalladas)
|
|
27
|
-
amplitude = 100, // Amplitud máxima de la animación (altura de las ondas)
|
|
28
|
-
isOpen = false, // Estado inicial (true = abierto/visible, false = cerrado/oculto)
|
|
29
|
-
speed = 0.01, // Velocidad de la animación (valores más altos = movimiento más rápido)
|
|
30
|
-
seed = 0, // Semilla para el generador de ruido Perlin (misma semilla = mismo patrón)
|
|
31
|
-
lerpSpeed = 0.1, // Velocidad de interpolación entre estados
|
|
32
|
-
delayPointsMax = 0.3, // Retraso máximo aleatorio para cada punto (crea efectos de cascada)
|
|
33
|
-
delayPerPath = 0.25 // Retraso entre capas/paths consecutivos (crea efecto escalonado)
|
|
34
|
-
}) {
|
|
35
|
-
// Call parent constructor with null element
|
|
36
|
-
// since we'll handle the SVG differently
|
|
37
|
-
super({
|
|
38
|
-
element: null,
|
|
39
|
-
speed: speed,
|
|
40
|
-
seed: seed,
|
|
41
|
-
lerpSpeed: lerpSpeed
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// SVG specific properties
|
|
45
|
-
this.svgElement = element;
|
|
46
|
-
this.paths = Array.isArray(element) ? element : [element];
|
|
47
|
-
this.numPoints = numPoints;
|
|
48
|
-
this.amplitude = amplitude;
|
|
49
|
-
this.isOpen = isOpen;
|
|
50
|
-
this.delayPointsMax = delayPointsMax;
|
|
51
|
-
this.delayPerPath = delayPerPath;
|
|
52
|
-
this.isAnimating = false;
|
|
53
|
-
|
|
54
|
-
// Arrays to store points and delays
|
|
55
|
-
this.allPoints = [];
|
|
56
|
-
this.pointsDelay = [];
|
|
57
|
-
|
|
58
|
-
// Initialize points and delays
|
|
59
|
-
this.initPoints();
|
|
60
|
-
|
|
61
|
-
// Add click event
|
|
62
|
-
if (this.paths.length > 0 && this.paths[0].ownerSVGElement) {
|
|
63
|
-
this.paths[0].ownerSVGElement.addEventListener('click', () => this.toggle());
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Initialize points for all paths
|
|
68
|
-
initPoints() {
|
|
69
|
-
// Initialize delays for points
|
|
70
|
-
for (let i = 0; i < this.numPoints; i++) {
|
|
71
|
-
this.pointsDelay[i] = Math.random() * this.delayPointsMax;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Initialize points for each path
|
|
75
|
-
for (let i = 0; i < this.paths.length; i++) {
|
|
76
|
-
let points = [];
|
|
77
|
-
this.allPoints.push(points);
|
|
78
|
-
|
|
79
|
-
for (let j = 0; j < this.numPoints; j++) {
|
|
80
|
-
points.push(this.isOpen ? 0 : this.amplitude);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Render initial paths
|
|
85
|
-
this.render();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Toggle between open/closed states
|
|
89
|
-
toggle() {
|
|
90
|
-
if (this.isAnimating) return;
|
|
91
|
-
|
|
92
|
-
this.isOpen = !this.isOpen;
|
|
93
|
-
this.isAnimating = true;
|
|
94
|
-
|
|
95
|
-
// Schedule animations for each point with Perlin noise-based delays
|
|
96
|
-
const startTime = performance.now();
|
|
97
|
-
const animate = (timestamp) => {
|
|
98
|
-
const elapsed = timestamp - startTime;
|
|
99
|
-
let stillAnimating = false;
|
|
100
|
-
|
|
101
|
-
// Update all points
|
|
102
|
-
for (let i = 0; i < this.paths.length; i++) {
|
|
103
|
-
const pathDelay = this.delayPerPath * 1000 * (this.isOpen ? i : (this.paths.length - i - 1));
|
|
104
|
-
|
|
105
|
-
for (let j = 0; j < this.numPoints; j++) {
|
|
106
|
-
const pointDelay = this.pointsDelay[j] * 1000; // Convert to ms
|
|
107
|
-
const totalDelay = pointDelay + pathDelay;
|
|
108
|
-
|
|
109
|
-
if (elapsed > totalDelay) {
|
|
110
|
-
// Calculate animation progress
|
|
111
|
-
const animProgress = Math.min(1, (elapsed - totalDelay) / (1000 * this.lerpSpeed * 5));
|
|
112
|
-
|
|
113
|
-
// Target value
|
|
114
|
-
const targetValue = this.isOpen ? 0 : this.amplitude;
|
|
115
|
-
|
|
116
|
-
// Interpolate towards target
|
|
117
|
-
if (Math.abs(this.allPoints[i][j] - targetValue) > 0.1) {
|
|
118
|
-
// Use Perlin noise to add natural variation
|
|
119
|
-
const noiseValue = this.noise.perlin2(j * 0.1, i * 0.1 + this.time);
|
|
120
|
-
const variation = noiseValue * 2; // Small variation
|
|
121
|
-
|
|
122
|
-
// Use our own lerp function since we can't access the private one
|
|
123
|
-
this.allPoints[i][j] = this.lerp(
|
|
124
|
-
this.allPoints[i][j],
|
|
125
|
-
targetValue + (this.isOpen ? 0 : variation), // Only add variation in closed state
|
|
126
|
-
animProgress
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
stillAnimating = true;
|
|
130
|
-
} else {
|
|
131
|
-
this.allPoints[i][j] = targetValue;
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
stillAnimating = true;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Update the display
|
|
140
|
-
this.render();
|
|
141
|
-
|
|
142
|
-
// Continue animation if needed
|
|
143
|
-
if (stillAnimating) {
|
|
144
|
-
this.raf = requestAnimationFrame(animate);
|
|
145
|
-
} else {
|
|
146
|
-
this.isAnimating = false;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Update time for Perlin noise
|
|
150
|
-
this.time += this.speed;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
// Start animation
|
|
154
|
-
this.raf = requestAnimationFrame(animate);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Render SVG paths
|
|
158
|
-
render() {
|
|
159
|
-
for (let i = 0; i < this.paths.length; i++) {
|
|
160
|
-
const path = this.paths[i];
|
|
161
|
-
const points = this.allPoints[i];
|
|
162
|
-
|
|
163
|
-
let d = "";
|
|
164
|
-
d += this.isOpen ? `M 0 0 V ${points[0]} C` : `M 0 ${points[0]} C`;
|
|
165
|
-
|
|
166
|
-
for (let j = 0; j < this.numPoints - 1; j++) {
|
|
167
|
-
let p = (j + 1) / (this.numPoints - 1) * 100;
|
|
168
|
-
let cp = p - (1 / (this.numPoints - 1) * 100) / 2;
|
|
169
|
-
d += ` ${cp} ${points[j]} ${cp} ${points[j+1]} ${p} ${points[j+1]}`;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
d += this.isOpen ? ` V 100 H 0` : ` V 0 H 0`;
|
|
173
|
-
path.setAttribute("d", d);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Override parent's animate method
|
|
178
|
-
// so it doesn't interfere with our custom animation
|
|
179
|
-
animate = () => {
|
|
180
|
-
// We don't do anything here, as we handle animation in toggle()
|
|
181
|
-
this.raf = requestAnimationFrame(this.animate);
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
// Our own lerp function since we can't access the private one from PerlinDOM
|
|
185
|
-
lerp(a, b, t) {
|
|
186
|
-
return a + t * (b - a);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export default PerlinSVG;
|