chnobli 0.1.2 → 0.1.4

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.
@@ -1,11 +1,44 @@
1
- import Value from '../utils/value.js';
1
+ import Value from '../values/value.js';
2
+ import ColorValue from '../values/colorvalue.js';
3
+ import StyleValue from '../values/stylevalue.js';
2
4
 
3
5
  export default class Property {
4
6
  constructor(name) {
5
7
  // this is the property name
6
8
  this.name = name;
9
+
10
+ this.from = null;
11
+ this.to = null;
12
+ }
13
+
14
+ allowsValueFn() {
15
+ return true;
16
+ }
17
+
18
+ /**
19
+ * From or to might be null
20
+ */
21
+ init(target, from, to) {
22
+ if (!from)
23
+ from = this.getValue(target);
24
+
25
+ if (!to)
26
+ to = this.getValue(target);
27
+
28
+ if (!from || !to)
29
+ throw new Error('could not determine from or to value');
30
+
31
+ const [nFrom, nTo] = target.unifyValues(this.name, from, to);
32
+
33
+ this.from = nFrom;
34
+ this.to = nTo;
7
35
  }
8
36
 
37
+ /**
38
+ * Gets called with the value of a property
39
+ *
40
+ * For examply during initializiation
41
+ */
9
42
  parseValue(val) {
10
43
  return Value.parse(val);
11
44
  }
@@ -23,65 +56,66 @@ export default class Property {
23
56
  return val;
24
57
  }
25
58
 
59
+ /**
60
+ * Pos needs to be between 0 and 1
61
+ */
62
+ interpolate(pos) {
63
+ const dif = this.to.num - this.from.num;
64
+
65
+ return this.from.cloneAdd(pos * dif);
66
+ }
67
+
26
68
  setValue(target, val) {
27
69
  return target.setValue(this.name, val);
28
70
  }
29
71
 
30
72
  removeValue(target) {
31
- // todo remove
32
73
  return target.removeValue(this.name);
33
74
  }
34
75
  }
35
76
 
36
- const TRANSFORM_PROPS = ['x', 'y', 'scale', 'scaleX', 'scaleY', 'rotate'];
77
+ const TRANSFORM_PROPS = {
78
+ 'x': [0, 'px'],
79
+ 'y': [0, 'px'],
80
+ 'z': [0, 'px'],
81
+ 'scale': [1, null],
82
+ 'scaleX': [1, null],
83
+ 'scaleY': [1, null],
84
+ 'scaleZ': [1, null],
85
+ 'rotate': [0, 'deg'],
86
+ 'rotateX': [0, 'deg'],
87
+ 'rotateY': [0, 'deg'],
88
+ 'rotateZ': [0, 'deg'],
89
+ 'skew': [0, 'deg'],
90
+ 'skewX': [0, 'deg'],
91
+ 'skewY': [0, 'deg']
92
+ };
37
93
 
38
94
  export class Transform extends Property {
39
95
  constructor(name) {
40
96
  super(name);
41
97
 
42
- if (name === 'x' || name === 'y') {
43
- this._defUnit = 'px';
44
- this._defVal = 0;
45
- this._transformFunction = 'translate' + name.toUpperCase();
46
- // } else if (name === 'rotation') {
47
- // this.defUnit = 'deg';
48
- // this.defVal = 0;
49
- } else if (name.startsWith('scale')) {
50
- this._defUnit = null;
51
- this._defVal = 1;
52
- this._transformFunction = name;
53
- } else if (name === 'rotate') {
54
- this._defUnit = 'deg';
55
- this._defVal = 0;
56
- this._transformFunction = name;
57
- } else {
58
- throw new Error('unknown prop ' + name);
59
- }
98
+ this._defaultValue = new Value(...TRANSFORM_PROPS[name]);
60
99
  }
61
100
 
62
101
  parseValue(val) {
63
- return Value.parse(val, this._defUnit);
102
+ return Value.parse(val);
64
103
  }
65
104
 
66
105
  defaultValue() {
67
- return new Value(this._defVal, this._defUnit);
106
+ return this._defaultValue.clone();
68
107
  }
69
108
 
70
109
  getValue(target) {
71
- const val = target.getTransformValue(this._transformFunction);
72
- if (typeof val === 'undefined' || val === null)
73
- return this.defaultValue();
74
-
75
- return val;
110
+ return target.getTransformValue(this.name) ?? this.defaultValue();
76
111
  }
77
112
 
78
113
  setValue(target, val) {
79
- return target.setTransformValue(this._transformFunction, val);
114
+ target.setTransformValue(this.name, val);
80
115
  }
81
116
 
82
117
  removeValue(target) {
83
- // todo remove
84
- return target.removeTransformValue(this._transformFunction);
118
+ target.removeTransformValue(this.name);
85
119
  }
86
120
  }
