chnobli 0.1.3 → 0.1.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chnobli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "author": "Sören Meier <info@soerenmeier.ch>",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -144,8 +144,8 @@ export class TransformXY extends Property {
144
144
  }
145
145
 
146
146
  getValue(target) {
147
- const x = target.getTransformValue('translateX');
148
- const y = target.getTransformValue('translateY');
147
+ const x = target.getTransformValue('x');
148
+ const y = target.getTransformValue('y');
149
149
 
150
150
  const def = this.defaultValue();
151
151
 
@@ -156,13 +156,13 @@ export class TransformXY extends Property {
156
156
  }
157
157
 
158
158
  setValue(target, val) {
159
- target.setTransformValue('translateX', val.x.withDefaultUnit('px'));
160
- target.setTransformValue('translateY', val.y.withDefaultUnit('px'));
159
+ target.setTransformValue('x', val.x.withDefaultUnit('px'));
160
+ target.setTransformValue('y', val.y.withDefaultUnit('px'));
161
161
  }
162
162
 
163
163
  removeValue(target) {
164
- target.removeTransformValue('translateX');
165
- target.removeTransformValue('translateY');
164
+ target.removeTransformValue('x');
165
+ target.removeTransformValue('y');
166
166
  }
167
167
  }
168
168
 
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
2
2
  import TestTicker from '../timing/testticker.js';
3
3
  import TestResponsiveEvent from '../responsive/testevent.js';
4
4
  import { animate, timeline } from '../chnobli.js';
5
- import { responsive } from '../utils/utils.js';
5
+ import { responsive, curve } from '../utils/utils.js';
6
6
  import { el } from '../target/testdomnode.js';
7
7
  import { timeout } from 'fire/util.js';
8
8
 
@@ -147,4 +147,32 @@ describe('properties', () => {
147
147
  respEv.resize();
148
148
  expect(div.style.width).toBe('33.333px');
149
149
  });
150
+
151
+ it('xy', () => {
152
+ const ticker = new TestTicker;
153
+
154
+ const div = el();
155
+ const tl = timeline()
156
+ .add(div, {
157
+ xy: curve([
158
+ { x: 0, y: 10 },
159
+ { x: 100, y: 10 },
160
+ { x: 50, y: 20 }
161
+ ]),
162
+ duration: 10
163
+ });
164
+
165
+ tl.play();
166
+ ticker.run(0);
167
+ expect(div.style.transform)
168
+ .toBe('translate3d(0.000px,10.000px,0.000px)');
169
+
170
+ ticker.run(5);
171
+ expect(div.style.transform)
172
+ .toBe('translate3d(100.000px,10.000px,0.000px)');
173
+
174
+ ticker.run();
175
+ expect(div.style.transform)
176
+ .toBe('translate3d(50.000px,20.000px,0.000px)');
177
+ });
150
178
  });
@@ -231,4 +231,43 @@ describe('timeline', () => {
231
231
  ticker.run();
232
232
  expect(endCounter).toBe(4);
233
233
  });
234
+
235
+ it('nested timeline', () => {
236
+ const ticker = new TestTicker;
237
+
238
+ const target = el();
239
+ const tl = timeline()
240
+ .add(target, {
241
+ width: 50,
242
+ duration: 10
243
+ })
244
+ .nest(tl => {
245
+ tl.add(target, {
246
+ opacity: 0,
247
+ duration: 5
248
+ })
249
+ .add(target, {
250
+ opacity: 1,
251
+ duration: 5
252
+ });
253
+ })
254
+ .add(target, {
255
+ height: 50,
256
+ duration: 10
257
+ })
258
+ .play();
259
+
260
+ ticker.run(0);
261
+ expect(target.style.width).toBe('0.000px');
262
+ ticker.run(10);
263
+ expect(target.style.width).toBe('50.000px');
264
+ expect(target.style.opacity).toBe('1.000');
265
+ ticker.run(5);
266
+ expect(target.style.opacity).toBe('0.000');
267
+ ticker.run(5);
268
+ expect(target.style.opacity).toBe('1.000');
269
+ expect(target.style.height).toBe('0.000px');
270
+ ticker.run(10);
271
+ expect(target.style.height).toBe('50.000px');
272
+ });
234
273
  });
