@andresclua/perlindom 0.0.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.
@@ -0,0 +1,215 @@
1
+ class PerlinDOM {
2
+ /* ---------- helpers privados y estáticos ---------- */
3
+ static #mulberry32(a) {
4
+ return function () {
5
+ let t = (a += 0x6d2b79f5);
6
+ t = Math.imul(t ^ (t >>> 15), t | 1);
7
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
8
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
9
+ };
10
+ }
11
+
12
+ static #generatePermutation(seed = 0) {
13
+ const permutation = Array.from({ length: 256 }, (_, i) => i);
14
+ const rand = PerlinDOM.#mulberry32(seed);
15
+ for (let i = 255; i > 0; i--) {
16
+ const j = Math.floor(rand() * (i + 1));
17
+ [permutation[i], permutation[j]] = [permutation[j], permutation[i]];
18
+ }
19
+ return permutation.concat(permutation);
20
+ }
21
+
22
+ constructor({
23
+ element,
24
+ x = null,
25
+ y = null,
26
+ speed = 0.01,
27
+ seed = 0,
28
+ lerpSpeed = 0.1
29
+ }) {
30
+ this.el = element;
31
+ this.xRange = x;
32
+ this.yRange = y;
33
+ this.speed = speed;
34
+ this.time = seed;
35
+ this.isPlaying = true;
36
+ this.lerpFactor = 1; // 1 = full speed, 0 = paused
37
+ this.lerpSpeed = lerpSpeed; // How fast to transition between play/pause states
38
+ this.targetLerpFactor = 1; // Target state (1 = play, 0 = pause)
39
+ this.lastX = 0;
40
+ this.lastY = 0;
41
+
42
+ // Solo inicializar estas propiedades si el elemento no es null
43
+ if (element) {
44
+ this.initialX = element.offsetLeft;
45
+ this.initialY = element.offsetTop;
46
+ } else {
47
+ this.initialX = 0;
48
+ this.initialY = 0;
49
+ }
50
+
51
+ this.animate = this.animate.bind(this);
52
+
53
+ this.init(seed);
54
+ }
55
+
56
+ init(seed) {
57
+ this.noise = this.#createPerlin(PerlinDOM.#generatePermutation(seed));
58
+ this.isPlaying = true;
59
+ this.lerpFactor = 1;
60
+ this.targetLerpFactor = 1;
61
+ this.raf = requestAnimationFrame(this.animate);
62
+ console.log('PerlinDOM initialized');
63
+ }
64
+
65
+ pause() {
66
+ if (this.isPlaying) {
67
+ this.targetLerpFactor = 0; // Target paused state
68
+ this.isPlaying = false;
69
+ console.log('PerlinDOM pausing...');
70
+ }
71
+ }
72
+
73
+ play() {
74
+ if (!this.isPlaying) {
75
+ this.targetLerpFactor = 1; // Target playing state
76
+ this.isPlaying = true;
77
+
78
+ // Only start a new animation frame if we're completely paused
79
+ if (this.lerpFactor === 0) {
80
+ this.raf = requestAnimationFrame(this.animate);
81
+ }
82
+
83
+ console.log('PerlinDOM playing...');
84
+ }
85
+ }
86
+
87
+ #createPerlin(p) {
88
+ const fade = t => t * t * t * (t * (t * 6 - 15) + 10);
89
+ const lerp = (t, a, b) => a + t * (b - a);
90
+
91
+ // Enhanced gradient function for 2D
92
+ const grad2D = (hash, x, y) => {
93
+ const h = hash & 15;
94
+ const u = h < 8 ? x : y;
95
+ const v = h < 4 ? y : (h === 12 || h === 14 ? x : 0);
96
+ return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
97
+ };
98
+
99
+ // 1D gradient (kept for backward compatibility)
100
+ const grad1D = (hash, x) => (hash & 1 ? -x : x);
101
+
102
+ return {
103
+ // Original 1D Perlin noise function
104
+ perlin1(x) {
105
+ const X = Math.floor(x) & 255;
106
+ x -= Math.floor(x);
107
+ const u = fade(x);
108
+ return lerp(u, grad1D(p[X], x), grad1D(p[X + 1], x - 1));
109
+ },
110
+
111
+ // New 2D Perlin noise function
112
+ perlin2(x, y) {
113
+ // Integer parts of coordinates
114
+ const X = Math.floor(x) & 255;
115
+ const Y = Math.floor(y) & 255;
116
+
117
+ // Fractional parts of coordinates
118
+ x -= Math.floor(x);
119
+ y -= Math.floor(y);
120
+
121
+ // Fade curves for x and y
122
+ const u = fade(x);
123
+ const v = fade(y);
124
+
125
+ // Hash coordinates of the 4 square corners
126
+ const A = p[X] + Y;
127
+ const B = p[X + 1] + Y;
128
+ const AA = p[A];
129
+ const AB = p[A + 1];
130
+ const BA = p[B];
131
+ const BB = p[B + 1];
132
+
133
+ // Blend the four corner gradients
134
+ return lerp(
135
+ v,
136
+ lerp(
137
+ u,
138
+ grad2D(p[AA], x, y),
139
+ grad2D(p[BA], x - 1, y)
140
+ ),
141
+ lerp(
142
+ u,
143
+ grad2D(p[AB], x, y - 1),
144
+ grad2D(p[BB], x - 1, y - 1)
145
+ )
146
+ );
147
+ }
148
+ };
149
+ }
150
+
151
+ #lerp(a, b, t) {
152
+ return a + t * (b - a);
153
+ }
154
+
155
+ animate = () => {
156
+ // Update lerp factor towards target
157
+ if (this.lerpFactor !== this.targetLerpFactor) {
158
+ this.lerpFactor = this.#lerp(
159
+ this.lerpFactor,
160
+ this.targetLerpFactor,
161
+ this.lerpSpeed
162
+ );
163
+
164
+ // Snap to target if very close
165
+ if (Math.abs(this.lerpFactor - this.targetLerpFactor) < 0.001) {
166
+ this.lerpFactor = this.targetLerpFactor;
167
+ }
168
+ }
169
+
170
+ // Only update time if not completely paused
171
+ if (this.lerpFactor > 0) {
172
+ // Scale time increment by lerp factor for smooth speed transition
173
+ this.time += this.speed * this.lerpFactor;
174
+
175
+ // Use 2D Perlin noise for more natural movement
176
+ // We use time for one dimension and a fixed offset for the other
177
+ // This creates a path through the 2D noise field
178
+ const noiseX = this.noise.perlin2(this.time, 0);
179
+ const noiseY = this.noise.perlin2(this.time, 100); // Different slice of the noise field
180
+
181
+ const valX = this.xRange
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
195
+ if (this.el) {
196
+ this.el.style.left = `${this.initialX + valX}px`;
197
+ this.el.style.top = `${this.initialY + valY}px`;
198
+ }
199
+ }
200
+
201
+ // Continue animation if not completely paused or still transitioning
202
+ if (this.lerpFactor > 0 || this.targetLerpFactor > 0) {
203
+ this.raf = requestAnimationFrame(this.animate);
204
+ }
205
+ };
206
+
207
+ #map(n, inMin, inMax, outMin, outMax) {
208
+ return ((n - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
209
+ }
210
+
211
+ destroy() {
212
+ cancelAnimationFrame(this.raf);
213
+ }
214
+ }
215
+ export default PerlinDOM;
@@ -0,0 +1,190 @@
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;
@@ -0,0 +1,40 @@
1
+ // Example 7: Animated Button with Circles
2
+ import PerlinDOM from './PerlinDom.js';
3
+
4
+ document.addEventListener('DOMContentLoaded', () => {
5
+ // Get all the circle elements inside the button
6
+ const circleElements = document.querySelectorAll('.animated-button .circle');
7
+
8
+ // Create an array to store the animation instances
9
+ let circleAnimations = [];
10
+
11
+ // Function to initialize animations
12
+ function initCircleAnimations() {
13
+ // Clear any existing animations
14
+ circleAnimations.forEach(anim => anim.destroy());
15
+ circleAnimations = [];
16
+
17
+ // Create new animations for each circle
18
+ circleElements.forEach((el, index) => {
19
+ const animation = new PerlinDOM({
20
+ element: el,
21
+ x: { min: -15, max: 15 },
22
+ y: { min: -15, max: 15 },
23
+ speed: 0.007,
24
+ seed: index * 1000,
25
+ lerpSpeed: 0.2 * index
26
+ });
27
+ circleAnimations.push(animation);
28
+ });
29
+ }
30
+
31
+ // Add event listeners to the button
32
+ document.querySelector('.animated-button')?.addEventListener('mouseenter', () => {
33
+ initCircleAnimations();
34
+ circleAnimations.forEach(anim => anim.play());
35
+ });
36
+
37
+ document.querySelector('.animated-button')?.addEventListener('mouseleave', () => {
38
+ circleAnimations.forEach(anim => anim.pause());
39
+ });
40
+ });
@@ -0,0 +1,43 @@
1
+ // Examples 1-4: Basic PerlinDOM Examples
2
+ import PerlinDOM from './PerlinDom.js';
3
+
4
+ document.addEventListener('DOMContentLoaded', () => {
5
+ // Example 1: Basic Animation
6
+ const basicAnimation = new PerlinDOM({
7
+ element: document.querySelector('.basic-element'),
8
+ x: { min: -50, max: 50 },
9
+ y: { min: -50, max: 50 },
10
+ speed: 0.01,
11
+ seed: 123
12
+ });
13
+
14
+ // Example 2: Horizontal Only
15
+ const horizontalAnimation = new PerlinDOM({
16
+ element: document.querySelector('.horizontal-element'),
17
+ x: { min: -100, max: 100 },
18
+ y: null, // No vertical movement
19
+ speed: 0.02
20
+ });
21
+
22
+ // Example 3: Multiple Elements
23
+ document.querySelectorAll('.particle').forEach((el, index) => {
24
+ new PerlinDOM({
25
+ element: el,
26
+ x: { min: -100, max: 100 + index * 20 }, // Slightly wider range for each element
27
+ y: { min: -100, max: 120}, // Random vertical range
28
+ speed: index * 0.001, // Slightly different speed for each element
29
+ seed: index * 16700 // Different seed for each element
30
+ });
31
+ });
32
+
33
+ // Example 4: Background Effect
34
+ document.querySelectorAll('.bg-element').forEach(el => {
35
+ new PerlinDOM({
36
+ element: el,
37
+ x: { min: -110, max: 110 },
38
+ y: { min: -110, max: 110 },
39
+ speed: 0.002, // Very slow movement
40
+ seed: Math.random() * 2000
41
+ });
42
+ });
43
+ });
@@ -0,0 +1,13 @@
1
+ import PerlinDOM from "./PerlinDom";
2
+ console.log('PerlinDOM loaded');
3
+
4
+ const elements = document.querySelectorAll('.box');
5
+ elements.forEach(el =>
6
+ new PerlinDOM({
7
+ element: el,
8
+ x: { min: -120, max: 120 },
9
+ y: { min: 0, max: 120 },
10
+ speed: 0.001,
11
+ seed: 1
12
+ })
13
+ );
@@ -0,0 +1,43 @@
1
+ // Example 6: Interactive Controls
2
+ import PerlinDOM from './PerlinDom.js';
3
+
4
+ document.addEventListener('DOMContentLoaded', () => {
5
+ let interactiveAnimation;
6
+
7
+ function initInteractiveAnimation() {
8
+ // Destroy previous instance if it exists
9
+ if (interactiveAnimation) {
10
+ interactiveAnimation.destroy();
11
+ }
12
+
13
+ // Get current values
14
+ const xRange = parseInt(document.getElementById('x-range').value);
15
+ const yRange = parseInt(document.getElementById('y-range').value);
16
+ const speed = parseFloat(document.getElementById('speed').value);
17
+ const seed = parseInt(document.getElementById('seed').value);
18
+
19
+ // Create new instance
20
+ interactiveAnimation = new PerlinDOM({
21
+ element: document.querySelector('.interactive-element'),
22
+ x: { min: -xRange, max: xRange },
23
+ y: { min: -yRange, max: yRange },
24
+ speed: speed,
25
+ seed: seed
26
+ });
27
+
28
+ // Update displays
29
+ document.querySelector('#x-range + .value-display').textContent = `-${xRange} to ${xRange}`;
30
+ document.querySelector('#y-range + .value-display').textContent = `-${yRange} to ${yRange}`;
31
+ document.querySelector('#speed + .value-display').textContent = speed.toFixed(3);
32
+ document.querySelector('#seed + .value-display').textContent = seed;
33
+ }
34
+
35
+ // Initialize
36
+ initInteractiveAnimation();
37
+
38
+ // Add event listeners
39
+ document.getElementById('x-range')?.addEventListener('input', initInteractiveAnimation);
40
+ document.getElementById('y-range')?.addEventListener('input', initInteractiveAnimation);
41
+ document.getElementById('speed')?.addEventListener('input', initInteractiveAnimation);
42
+ document.getElementById('seed')?.addEventListener('input', initInteractiveAnimation);
43
+ });
@@ -0,0 +1,33 @@
1
+ // Example 5: Pause and Play with Mouse Events
2
+ import PerlinDOM from './PerlinDom.js';
3
+
4
+ document.addEventListener('DOMContentLoaded', () => {
5
+ // Create the animation
6
+ const pausePlayAnimation = new PerlinDOM({
7
+ element: document.querySelector('.pause-play-element'),
8
+ x: { min: -80, max: 80 },
9
+ y: { min: -180, max: 80 },
10
+ speed: 0.01,
11
+ seed: 42,
12
+ lerpSpeed: 0.05 // Controls how smooth the transition is
13
+ });
14
+
15
+ // Pause on mouse leave
16
+ document.querySelector('.pause-play-container')?.addEventListener('mouseleave', () => {
17
+ pausePlayAnimation.pause();
18
+ document.querySelector('.status-indicator').textContent = 'Pausing...';
19
+ });
20
+
21
+ // Play on mouse enter
22
+ document.querySelector('.pause-play-container')?.addEventListener('mouseenter', () => {
23
+ pausePlayAnimation.play();
24
+ document.querySelector('.status-indicator').textContent = 'Playing...';
25
+ });
26
+
27
+ // Update lerpSpeed when slider changes
28
+ document.getElementById('lerp-speed')?.addEventListener('input', (e) => {
29
+ const newSpeed = parseFloat(e.target.value);
30
+ pausePlayAnimation.lerpSpeed = newSpeed;
31
+ document.querySelector('.lerp-value').textContent = newSpeed.toFixed(2);
32
+ });
33
+ });
@@ -0,0 +1,48 @@
1
+ // Example 8: ClipPath Triangle with Animated Vertex
2
+ import PerlinDOM from './PerlinDom.js';
3
+
4
+ document.addEventListener('DOMContentLoaded', () => {
5
+ // Get the polygon element
6
+ const trianglePath = document.getElementById('trianglePath');
7
+
8
+ if (!trianglePath) return;
9
+
10
+ // Store the original points
11
+ const originalPoints = {
12
+ x1: 150, y1: 50, // Top vertex
13
+ x2: 250, y2: 250, // Bottom right
14
+ x3: 50, y3: 250 // Bottom left
15
+ };
16
+
17
+ // Create a custom animation function
18
+ function animateTriangle() {
19
+ // Create a PerlinDOM instance for the top vertex
20
+ const topVertexAnimation = new PerlinDOM({
21
+ element: null, // We're not moving a DOM element directly
22
+ x: { min: -30, max: 30 },
23
+ y: { min: -20, max: 20 },
24
+ speed: 0.005,
25
+ seed: 42
26
+ });
27
+
28
+ // Animation function
29
+ function updateTriangle() {
30
+ // Get the current noise values
31
+ const offsetX = topVertexAnimation.lastX;
32
+ const offsetY = topVertexAnimation.lastY;
33
+
34
+ // Update the polygon points
35
+ const newPoints = `${originalPoints.x1 + offsetX},${originalPoints.y1 + offsetY} ${originalPoints.x2},${originalPoints.y2} ${originalPoints.x3},${originalPoints.y3}`;
36
+ trianglePath.setAttribute('points', newPoints);
37
+
38
+ // Continue animation
39
+ requestAnimationFrame(updateTriangle);
40
+ }
41
+
42
+ // Start the animation
43
+ updateTriangle();
44
+ }
45
+
46
+ // Initialize the animation
47
+ animateTriangle();
48
+ });
@@ -0,0 +1,20 @@
1
+ .spacer{
2
+ padding:120px;
3
+ border:1px solid red;
4
+ position:relative;
5
+ overflow: hidden;
6
+ }
7
+
8
+
9
+ .box{
10
+ width:120px;
11
+ height:120px;
12
+ border-radius: 50%;
13
+ background-color: blue;
14
+ position:absolute;
15
+ left:0;
16
+ top:-20%;
17
+ filter: blur(40px) drop-shadow(0 0 20px rgba(0, 0, 255, 0.5));
18
+
19
+
20
+ }
package/vite.config.js ADDED
@@ -0,0 +1,36 @@
1
+ import { defineConfig } from 'vite';
2
+ import { resolve } from 'path';
3
+
4
+ const root = resolve(__dirname, 'src');
5
+ const outDir = resolve(__dirname, 'dist');
6
+ const entryPath = resolve(__dirname, 'src/js/PerlinDOM.js');
7
+
8
+ export default defineConfig({
9
+ root,
10
+ publicDir: 'public',
11
+ plugins: [],
12
+ build: {
13
+ lib: {
14
+ entry: entryPath,
15
+ name: 'PerlinDOM', // Library name as : Counter | etc
16
+ formats: ['es', 'umd'], // Output formats as : es | umd, es is for modern browsers, umd is for older browsers
17
+ fileName: (format,name) => `PerlinDOM.${format}.js`, // File naming as : reveal-it | collapsify | etc
18
+ },
19
+ outDir,
20
+ emptyOutDir: true,
21
+ // rollupOptions: {
22
+
23
+ // output: {
24
+ // name: 'Validate',
25
+ // },
26
+ // },
27
+ },
28
+ resolve: {
29
+ alias: {
30
+ '@': resolve(__dirname, './src'),
31
+ '@js': resolve(__dirname, './src/js'),
32
+ '@scss': resolve(__dirname, './src/scss'),
33
+ '@assets': resolve(__dirname, './src/assets'),
34
+ },
35
+ },
36
+ });