87
121
 
@@ -97,8 +131,8 @@ export class TransformXY extends Property {
97
131
  const { x, y } = val;
98
132
 
99
133
  return {
100
- x: Value.parse(val.x, 'px'),
101
- y: Value.parse(val.y, 'px')
134
+ x: Value.parse(x),
135
+ y: Value.parse(y)
102
136
  };
103
137
  }
104
138
 
@@ -110,8 +144,8 @@ export class TransformXY extends Property {
110
144
  }
111
145
 
112
146
  getValue(target) {
113
- const x = target.getTransformValue('translateX');
114
- const y = target.getTransformValue('translateY');
147
+ const x = target.getTransformValue('x');
148
+ const y = target.getTransformValue('y');
115
149
 
116
150
  const def = this.defaultValue();
117
151
 
@@ -122,41 +156,153 @@ export class TransformXY extends Property {
122
156
  }
123
157
 
124
158
  setValue(target, val) {
125
- target.setTransformValue('translateX', val.x);
126
- target.setTransformValue('translateY', val.y);
159
+ target.setTransformValue('x', val.x.withDefaultUnit('px'));
160
+ target.setTransformValue('y', val.y.withDefaultUnit('px'));
127
161
  }
128
162
 
129
163
  removeValue(target) {
130
- target.removeTransformValue('translateX');
131
- target.removeTransformValue('translateY');
164
+ target.removeTransformValue('x');
165
+ target.removeTransformValue('y');
132
166
  }
133
167
  }
134
168
 
135
169
  const STYLE_PROPS = {
170
+ 'position': 'static',
171
+ 'display': 'block',
172
+ 'visibility': 'visible',
173
+ 'minWidth': 'px',
136
174
  'width': 'px',
175
+ 'maxWidth': 'px',
176
+ 'minHeight': 'px',
137
177
  'height': 'px',
178
+ 'maxHeight': 'px',
138
179
  'top': 'px',
139
180
  'left': 'px',
140
181
  'right': 'px',
141
182
  'bottom': 'px',
142
- 'opacity': null,
183
+ 'opacity': [1, null],
184
+ 'padding': 'px',
185
+ 'margin': 'px',
186
+ 'color': '#000',
187
+ 'backgroundColor': '#fff'
143
188
  }
144
189
 
