@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/index.html
CHANGED
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
background-color: #3498db;
|
|
60
60
|
border-radius: 50%;
|
|
61
61
|
position: absolute;
|
|
62
|
-
left: calc(50% - 40px);
|
|
63
|
-
top: calc(50% - 40px);
|
|
62
|
+
left: calc(50% - 40px);
|
|
63
|
+
top: calc(50% - 40px);
|
|
64
64
|
filter: blur(5px);
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -86,6 +86,32 @@
|
|
|
86
86
|
filter: blur(10px);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/* Positioning for particles - these are initial positions, PerlinDOM will animate from here */
|
|
90
|
+
.particle:nth-child(1) {
|
|
91
|
+
left: 30%;
|
|
92
|
+
top: 30%;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.particle:nth-child(2) {
|
|
96
|
+
left: 50%;
|
|
97
|
+
top: 50%;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.particle:nth-child(3) {
|
|
101
|
+
left: 70%;
|
|
102
|
+
top: 70%;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.particle:nth-child(4) {
|
|
106
|
+
left: 30%;
|
|
107
|
+
top: 70%;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.particle:nth-child(5) {
|
|
111
|
+
left: 70%;
|
|
112
|
+
top: 30%;
|
|
113
|
+
}
|
|
114
|
+
|
|
89
115
|
/* Example 4: Background Effect */
|
|
90
116
|
.bg-container {
|
|
91
117
|
background-color: #34495e;
|
|
@@ -403,11 +429,11 @@
|
|
|
403
429
|
});
|
|
404
430
|
</div>
|
|
405
431
|
<div class="example-container">
|
|
406
|
-
<div class="particle"
|
|
407
|
-
<div class="particle"
|
|
408
|
-
<div class="particle"
|
|
409
|
-
<div class="particle"
|
|
410
|
-
<div class="particle"
|
|
432
|
+
<div class="particle"></div>
|
|
433
|
+
<div class="particle"></div>
|
|
434
|
+
<div class="particle"></div>
|
|
435
|
+
<div class="particle"></div>
|
|
436
|
+
<div class="particle"></div>
|
|
411
437
|
</div>
|
|
412
438
|
|
|
413
439
|
<!-- Example 4: Background Effect -->
|
|
@@ -644,6 +670,96 @@
|
|
|
644
670
|
</div>
|
|
645
671
|
</div>
|
|
646
672
|
|
|
673
|
+
<!-- Example 9: Pixel vs. Percentage Values -->
|
|
674
|
+
<h2>9. Pixel vs. Percentage Values</h2>
|
|
675
|
+
<div class="code-block">
|
|
676
|
+
// Pixel-based animation (fixed size)<br>
|
|
677
|
+
const pixelAnimation = new PerlinDOM({<br>
|
|
678
|
+
element: document.querySelector('.pixel-element'),<br>
|
|
679
|
+
x: { min: -50, max: 50 },<br>
|
|
680
|
+
y: { min: -50, max: 50 },<br>
|
|
681
|
+
speed: 0.01,<br>
|
|
682
|
+
seed: 123<br>
|
|
683
|
+
});<br><br>
|
|
684
|
+
|
|
685
|
+
// Percentage-based animation (responsive)<br>
|
|
686
|
+
const percentageAnimation = new PerlinDOM({<br>
|
|
687
|
+
element: document.querySelector('.percentage-element'),<br>
|
|
688
|
+
x: { min: '-20%', max: '20%' },<br>
|
|
689
|
+
y: { min: '-20%', max: '20%' },<br>
|
|
690
|
+
speed: 0.01,<br>
|
|
691
|
+
seed: 123<br>
|
|
692
|
+
});
|
|
693
|
+
</div>
|
|
694
|
+
<div class="example-container">
|
|
695
|
+
<div style="display: flex; justify-content: space-between; height: 100%;">
|
|
696
|
+
<div style="width: 48%; height: 100%; position: relative; border: 1px dashed #ccc; border-radius: 5px; overflow: hidden;">
|
|
697
|
+
<div style="position: absolute; top: 10px; left: 0; width: 100%; text-align: center; font-weight: bold; color: #7f8c8d; z-index: 10;">Pixel Values (Fixed)</div>
|
|
698
|
+
<div class="pixel-element" style="width: 80px; height: 80px; background-color: #3498db; border-radius: 50%; position: absolute; left: calc(50% - 40px); top: calc(50% - 40px); filter: blur(5px);"></div>
|
|
699
|
+
<div style="position: absolute; bottom: 10px; left: 0; width: 100%; text-align: center; font-size: 12px; color: #7f8c8d;">Resize your browser window - animation range stays the same</div>
|
|
700
|
+
</div>
|
|
701
|
+
<div style="width: 48%; height: 100%; position: relative; border: 1px dashed #ccc; border-radius: 5px; overflow: hidden;">
|
|
702
|
+
<div style="position: absolute; top: 10px; left: 0; width: 100%; text-align: center; font-weight: bold; color: #7f8c8d; z-index: 10;">Percentage Values (Responsive)</div>
|
|
703
|
+
<div class="percentage-element" style="width: 80px; height: 80px; background-color: #e74c3c; border-radius: 50%; position: absolute; left: calc(50% - 40px); top: calc(50% - 40px); filter: blur(5px);"></div>
|
|
704
|
+
<div style="position: absolute; bottom: 10px; left: 0; width: 100%; text-align: center; font-size: 12px; color: #7f8c8d;">Resize your browser window - animation range adapts</div>
|
|
705
|
+
</div>
|
|
706
|
+
</div>
|
|
707
|
+
</div>
|
|
708
|
+
|
|
709
|
+
<!-- Example 10: Resizable Container with Percentage Values -->
|
|
710
|
+
<h2>10. Resizable Container with Percentage Values</h2>
|
|
711
|
+
<div class="code-block">
|
|
712
|
+
const responsiveAnimation = new PerlinDOM({<br>
|
|
713
|
+
element: document.querySelector('.responsive-element'),<br>
|
|
714
|
+
x: { min: '-25%', max: '25%' },<br>
|
|
715
|
+
y: { min: '-25%', max: '25%' },<br>
|
|
716
|
+
speed: 0.008,<br>
|
|
717
|
+
seed: 456<br>
|
|
718
|
+
});
|
|
719
|
+
</div>
|
|
720
|
+
<div class="example-container">
|
|
721
|
+
<div style="height: 100%; position: relative; border: 1px solid #ddd; border-radius: 5px; overflow: hidden; resize: both; min-width: 200px; min-height: 200px; background-color: #f0f0f0;">
|
|
722
|
+
<div class="responsive-element" style="width: 60px; height: 60px; background-color: #9b59b6; border-radius: 50%; position: absolute; left: calc(50% - 30px); top: calc(50% - 30px);"></div>
|
|
723
|
+
<div style="position: absolute; bottom: 10px; right: 10px; font-size: 12px; color: #7f8c8d;">↘️ Drag this corner to resize the container</div>
|
|
724
|
+
</div>
|
|
725
|
+
</div>
|
|
726
|
+
|
|
727
|
+
<!-- Example 11: Mixed Pixel and Percentage Values -->
|
|
728
|
+
<h2>11. Mixed Pixel and Percentage Values</h2>
|
|
729
|
+
<div class="code-block">
|
|
730
|
+
// Horizontal percentage, vertical pixel<br>
|
|
731
|
+
const horizontalPercentAnimation = new PerlinDOM({<br>
|
|
732
|
+
element: document.querySelector('.horizontal-percent-element'),<br>
|
|
733
|
+
x: { min: '-30%', max: '30%' },<br>
|
|
734
|
+
y: { min: -40, max: 40 },<br>
|
|
735
|
+
speed: 0.01,<br>
|
|
736
|
+
seed: 789<br>
|
|
737
|
+
});<br><br>
|
|
738
|
+
|
|
739
|
+
// Horizontal pixel, vertical percentage<br>
|
|
740
|
+
const verticalPercentAnimation = new PerlinDOM({<br>
|
|
741
|
+
element: document.querySelector('.vertical-percent-element'),<br>
|
|
742
|
+
x: { min: -80, max: 80 },<br>
|
|
743
|
+
y: { min: '-20%', max: '20%' },<br>
|
|
744
|
+
speed: 0.01,<br>
|
|
745
|
+
seed: 789<br>
|
|
746
|
+
});
|
|
747
|
+
</div>
|
|
748
|
+
<div class="example-container">
|
|
749
|
+
<div style="display: flex; justify-content: space-between; height: 100%;">
|
|
750
|
+
<div style="width: 48%; height: 100%; position: relative; border: 1px dashed #ccc; border-radius: 5px; overflow: hidden;">
|
|
751
|
+
<div style="position: absolute; top: 10px; left: 0; width: 100%; text-align: center; font-weight: bold; color: #7f8c8d; z-index: 10;">Horizontal % / Vertical px</div>
|
|
752
|
+
<div class="horizontal-percent-element" style="width: 60px; height: 60px; background-color: #f39c12; border-radius: 10px; position: absolute; left: calc(50% - 30px); top: calc(50% - 30px);"></div>
|
|
753
|
+
<div style="position: absolute; bottom: 10px; left: 0; width: 100%; text-align: center; font-size: 12px; color: #7f8c8d;">Horizontal movement adapts to width, vertical is fixed</div>
|
|
754
|
+
</div>
|
|
755
|
+
<div style="width: 48%; height: 100%; position: relative; border: 1px dashed #ccc; border-radius: 5px; overflow: hidden;">
|
|
756
|
+
<div style="position: absolute; top: 10px; left: 0; width: 100%; text-align: center; font-weight: bold; color: #7f8c8d; z-index: 10;">Horizontal px / Vertical %</div>
|
|
757
|
+
<div class="vertical-percent-element" style="width: 60px; height: 60px; background-color: #f39c12; border-radius: 10px; position: absolute; left: calc(50% - 30px); top: calc(50% - 30px);"></div>
|
|
758
|
+
<div style="position: absolute; bottom: 10px; left: 0; width: 100%; text-align: center; font-size: 12px; color: #7f8c8d;">Horizontal movement is fixed, vertical adapts to height</div>
|
|
759
|
+
</div>
|
|
760
|
+
</div>
|
|
761
|
+
</div>
|
|
762
|
+
|
|
647
763
|
<!-- Load all scripts -->
|
|
648
764
|
<script type="module" src="./js/index.js"></script>
|
|
649
765
|
<script type="module" src="./js/basic-examples.js"></script>
|
|
@@ -651,5 +767,51 @@
|
|
|
651
767
|
<script type="module" src="./js/interactive-controls.js"></script>
|
|
652
768
|
<script type="module" src="./js/animated-button.js"></script>
|
|
653
769
|
<script type="module" src="./js/triangle-animation.js"></script>
|
|
770
|
+
<script type="module">
|
|
771
|
+
import PerlinDOM from './js/PerlinDom.js';
|
|
772
|
+
|
|
773
|
+
// Example 9: Pixel vs. Percentage Values
|
|
774
|
+
const pixelAnimation = new PerlinDOM({
|
|
775
|
+
element: document.querySelector('.pixel-element'),
|
|
776
|
+
x: { min: -50, max: 50 },
|
|
777
|
+
y: { min: -50, max: 50 },
|
|
778
|
+
speed: 0.01,
|
|
779
|
+
seed: 123
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
const percentageAnimation = new PerlinDOM({
|
|
783
|
+
element: document.querySelector('.percentage-element'),
|
|
784
|
+
x: { min: '-20%', max: '20%' },
|
|
785
|
+
y: { min: '-20%', max: '20%' },
|
|
786
|
+
speed: 0.01,
|
|
787
|
+
seed: 123
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
// Example 10: Resizable Container
|
|
791
|
+
const responsiveAnimation = new PerlinDOM({
|
|
792
|
+
element: document.querySelector('.responsive-element'),
|
|
793
|
+
x: { min: '-25%', max: '25%' },
|
|
794
|
+
y: { min: '-25%', max: '25%' },
|
|
795
|
+
speed: 0.008,
|
|
796
|
+
seed: 456
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
// Example 11: Mixed Values
|
|
800
|
+
const horizontalPercentAnimation = new PerlinDOM({
|
|
801
|
+
element: document.querySelector('.horizontal-percent-element'),
|
|
802
|
+
x: { min: '-30%', max: '30%' },
|
|
803
|
+
y: { min: -40, max: 40 },
|
|
804
|
+
speed: 0.01,
|
|
805
|
+
seed: 789
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
const verticalPercentAnimation = new PerlinDOM({
|
|
809
|
+
element: document.querySelector('.vertical-percent-element'),
|
|
810
|
+
x: { min: -80, max: 80 },
|
|
811
|
+
y: { min: '-20%', max: '20%' },
|
|
812
|
+
speed: 0.01,
|
|
813
|
+
seed: 789
|
|
814
|
+
});
|
|
815
|
+
</script>
|
|
654
816
|
</body>
|
|
655
817
|
</html>
|
package/src/js/PerlinDom.js
CHANGED
|
@@ -18,6 +18,18 @@ class PerlinDOM {
|
|
|
18
18
|
}
|
|
19
19
|
return permutation.concat(permutation);
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
// Helper to check if a value is a percentage string
|
|
23
|
+
static #isPercentage(value) {
|
|
24
|
+
return typeof value === 'string' && value.endsWith('%');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Helper to convert percentage to pixels
|
|
28
|
+
static #percentToPixels(percentage, dimension) {
|
|
29
|
+
if (!PerlinDOM.#isPercentage(percentage)) return percentage;
|
|
30
|
+
const value = parseFloat(percentage);
|
|
31
|
+
return (value / 100) * dimension;
|
|
32
|
+
}
|
|
21
33
|
|
|
22
34
|
constructor({
|
|
23
35
|
element,
|
|
@@ -38,14 +50,33 @@ class PerlinDOM {
|
|
|
38
50
|
this.targetLerpFactor = 1; // Target state (1 = play, 0 = pause)
|
|
39
51
|
this.lastX = 0;
|
|
40
52
|
this.lastY = 0;
|
|
53
|
+
|
|
54
|
+
// Check if we have percentage values
|
|
55
|
+
this.hasPercentages = false;
|
|
56
|
+
if (x && (PerlinDOM.#isPercentage(x.min) || PerlinDOM.#isPercentage(x.max)) ||
|
|
57
|
+
y && (PerlinDOM.#isPercentage(y.min) || PerlinDOM.#isPercentage(y.max))) {
|
|
58
|
+
this.hasPercentages = true;
|
|
59
|
+
}
|
|
41
60
|
|
|
42
61
|
// Solo inicializar estas propiedades si el elemento no es null
|
|
43
62
|
if (element) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
// Ensure element has positioning for transform to work properly
|
|
64
|
+
const computedStyle = window.getComputedStyle(element);
|
|
65
|
+
if (computedStyle.position === 'static') {
|
|
66
|
+
element.style.position = 'relative';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Initialize transform if it doesn't exist
|
|
70
|
+
const currentTransform = computedStyle.transform;
|
|
71
|
+
if (!currentTransform || currentTransform === 'none') {
|
|
72
|
+
element.style.transform = 'translate3d(0px, 0px, 0px)';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// If using percentages, add resize listener
|
|
76
|
+
if (this.hasPercentages) {
|
|
77
|
+
this.handleResize = this.handleResize.bind(this);
|
|
78
|
+
window.addEventListener('resize', this.handleResize);
|
|
79
|
+
}
|
|
49
80
|
}
|
|
50
81
|
|
|
51
82
|
this.animate = this.animate.bind(this);
|
|
@@ -62,6 +93,15 @@ class PerlinDOM {
|
|
|
62
93
|
console.log('PerlinDOM initialized');
|
|
63
94
|
}
|
|
64
95
|
|
|
96
|
+
// Handle window resize for percentage-based values
|
|
97
|
+
handleResize() {
|
|
98
|
+
// Only recalculate if we're using percentages
|
|
99
|
+
if (this.hasPercentages && this.el) {
|
|
100
|
+
// Force a recalculation on next animation frame
|
|
101
|
+
this.needsRecalculation = true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
65
105
|
pause() {
|
|
66
106
|
if (this.isPlaying) {
|
|
67
107
|
this.targetLerpFactor = 0; // Target paused state
|
|
@@ -178,23 +218,72 @@ class PerlinDOM {
|
|
|
178
218
|
const noiseX = this.noise.perlin2(this.time, 0);
|
|
179
219
|
const noiseY = this.noise.perlin2(this.time, 100); // Different slice of the noise field
|
|
180
220
|
|
|
181
|
-
|
|
182
|
-
? this.#map(noiseX, -1, 1, this.xRange.min, this.xRange.max)
|
|
183
|
-
: 0;
|
|
184
|
-
|
|
185
|
-
const valY = this.yRange
|
|
186
|
-
? this.#map(noiseY, -1, 1, this.yRange.min, this.yRange.max)
|
|
187
|
-
: 0;
|
|
188
|
-
|
|
189
|
-
// Store current values for interpolation
|
|
190
|
-
this.lastX = valX;
|
|
191
|
-
this.lastY = valY;
|
|
192
|
-
|
|
193
|
-
// Update position using left/top instead of transform
|
|
194
|
-
// Solo actualizar el elemento si existe
|
|
221
|
+
// Only process if we have an element
|
|
195
222
|
if (this.el) {
|
|
196
|
-
|
|
197
|
-
|
|
223
|
+
let minX, maxX, minY, maxY;
|
|
224
|
+
|
|
225
|
+
// Get parent dimensions for percentage calculations if needed
|
|
226
|
+
if (this.hasPercentages) {
|
|
227
|
+
const parent = this.el.parentElement || document.body;
|
|
228
|
+
const parentWidth = parent.clientWidth;
|
|
229
|
+
const parentHeight = parent.clientHeight;
|
|
230
|
+
|
|
231
|
+
// Convert percentage values to pixels if needed
|
|
232
|
+
if (this.xRange) {
|
|
233
|
+
minX = PerlinDOM.#isPercentage(this.xRange.min)
|
|
234
|
+
? PerlinDOM.#percentToPixels(this.xRange.min, parentWidth)
|
|
235
|
+
: this.xRange.min;
|
|
236
|
+
|
|
237
|
+
maxX = PerlinDOM.#isPercentage(this.xRange.max)
|
|
238
|
+
? PerlinDOM.#percentToPixels(this.xRange.max, parentWidth)
|
|
239
|
+
: this.xRange.max;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (this.yRange) {
|
|
243
|
+
minY = PerlinDOM.#isPercentage(this.yRange.min)
|
|
244
|
+
? PerlinDOM.#percentToPixels(this.yRange.min, parentHeight)
|
|
245
|
+
: this.yRange.min;
|
|
246
|
+
|
|
247
|
+
maxY = PerlinDOM.#isPercentage(this.yRange.max)
|
|
248
|
+
? PerlinDOM.#percentToPixels(this.yRange.max, parentHeight)
|
|
249
|
+
: this.yRange.max;
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
// Use the original values if not using percentages
|
|
253
|
+
minX = this.xRange?.min;
|
|
254
|
+
maxX = this.xRange?.max;
|
|
255
|
+
minY = this.yRange?.min;
|
|
256
|
+
maxY = this.yRange?.max;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Calculate the actual pixel values
|
|
260
|
+
const valX = this.xRange
|
|
261
|
+
? this.#map(noiseX, -1, 1, minX, maxX)
|
|
262
|
+
: 0;
|
|
263
|
+
|
|
264
|
+
const valY = this.yRange
|
|
265
|
+
? this.#map(noiseY, -1, 1, minY, maxY)
|
|
266
|
+
: 0;
|
|
267
|
+
|
|
268
|
+
// Store current values for interpolation
|
|
269
|
+
this.lastX = valX;
|
|
270
|
+
this.lastY = valY;
|
|
271
|
+
|
|
272
|
+
// Update position using transform for better performance
|
|
273
|
+
this.el.style.transform = `translate3d(${valX}px, ${valY}px, 0)`;
|
|
274
|
+
} else {
|
|
275
|
+
// If no element, just calculate and store the values
|
|
276
|
+
const valX = this.xRange
|
|
277
|
+
? this.#map(noiseX, -1, 1, this.xRange.min, this.xRange.max)
|
|
278
|
+
: 0;
|
|
279
|
+
|
|
280
|
+
const valY = this.yRange
|
|
281
|
+
? this.#map(noiseY, -1, 1, this.yRange.min, this.yRange.max)
|
|
282
|
+
: 0;
|
|
283
|
+
|
|
284
|
+
// Store current values for interpolation
|
|
285
|
+
this.lastX = valX;
|
|
286
|
+
this.lastY = valY;
|
|
198
287
|
}
|
|
199
288
|
}
|
|
200
289
|
|
|
@@ -210,6 +299,11 @@ class PerlinDOM {
|
|
|
210
299
|
|
|
211
300
|
destroy() {
|
|
212
301
|
cancelAnimationFrame(this.raf);
|
|
302
|
+
|
|
303
|
+
// Remove resize listener if it was added
|
|
304
|
+
if (this.hasPercentages) {
|
|
305
|
+
window.removeEventListener('resize', this.handleResize);
|
|
306
|
+
}
|
|
213
307
|
}
|
|
214
308
|
}
|
|
215
309
|
export default PerlinDOM;
|