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 +1 -1
- package/src/animation/property.js +6 -6
- package/src/tests/properties.test.js +29 -1
- package/src/tests/timeline.test.js +39 -0
- package/src/timeline/nested.js +61 -0
- package/src/timeline/public.js +46 -22
- package/src/timeline/timeline.js +32 -2
- package/src/utils/utils.js +33 -1
package/package.json
CHANGED
|
@@ -144,8 +144,8 @@ export class TransformXY extends Property {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
getValue(target) {
|
|
147
|
-
const x = target.getTransformValue('
|
|
148
|
-
const y = target.getTransformValue('
|
|
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('
|
|
160
|
-
target.setTransformValue('
|
|
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('
|
|
165
|
-
target.removeTransformValue('
|
|
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
|
+
}
|
package/src/timeline/public.js
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/src/timeline/timeline.js
CHANGED
|
@@ -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 =>
|
|
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
|
|
package/src/utils/utils.js
CHANGED
|
@@ -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
|
/**
|