145
190
  export class StyleProp extends Property {
146
191
  constructor(name) {
147
192
  super(name);
148
193
 
149
- this.unit = STYLE_PROPS[name];
194
+ const def = STYLE_PROPS[name];
195
+ if (Array.isArray(def)) {
196
+ this._defaultValue = new StyleValue([new Value(def[0], def[1])]);
197
+ this.unit = def[1];
198
+ } else if (def === 'px') {
199
+ this._defaultValue = new StyleValue([new Value(0, def)]);
200
+ this.unit = def;
201
+ } else if (typeof def !== 'undefined') {
202
+ this._defaultValue = StyleValue.parse(def);
203
+ this.unit = null;
204
+ } else {
205
+ this._defaultValue = null;
206
+ this.unit = null;
207
+ }
208
+
209
+ // text|color|values
210
+ this.kind = null;
211
+ }
212
+
213
+ init(target, from, to) {
214
+ // this check is probably not necessary?
215
+ if (!from && !to)
216
+ throw new Error('either from or to need to be defined');
217
+
218
+ /*
219
+ if the user inputed a text value both need to be treated as text
220
+ and interpolated as classes or in reverse if they are negative values
221
+
222
+ if the user inputed a value both need to be treated as values and need
223
+ to match in length
224
+ */
225
+
226
+ if (from?.kind === 'text' || to?.kind === 'text') {
227
+ if (from && from.kind !== 'text')
228
+ throw new Error('from is expected to be a text because to is');
229
+
230
+ if (to && to.kind !== 'text')
231
+ throw new Error('to is expected to be a text because from is');
232
+
233
+ this.kind = 'text';
234
+ this.from = from;
235
+ this.to = to;
236
+ return;
237
+ }
238
+
239
+ // get the current value
240
+ if (!from)
241
+ from = this.getValue(target);
242
+
243
+ // get the current value
244
+ if (!to)
245
+ to = this.getValue(target);
246
+
247
+ if (!from || !to)
248
+ throw new Error('from or to could not be defined, specified ' +
249
+ 'it with fromTo');
250
+
251
+ // if one is a color both need to be a color
252
+ if (from.kind === 'color' || to.kind === 'color') {
253
+ if (from.kind !== to.kind)
254
+ throw new Error('expected two colors');
255
+
256
+ this.kind = 'color';
257
+ this.from = from;
258
+ this.to = to;
259
+ return;
260
+ }
261
+
262
+ // check that both are values
263
+ if (from.kind === 'text')
264
+ from = this.defaultValue();
265
+
266
+ if (to.kind === 'text')
267
+ to = this.defaultValue();
268
+
269
+ if (from.length === 0 || to.length === 0)
270
+ throw new Error('from or to has zero values');
271
+
272
+ // either from or to needs to be 1 to work
273
+
274
+ if (from.length !== to.length && from.length !== 1 && to.length !== 1)
275
+ throw new Error('from or to need to match in length or be one');
276
+
277
+ const newLength = Math.max(from.length, to.length);
278
+ from.extendToLength(newLength);
279
+ to.extendToLength(newLength);
280
+
281
+ // now let's try to unify each value
282
+ const fromVals = [];
283
+ const toVals = [];
284
+
285
+ // now we need to make sure the values are the same
286
+ for (let i = 0; i < newLength; i++) {
287
+ const f = from.values[i];
288
+ const t = to.values[i];
289
+
290
+ const [nFrom, nTo] = target.unifyValues(this.name, f, t);
291
+ fromVals.push(nFrom);
292
+ toVals.push(nTo);
293
+ }
294
+
295
+ this.kind = 'values';
296
+ this.from = new StyleValue(fromVals);
297
+ this.to = new StyleValue(toVals);
150
298
  }
151
299
 
152
300
  parseValue(val) {
153
- // maybe keep it unitless as long as possible, maybe until we need to
154
- // write it
155
- return Value.parse(val, this.unit);
301
+ return StyleValue.parse(val);
156
302
  }
157
303
 
158
304
  defaultValue() {
159
- return new Value(0, this.unit);
305
+ return this._defaultValue?.clone();
160
306
  }
161
307
 
162
308
  getValue(target) {
@@ -167,8 +313,99 @@ export class StyleProp extends Property {
167
313
  return val;
168
314
  }
169
315
 
316
+ interpolate(pos) {
317
+ switch (this.kind) {
318
+ case 'text':
319
+ return this._interpolateText(pos);
320
+
321
+ case 'color':
322
+ return this._interpolateColor(pos);
323
+
324
+ default:
325
+ return this._interpolateValues(pos);
326
+ }
327
+ }
328
+
329
+ _interpolateText(pos) {
330
+ /*
331
+ ## if both values are set
332
+
333
+ <=0 the from value will be set
334
+ >0 the to value will be set
335
+
336
+
337
+ ## if only one text is set
338
+
339
+ if the to value is
340
+
341
+
342
+ the text in the to value will be set >0
343
+ the text in the to value will be removed in <=0
344
+
345
+ the text in the from value will be set <1
346
+ the text in the from value will be removed in >=1
347
+ */
348
+ if (this.from && this.to) {
349
+ if (pos <= 0)
350
+ return this.from.clone();
351
+
352
+ return this.to.clone();
353
+ }
354
+
355
+ if (this.to) {
356
+ if (pos <= 0)
357
+ return null;
358
+
359
+ return this.to.clone();
360
+ }
361
+
362
+ if (this.from) {
363
+ if (pos >= 1)
364
+ return null;
365
+
366
+ return this.from.clone();
367
+ }
368
+
369
+ throw new Error('expected from or to');
370
+ }
371
+
372
+ _interpolateColor(pos) {
373
+ const vals = [];
374
+
375
+ for (let i = 0; i < 4; i++) {
376
+ const f = this.from.values.values[i];
377
+ const t = this.to.values.values[i];
378
+
379
+ const dif = t - f;
380
+
381
+ vals.push(f + pos * dif);
382
+ }
383
+
384
+ return new StyleValue(new ColorValue(vals));
385
+ }
386
+
387
+ _interpolateValues(pos) {
388
+ // both values should be the same length
389
+
390
+ const vals = [];
391
+
392
+ for (let i = 0; i < this.from.values.length; i++) {
393
+ const f = this.from.values[i];
394
+ const t = this.to.values[i];
395
+
396
+ const dif = t.num - f.num;
397
+
398
+ vals.push(f.cloneAdd(pos * dif));
399
+ }
400
+
401
+ return new StyleValue(vals);
402
+ }
403
+
170
404
  setValue(target, val) {
171
- return target.setStyleValue(this.name, val);
405
+ if (!val)
406
+ return target.removeStyleValue(this.name);
407
+
408
+ return target.setStyleValue(this.name, val.withDefaultUnit(this.unit));
172
409
  }
173
410
 
174
411
  removeValue(target) {
@@ -176,16 +413,144 @@ export class StyleProp extends Property {
176
413
  }
177
414
  }
178
415
 
416
+ /**
417
+ * In most cases we wan't to add the class at pos 0
418
+ * and in reverse remove it at pos 0
419
+ *
420
+ * so this behaviour should be in str|{ to }
421
+ *
422
+ * If we wan't to remove the class at pos 1
423
+ * and in reverse add it at pos 1
424
+ * we do it in { from }
425
+ */
426
+ export class ClassNameProp extends Property {
427
+ constructor(name) {
428
+ super(name);
429
+ }
430
+
431
+ allowsValueFn() {
432
+ return false;
433
+ }
434
+
435
+ init(target, from, to) {
436
+ this.from = from ?? [];
437
+ this.to = to ?? [];
438
+
439
+ this.preStates = new Map;
440
+ this.states = new Map;
441
+
442
+ for (const cls of [...this.from, ...this.to]) {
443
+ const exists = target.hasClass(cls);
444
+ this.preStates.set(cls, exists);
445
+ this.states.set(cls, exists);
446
+ }
447
+ }
448
+
449
+ parseValue(val) {
450
+ if (Array.isArray(val)) {
451
+ return val.map(v => {
452
+ if (typeof v !== 'string')
453
+ throw new Error('cls only accepts strings[]');
454
+
455
+ return v;
456
+ });
457
+ }
458
+
459
+ if (typeof val === 'string')
460
+ return [val];
461
+
462
+ throw new Error('cls only accepts strings and strings[]');
463
+ }
464
+
465
+ defaultValue() {
466
+ throw new Error('unused');
467
+ }
468
+
469
+ getValue(_target) {
470
+ throw new Error('unused');
471
+ }
472
+
473
+ interpolate(pos) {
474
+ /**
475
+ * ## to
476
+ * add the class at pos >0
477
+ * and in reverse remove it at pos 0
478
+ *
479
+ * ## from
480
+ * remove the class at pos <1
481
+ * and in reverse add it at pos 1
482
+ */
483
+
484
+ let obj = {
485
+ add: [],
486
+ remove: []
487
+ };
488
+
489
+ if (pos <= 0) {
490
+ obj.remove = this.to;
491
+ } else {
492
+ obj.add = this.to;
493
+ }
494
+
495
+ if (pos >= 1) {
496
+ obj.remove = this.from;
497
+ } else {
498
+ obj.add = [...obj.add, ...this.from];
499
+ }
500
+
501
+ return obj;
502
+ }
503
+
504
+ setValue(target, val) {
505
+ let { add, remove } = val;
506
+ add = add ?? [];
507
+ remove = remove ?? [];
508
+
509
+ if (!Array.isArray(add) || !Array.isArray(remove))
510
+ throw new Error('add or remove expected');
511
+
512
+ add.forEach(cls => {
513
+ const exists = this.states.get(cls);
514
+ if (!exists) {
515
+ target.addClass(cls);
516
+ this.states.set(cls, true);
517
+ }
518
+ });
519
+
520
+ remove.forEach(cls => {
521
+ const exists = this.states.get(cls);
522
+ if (exists) {
523
+ target.removeClass(cls);
524
+ this.states.set(cls, false);
525
+ }
526
+ });
527
+ }
528
+
529
+ removeValue(target) {
530
+ this.preStates.forEach((exists, cls) => {
531
+ if (exists)
532
+ target.addClass(cls);
533
+ else
534
+ target.removeClass(cls);
535
+ });
536
+ this.states = new Map(this.preStates);
537
+ }
538
+ }
539
+
179
540
  export function newProperty(prop, targetType = '') {
180
541
  if (targetType !== 'dom')
181
542
  throw new Error('only dom is supported as target');
182
543
 
183
- if (TRANSFORM_PROPS.includes(prop))
544
+ if (prop in TRANSFORM_PROPS)
184
545
  return new Transform(prop);
185
546
  if (prop === 'xy')
186
547
  return new TransformXY(prop);
548
+ if (prop.startsWith('--'))
549
+ return new StyleProp(prop);
187
550
  if (prop in STYLE_PROPS)
188
551
  return new StyleProp(prop);
552
+ if (prop === 'cls')
553
+ return new ClassNameProp(prop);
189
554
 
190
555
  throw new Error('unknown prop ' + prop);
191
556
  }
@@ -1,8 +1,14 @@
1
1
  import { takeProp } from '../utils/internal.js';
2
2
  import Timeline from '../timeline/public.js';
3
- import { parseDelay } from '../timing/timing.js';
4
3
  import { staggerMap } from '../stagger/stagger.js';
5
4
 
5
+ /**
6
+ * The animation consist of a timeline with just one animation
7
+ * This allows to reuse a lot of code
8
+ *
9
+ * ## Props
10
+ *
11
+ */
6
12
  export default class PublicAnimation {
7
13
  constructor(targets, props = {}) {
8
14
  const autoplay = parseAutoplay(takeProp(props, 'autoplay', true));
@@ -31,18 +37,82 @@ export default class PublicAnimation {
31
37
  this._tl.pause();
32
38
  }
33
39
 
34
- // 0-1
40
+ /**
41
+ * Seeks to a position in the timeline
42
+ */
43
+ seekMs(ms) {
44
+ this._tl.seekMs(ms);
45
+ }
46
+
47
+ /**
48
+ * Seeks to a normalized position in the timeline
49
+ *
50
+ * @param pos = number between 0 and 1
51
+ */
35
52
  seek(pos) {
36
53
  this._tl.seek(pos);
37
54
  }
38
55
 
56
+ /**
57
+ * Resets the current timeline to the start
58
+ * without changing anything
59
+ */
39
60
  reset() {
40
61
  this._tl.reset();
41
62
  }
42
63
 
64
+ /**
65
+ * Resets every used prop to it's previous value
66
+ */
67
+ resetProps() {
68
+ this._tl.resetProps();
69
+ }
70
+
71
+ /**
72
+ * Recalculates all values this should be called for example after a resize
73
+ */
74
+ update() {
75
+ this._tl.update();
76
+ }
77
+
78
+
79
+ /**
80
+ * Returns wether the timeline is set to reversed
81
+ */
82
+ isReversed() {
83
+ return this._tl.isReversed();
84
+ }
85
+
86
+ /**
87
+ * Set's the timeline to a specific direction
88
+ */
89
+ setReversed(reversed) {
90
+ this._tl.setReversed(reversed);
91
+ }
92
+
93
+ /**
94
+ * Reverses the order of the current timeline
95
+ */
43
96
  reverse() {
44
97
  this._tl.reverse();
45
98
  }
99
+
100
+ on(event, fn) {
101
+ return this._tl.on(event, fn);
102
+ }
103
+
104
+ // not wroking
105
+ onPromise(event) {
106
+ return this._tl.onPromise(event);
107
+ }
108
+
109
+ /**
110
+ * Destroys this timeline and resets all props
111
+ */
112
+ destroy() {
113
+ this._tl.destroy();
114
+ this._tl = null;
115
+ }
46
116
  }
47
117
 
48
118
  function parseAutoplay(autoplay) {
package/src/animator.js CHANGED
@@ -1,3 +1,5 @@
1
+ /* eslint no-unused-vars: 0 */
2
+
1
3
  import { quadInOut, quadOut } from './easing/index.js';
2
4
  import { range } from 'fire/util.js';
3
5
 
package/src/chnobli.js CHANGED
@@ -1,12 +1,17 @@
1
1
  import Animation from './animation/public.js';
2
2
  import Timeline from './timeline/public.js';
3
3
  import Scroll from './scroll/public.js';
4
- import { stagger as _stagger } from './stagger/stagger.js';
4
+ import Stagger from './stagger/stagger.js';
5
5
 
6
6
 
7
7
  // todo maybe add, to, from and fromTo to the animate function
8
8
 
9
-
9
+ /**
10
+ * Creates a simple animation
11
+ *
12
+ * ## Properties
13
+ *
14
+ */
10
15
  export function animate(targets, props = {}) {
11
16
  return new Animation(targets, props);
12
17
  }
@@ -16,7 +21,7 @@ export function timeline(props = {}) {
16
21
  }
17
22
 
18
23
  export function stagger(value) {
19
- return _stagger(value);
24
+ return new Stagger(value);
20
25
  }
21
26
 
22
27
  export function scroll(props = {}) {