select-animation 1.2.4 → 1.3.0
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/package.json +2 -2
- package/readme.md +11 -9
- package/select-animation.js +38 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "select-animation",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "High-performance JavaScript animation engine for organic & fluid motion",
|
|
5
5
|
"main": "select-animation.js",
|
|
6
6
|
"scripts": {
|
|
@@ -23,4 +23,4 @@
|
|
|
23
23
|
],
|
|
24
24
|
"author": "Housseyn Cheriet",
|
|
25
25
|
"license": "MIT"
|
|
26
|
-
}
|
|
26
|
+
}
|
package/readme.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
# 🚀 Select-Animation
|
|
3
3
|
|
|
4
|
+
[](https://www.npmjs.com/package/select-animation)
|
|
5
|
+
[](https://github.com/housseynCheriet/Select-Animation)
|
|
6
|
+
|
|
7
|
+
A high-performance, lightweight JavaScript animation engine.
|
|
8
|
+
|
|
9
|
+
**Support our project:** [selectjs.vercel.app](https://selectjs.vercel.app)
|
|
10
|
+
**NPM Package:** [npmjs.com/package/select-animation](https://www.npmjs.com/package/select-animation)
|
|
11
|
+
**Source Code:** [github.com/housseynCheriet/Select-Animation](https://github.com/housseynCheriet/Select-Animation)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
4
15
|
## **High‑Performance JavaScript Engine for Organic & Fluid Motion**
|
|
5
16
|
|
|
6
17
|
**Select-Animation** is a lightweight yet powerful JavaScript animation engine built in pure JavaScript, with zero dependencies. Designed for developers who require **full control over timing, easing, looping, and DOM interactions**, it emphasizes **performance, mathematical precision, and flexibility**.
|
|
@@ -233,16 +244,7 @@ Experiment with **Select-Animation** and explore various examples in your browse
|
|
|
233
244
|
|
|
234
245
|
This interactive playground allows you to tweak properties, easing types, and sequences in real-time.
|
|
235
246
|
|
|
236
|
-
---
|
|
237
|
-
|
|
238
|
-
[](https://www.npmjs.com/package/select-animation)
|
|
239
|
-
[](https://github.com/housseynCheriet/Select-Animation)
|
|
240
|
-
|
|
241
|
-
A high-performance, lightweight JavaScript animation engine.
|
|
242
247
|
|
|
243
|
-
**Support our project:** [selectjs.vercel.app](https://selectjs.vercel.app)
|
|
244
|
-
**NPM Package:** [npmjs.com/package/select-animation](https://www.npmjs.com/package/select-animation)
|
|
245
|
-
**Source Code:** [github.com/housseynCheriet/Select-Animation](https://github.com/housseynCheriet/Select-Animation)
|
|
246
248
|
|
|
247
249
|
---
|
|
248
250
|
|
package/select-animation.js
CHANGED
|
@@ -117,54 +117,37 @@
|
|
|
117
117
|
const definitions = arguments;
|
|
118
118
|
const defsCopy = copyObj(definitions);
|
|
119
119
|
|
|
120
|
-
return function (
|
|
121
|
-
let
|
|
120
|
+
return function () {
|
|
121
|
+
let tmp, i, propIndex, fromItem, toItem, valFrom, valTo, t;
|
|
122
122
|
let grouped = [];
|
|
123
|
-
let runtimeGrouped = [];
|
|
124
123
|
let expectNextGroup = false;
|
|
125
124
|
let staging = { color: {}, transform: {}, from: {}, to: {} };
|
|
126
125
|
|
|
126
|
+
// Process the definitions to map elements to their animation settings
|
|
127
127
|
buildPlan();
|
|
128
128
|
|
|
129
129
|
function buildPlan() {
|
|
130
|
-
if (targets !== undefined) {
|
|
131
|
-
if (!Array.isArray(targets)) targets = [targets];
|
|
132
|
-
runtimeGrouped = targets;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
130
|
let secondPass = false;
|
|
136
131
|
|
|
137
132
|
for (let c = 0, total = definitions.length; c < total; c++) {
|
|
138
133
|
propIndex = 0;
|
|
134
|
+
// If current item is an element or an array of elements
|
|
139
135
|
if (expectNextGroup || Array.isArray(defsCopy[c]) || isElement(defsCopy[c])) {
|
|
140
136
|
if (secondPass) {
|
|
141
137
|
Array.isArray(defsCopy[c]) || (defsCopy[c] = [defsCopy[c]]);
|
|
142
138
|
Array.prototype.push.apply(grouped, defsCopy[c]);
|
|
143
139
|
}
|
|
144
140
|
expectNextGroup = false;
|
|
145
|
-
}
|
|
141
|
+
}
|
|
142
|
+
// If current item is a configuration object
|
|
143
|
+
else if (typeof defsCopy[c] === 'object' && defsCopy[c] !== null) {
|
|
146
144
|
if (secondPass) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (typeof targets[u] !== "number") grouped.push(targets[u]);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (targets !== undefined) {
|
|
153
|
-
for (let j = 0; void 0 !== runtimeGrouped[j]; j++) {
|
|
154
|
-
if (typeof runtimeGrouped[j] !== "number") {
|
|
155
|
-
eventCheckResult = checkValEvent(runtimeContext, primaryParam, currentTargets, runtimeGrouped[j + 1]);
|
|
156
|
-
if (eventCheckResult[0]) currentTargets = eventCheckResult[0];
|
|
157
|
-
bindEvent(runtimeGrouped[j], runner(grouped, definitions, defsCopy, c), eventCheckResult[1]);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
} else {
|
|
161
|
-
runner(grouped, definitions, defsCopy, c)();
|
|
162
|
-
}
|
|
145
|
+
// Execute the runner for the current group and config
|
|
146
|
+
runner(grouped, definitions, defsCopy, c)();
|
|
163
147
|
} else {
|
|
164
148
|
expectNextGroup = false;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
149
|
+
|
|
150
|
+
// Handle specialized animation types like vibration
|
|
168
151
|
if (defsCopy[c].typeAnimation === "vibration" && defsCopy[c].vibrationStep === undefined) {
|
|
169
152
|
defsCopy[c].vibrationStep = 6;
|
|
170
153
|
} else {
|
|
@@ -175,20 +158,26 @@
|
|
|
175
158
|
}
|
|
176
159
|
}
|
|
177
160
|
|
|
161
|
+
// Set default loop behavior
|
|
178
162
|
if (defsCopy[c].boucle && defsCopy[c].boucleType === undefined) {
|
|
179
163
|
defsCopy[c].boucleType = "return";
|
|
180
164
|
}
|
|
181
165
|
|
|
166
|
+
// Organize properties and prepare "from" and "to" values
|
|
182
167
|
if (!defsCopy[c].callback &&
|
|
183
168
|
(Array.isArray(defsCopy[c].property) || (defsCopy[c].property !== undefined && (defsCopy[c].property = [defsCopy[c].property])))) {
|
|
184
169
|
|
|
185
170
|
defsCopy[c].property.forEach(function (propItem) {
|
|
171
|
+
const fromIsObject = typeof defsCopy[c].from === "object";
|
|
172
|
+
const toIsObject = typeof defsCopy[c].to === "object";
|
|
173
|
+
|
|
186
174
|
if (!fromIsObject) valFrom = defsCopy[c].from;
|
|
187
175
|
else fromItem = defsCopy[c]["from"][propIndex];
|
|
188
176
|
|
|
189
177
|
if (!toIsObject) valTo = defsCopy[c].to;
|
|
190
178
|
else toItem = defsCopy[c]["to"][propIndex];
|
|
191
179
|
|
|
180
|
+
// Handle complex properties (Colors and Transforms)
|
|
192
181
|
if (typeof propItem === "object") {
|
|
193
182
|
let propName = Object.keys(propItem)[0];
|
|
194
183
|
if (!Array.isArray(propItem[propName])) propItem[propName] = [propItem[propName]];
|
|
@@ -204,6 +193,7 @@
|
|
|
204
193
|
if (propName.toLowerCase() === "transform") staging[propName][innerProp] = 0;
|
|
205
194
|
else staging.color[propName][innerProp] = 0;
|
|
206
195
|
|
|
196
|
+
// Parse specific sub-properties (like scale, rotate, or RGB channels)
|
|
207
197
|
if (fromIsObject) {
|
|
208
198
|
if (fromItem[propName] !== undefined) {
|
|
209
199
|
if (typeof fromItem[propName] === "number") {
|
|
@@ -220,6 +210,7 @@
|
|
|
220
210
|
staging["from"][propName][innerProp] = defsCopy[c].from;
|
|
221
211
|
}
|
|
222
212
|
|
|
213
|
+
// Repeat logic for "to" values
|
|
223
214
|
if (toIsObject) {
|
|
224
215
|
if (toItem[propName] !== undefined) {
|
|
225
216
|
if (typeof toItem[propName] === "number") {
|
|
@@ -240,6 +231,7 @@
|
|
|
240
231
|
propIndex++;
|
|
241
232
|
}
|
|
242
233
|
} else {
|
|
234
|
+
// Handle simple numeric CSS properties (e.g., width, opacity)
|
|
243
235
|
if (fromIsObject) {
|
|
244
236
|
valFrom = staging["from"][propItem] = fromItem[propItem] !== undefined ? fromItem[propItem] : (fromItem !== undefined ? fromItem : valFrom);
|
|
245
237
|
} else {
|
|
@@ -255,14 +247,19 @@
|
|
|
255
247
|
}
|
|
256
248
|
});
|
|
257
249
|
}
|
|
250
|
+
// Save the processed plan into a store for runtime use
|
|
258
251
|
defsCopy[c].storeValueAnim = copyObj(staging);
|
|
259
252
|
staging = { color: {}, transform: {}, from: {}, to: {} };
|
|
260
253
|
}
|
|
254
|
+
|
|
255
|
+
// Check if the next argument starts a new animation sequence
|
|
261
256
|
if (definitions[c + 1] !== undefined && (Array.isArray(definitions[c + 1]) || isElement(definitions[c + 1]))) {
|
|
262
257
|
expectNextGroup = true;
|
|
263
258
|
grouped = [];
|
|
264
259
|
}
|
|
265
260
|
}
|
|
261
|
+
|
|
262
|
+
// Switch to second pass to execute the runners
|
|
266
263
|
if (c === total - 1 && !secondPass) {
|
|
267
264
|
expectNextGroup = false;
|
|
268
265
|
secondPass = true;
|
|
@@ -276,9 +273,11 @@
|
|
|
276
273
|
const declaredAnim = defsCopyLocal[configIndex].typeAnimation;
|
|
277
274
|
let alternateAnim = declaredAnim;
|
|
278
275
|
|
|
276
|
+
// Sanitize timing inputs
|
|
279
277
|
conf.timeline = !isNaN(Number(conf.timeline)) ? Number(conf.timeline) : 0;
|
|
280
278
|
conf.startafter = !isNaN(Number(conf.startafter)) ? Number(conf.startafter) : 0;
|
|
281
279
|
|
|
280
|
+
// Prepare looping and easing alternates
|
|
282
281
|
if (conf.boucle) {
|
|
283
282
|
conf.delay = !isNaN(Number(conf.delay)) ? Number(conf.delay) : undefined;
|
|
284
283
|
if (conf.boucleType === "returnRepeat" || conf.boucleType === "repeatReturn") {
|
|
@@ -291,6 +290,7 @@
|
|
|
291
290
|
let isPaused = false;
|
|
292
291
|
let pauseStart;
|
|
293
292
|
|
|
293
|
+
// Initialize event-based pause/resume logic if configured
|
|
294
294
|
if (conf.pause && Array.isArray(conf.pause)) {
|
|
295
295
|
const eventCfg = conf.pause[1] || "e:click|false";
|
|
296
296
|
const parts = eventCfg.replace('e:', '').split('|');
|
|
@@ -315,6 +315,7 @@
|
|
|
315
315
|
|
|
316
316
|
const startedAt = Date.now();
|
|
317
317
|
|
|
318
|
+
// Initialize state storage for each element in the group
|
|
318
319
|
group.forEach(function (el, idx) {
|
|
319
320
|
if (!el.storeTransform) el.storeTransform = copyObj(defsCopyLocal[configIndex].storeValueAnim.transform);
|
|
320
321
|
if (!el.storeColor) {
|
|
@@ -325,16 +326,19 @@
|
|
|
325
326
|
});
|
|
326
327
|
}
|
|
327
328
|
|
|
329
|
+
// Start staggered animations if timeline offset exists
|
|
328
330
|
if (conf.timeline !== 0) {
|
|
329
331
|
frameRunner([el], idx, conf.timeline * idx + conf.startafter, conf.startafter);
|
|
330
332
|
}
|
|
331
333
|
});
|
|
332
334
|
|
|
335
|
+
// Start simultaneous animations
|
|
333
336
|
if (conf.timeline === 0) {
|
|
334
337
|
frameRunner(group, 0, 0 + conf.startafter, conf.startafter);
|
|
335
338
|
}
|
|
336
339
|
|
|
337
340
|
function frameRunner(targetArray, idx, timeOffset, startAfter) {
|
|
341
|
+
// Manage animation frame cycles
|
|
338
342
|
if (conf.animFram) cancelAnimationFrame(conf.animFram[idx]);
|
|
339
343
|
else conf.animFram = {};
|
|
340
344
|
|
|
@@ -346,6 +350,7 @@
|
|
|
346
350
|
let skipCounter;
|
|
347
351
|
const sv = iterConf.storeValueAnim;
|
|
348
352
|
|
|
353
|
+
// The main animation loop using requestAnimationFrame
|
|
349
354
|
function loop() {
|
|
350
355
|
if (isPaused) {
|
|
351
356
|
conf.animFram[idx] = requestAnimationFrame(loop);
|
|
@@ -357,6 +362,7 @@
|
|
|
357
362
|
const elapsed = Date.now() - (startedAt + timeOffset + pausedAccum);
|
|
358
363
|
|
|
359
364
|
if (elapsed >= 0) {
|
|
365
|
+
// Logic for handling loops, delays, and reversing (yoyo) animations
|
|
360
366
|
if (iterConf.boucle) {
|
|
361
367
|
if (iterConf.delay !== undefined) {
|
|
362
368
|
delay = iterConf.delay;
|
|
@@ -398,7 +404,9 @@
|
|
|
398
404
|
iterConf.timeEasing = elapsed < iterConf.duration ? elapsed : iterConf.duration;
|
|
399
405
|
}
|
|
400
406
|
|
|
407
|
+
// Update properties if not in delay phase
|
|
401
408
|
if (!iterConf.skipDelay || iterConf.skip) {
|
|
409
|
+
// Calculate ease factor (0 to 1)
|
|
402
410
|
eased = Easing[iterConf.changetypeAnim][0](iterConf.timeEasing, 0, 1, iterConf.duration, iterConf, idx);
|
|
403
411
|
|
|
404
412
|
if (iterConf.callback) {
|
|
@@ -406,6 +414,7 @@
|
|
|
406
414
|
iterConf.callback(el, eased, iterConf, idx !== index ? idx : index);
|
|
407
415
|
});
|
|
408
416
|
} else {
|
|
417
|
+
// Apply styles: Transform, Color, or standard Numeric properties
|
|
409
418
|
iterConf.property.forEach(function (prop) {
|
|
410
419
|
css = "";
|
|
411
420
|
let key = typeof prop === "string" ? prop : Object.keys(prop)[0];
|
|
@@ -444,6 +453,7 @@
|
|
|
444
453
|
}
|
|
445
454
|
}
|
|
446
455
|
|
|
456
|
+
// Continue the loop if animation is still active or looping
|
|
447
457
|
if (iterConf.boucle || elapsed < iterConf.duration) {
|
|
448
458
|
conf.animFram[idx] = requestAnimationFrame(loop);
|
|
449
459
|
}
|