@@ -0,0 +1,61 @@
1
+ import { takeProp } from '../utils/internal.js';
2
+ import Timeline from './timeline.js';
3
+ import { timelineAdd } from './public.js';
4
+
5
+
6
+ export default class NestedTimeline {
7
+ constructor(props = {}) {
8
+ this._defaults = takeProp(props, 'defaults', {});
9
+
10
+ if ('duration' in props)
11
+ throw new Error('a timeline does not accept a duration');
12
+
13
+ this._inner = new Timeline(props);
14
+ }
15
+
16
+ /**
17
+ * Set's properties
18
+ * offset can be staggered, a number, a label or a string `+=10`
19
+ */
20
+ set(targets, props, offset = null) {
21
+ // for the moment let's just add as usual but set the duration to 0
22
+ return this.add(targets, { ...props, duration: 0 }, offset);
23
+ }
24
+
25
+ /**
26
+ * Add an animation to the timeline
27
+ *
28
+ * offset can be staggered, a number, a label or a string `+=10`
29
+ */
30
+ add(targets, props, offset = null) {
31
+ timelineAdd(this, targets, props, offset);
32
+
33
+ return this;
34
+ }
35
+
36
+ /**
37
+ * Adds a label at to the current nOffse
38
+ *
39
+ * This label can then be used in offsets
40
+ */
41
+ label(label, offset = null) {
42
+ this._inner.label(label, offset);
43
+
44
+ return this;
45
+ }
46
+
47
+ /**
48
+ * Allows to nest timelines
49
+ *
50
+ * fn: (timeline)
51
+ */
52
+ nest(fn, opts = {}, offset = null) {
53
+ const tl = new NestedTimeline(opts);
54
+
55
+ fn(tl);
56
+
57
+ this._inner.addTimeline(tl._inner, offset);
58
+
59
+ return this;
60
+ }
61
+ }
@@ -4,6 +4,7 @@ import { STATE_ENDED } from '../timing/timing.js';
4
4
  import { callStagger } from '../stagger/stagger.js';
5
5
  import Events from '../utils/events.js';
6
6
  import ResponsiveEvent from '../responsive/event.js';
7
+ import NestedTimeline from './nested.js';
7
8
 
8
9
  const STATE_PAUSED = 0;
9
10
  const STATE_RENDER_ONCE = 1;
@@ -59,28 +60,7 @@ export default class PublicTimeline {
59
60
  * offset can be staggered, a number, a label or a string `+=10`
60
61
  */
61
62
  add(targets, props, offset = null) {
62
- if (Array.from(targets).length === 0)
63
- targets = [targets];
64
- else
65
- targets = Array.from(targets);
66
-
67
- let i = -1;
68
- for (const target of targets) {
69
- i++;
70
-
71
- const nProps = {
72
- ...this._defaults,
73
- ...props
74
- };
75
-
76
- for (const prop in nProps) {
77
- nProps[prop] = callStagger(nProps[prop], i, targets.length);
78
- }
79
-
80
- const nOffset = callStagger(offset, i);
81
-
82
- this._inner.add(target, nProps, nOffset, i);
83
- }
63
+ timelineAdd(this, targets, props, offset);
84
64
 
85
65
  return this;
86
66
  }
@@ -96,6 +76,21 @@ export default class PublicTimeline {
96
76
  return this;
97
77
  }
98
78
 
79
+ /**
80
+ * Allows to nest timelines
81
+ *
82
+ * fn: (timeline)
83
+ */
84
+ nest(fn, opts = {}, offset = null) {
85
+ const tl = new NestedTimeline(opts);
86
+
87
+ fn(tl);
88
+
89
+ this._inner.addTimeline(tl._inner, offset);
90
+
91
+ return this;
92
+ }
93
+
99
94
  /**
100
95
  * Adds a responsive function call which get's called before the responsive
101
96
  * functions in properties
@@ -330,4 +325,33 @@ export default class PublicTimeline {
330
325
  // return;
331
326
  this._inner.update();
332
327
  }
328
+ }
329
+
330
+ /**
331
+ * Add an animation to the timeline
332
+ * tl needs to have _defaults, and _inner
333
+ */
334
+ export function timelineAdd(tl, targets, props, offset = null) {
335
+ if (Array.from(targets).length === 0)
336
+ targets = [targets];
337
+ else
338
+ targets = Array.from(targets);
339
+
340
+ let i = -1;
341
+ for (const target of targets) {
342
+ i++;
343
+
344
+ const nProps = {
345
+ ...tl._defaults,
346
+ ...props
347
+ };
348
+
349
+ for (const prop in nProps) {
350
+ nProps[prop] = callStagger(nProps[prop], i, targets.length);
351
+ }
352
+
353
+ const nOffset = callStagger(offset, i);
354
+
355
+ tl._inner.add(target, nProps, nOffset, i);
356
+ }
333
357
  }
@@ -40,6 +40,12 @@ export default class Timeline {
40
40
  this._renderQueue = null;
41
41
  }
42
42
 
43
+ isFinite() {
44
+ return !this.entries.some(e => {
45
+ return e.type === 'animation' && !e.value.isFinite();
46
+ });
47
+ }
48
+
43
49
  // relative represents the point which the offset should be taken
44
50
  // if relative 1 is done the offset will be taken from the previous - 1
45
51
  add(targets, props, offset = null, relative = 0) {
@@ -58,6 +64,18 @@ export default class Timeline {
58
64
  return this;
59
65
  }
60
66
 
67
+ addTimeline(tl, offset = null) {
68
+ if (!tl.isFinite())
69
+ throw new Error('the animation needs to be finite');
70
+
71
+ this.entries.push({
72
+ type: 'timeline',
73
+ value: tl,
74
+ offset: new Offset(offset),
75
+ relativeOffset: 0
76
+ });
77
+ }
78
+
61
79
  label(label, offset = null) {
62
80
  this.entries.push({
63
81
  type: 'label',
@@ -123,6 +141,11 @@ export default class Timeline {
123
141
  if (entry.type === 'animation')
124
142
  end += entry.value.duration;
125
143
 
144
+ if (entry.type === 'timeline') {
145
+ entry.value.init();
146
+ end += entry.value.timing.duration;
147
+ }
148
+
126
149
  if (entry.type === 'label')
127
150
  labels[entry.value] = start;
128
151
 
@@ -148,7 +171,9 @@ export default class Timeline {
148
171
  // initialization means
149
172
  // we need to define which value should be used as the from and the to
150
173
  // value
151
- const startOrdered = this.entries.filter(e => e.type === 'animation');
174
+ const startOrdered = this.entries.filter(e => {
175
+ return e.type === 'animation' || e.type === 'timeline';
176
+ });
152
177
  startOrdered.sort((a, b) => a.start - b.start);
153
178
 
154
179
  for (let i = 0; i < startOrdered.length; i++) {
@@ -207,7 +232,7 @@ export default class Timeline {
207
232
  const pos = this.timing.positionMsUnbounded();
208
233
 
209
234
  for (const entry of this.entries) {
210
- if (entry.type !== 'animation')
235
+ if (entry.type !== 'animation' && entry.type !== 'timeline')
211
236
  continue;
212
237
 
213
238
  const animation = entry.value;
@@ -251,6 +276,11 @@ export default class Timeline {
251
276
 
252
277
  destroy() {
253
278
  for (const entry of this.entries) {
279
+ if (entry.type === 'timeline') {
280
+ entry.value.destroy();
281
+ continue
282
+ }
283
+
254
284
  if (entry.type !== 'animation')
255
285
  continue;
256
286
 
@@ -43,13 +43,45 @@ export function chars(element) {
43
43
  element.dataset.processedChars = 'yes';
44
44
 
45
45
  return chars.map(c => {
46
+ if (c.trim().length === 0) {
47
+ element.appendChild(document.createTextNode(c));
48
+ return null;
49
+ }
50
+
46
51
  const span = document.createElement('span');
47
52
  span.textContent = c;
48
53
  span.style.display = 'inline-block';
49
54
  element.appendChild(span);
50
55
 
51
56
  return span;
52
- });
57
+ }).filter(c => !!c);
58
+ }
59
+
60
+ /**
61
+ * Converts an element with text into span with words
62
+ */
63
+ export function words(element) {
64
+ if (element.dataset.processedWords)
65
+ return Array.from(element.children);
66
+
67
+ const words = element.textContent.split(/(\s+)/);
68
+
69
+ element.textContent = '';
70
+ element.dataset.processedWords = 'yes';
71
+
72
+ return words.map(word => {
73
+ if (word.trim().length === 0) {
74
+ element.appendChild(document.createTextNode(word));
75
+ return null;
76
+ }
77
+
78
+ const span = document.createElement('span');
79
+ span.textContent = word;
80
+ span.style.display = 'inline-block';
81
+ element.appendChild(span);
82
+
83
+ return span;
84
+ }).filter(w => !!w);
53
85
  }
54
86
 
55
87
  /**