chnobli 0.1.0 → 0.1.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,250 @@
1
+ import { takeProp } from '../utils/internal.js';
2
+ import Timing, {
3
+ parseEase, parseRepeat, parseAlternate, parseReversed,
4
+ STATE_RUNNING
5
+ } from '../timing/timing.js';
6
+ import Ticker from '../timing/ticker.js';
7
+ import Animation from '../animation/animation.js';
8
+
9
+ export default class Timeline {
10
+ /*
11
+ {
12
+ ease,
13
+ repeat: -1 - x,
14
+ alternate
15
+ }
16
+ */
17
+ constructor(props = {}) {
18
+ const nProps = {
19
+ duration: 1000,
20
+ ease: parseEase(takeProp(props, 'ease', null)),
21
+ repeat: parseRepeat(takeProp(props, 'repeat', false)),
22
+ alternate: parseAlternate(takeProp(props, 'alternate', true)),
23
+ reversed: parseReversed(takeProp(props, 'reversed', false))
24
+ };
25
+
26
+ this.timing = new Timing(nProps);
27
+ this.ticker = Ticker.global();
28
+
29
+ // { type: label|animation, value: {}, offset, start: x, end: y, render }
30
+ this.entries = [];
31
+
32
+ this._initialized = false;
33
+ this._renderQueue = null;
34
+ }
35
+
36
+ // relative represents the point which the offset should be taken
37
+ // if relative 1 is done the offset will be taken from the previous - 1
38
+ add(targets, props, offset = null, relative = 0) {
39
+ const animation = new Animation(targets, this.ticker, props);
40
+
41
+ if (!animation.isFinite())
42
+ throw new Error('the animation needs to be finite');
43
+
44
+ this.entries.push({
45
+ type: 'animation',
46
+ value: animation,
47
+ offset: new Offset(offset),
48
+ relativeOffset: relative
49
+ });
50
+
51
+ return this;
52
+ }
53
+
54
+ label(label, offset = null) {
55
+ this.entries.push({
56
+ type: 'label',
57
+ value: label,
58
+ offset: new Offset(offset),
59
+ relativeOffset: 0
60
+ });
61
+
62
+ return this;
63
+ }
64
+
65
+ advance(change) {
66
+ this.timing.advance(change);
67
+
68
+ this._updateTimings();
69
+ }
70
+
71
+ seek(pos) {
72
+ this.timing.seek(pos);
73
+
74
+ this._updateTimings();
75
+ }
76
+
77
+ init() {
78
+ if (this._initialized)
79
+ return;
80
+ this._initialized = true;
81
+
82
+ let duration = 0;
83
+
84
+ const labels = {};
85
+
86
+ let i = -1;
87
+ for (const entry of this.entries) {
88
+ i++;
89
+ let offset = entry.offset;
90
+
91
+ if (offset.type === 'label') {
92
+ if (!(offset.value in labels))
93
+ throw new Error('could not find label ' + offset.value);
94
+
95
+ offset = new Offset(labels[offset.value]);
96
+ }
97
+
98
+ const pos = this.entries[i - 1 - entry.relativeOffset]?.end ?? 0;
99
+
100
+ let start = offset.calculate(pos);
101
+ if (start < 0)
102
+ throw new Error('cannot go in the past start < 0');
103
+
104
+ let end = start;
105
+ if (entry.type === 'animation')
106
+ end += entry.value.duration;
107
+
108
+ if (entry.type === 'label')
109
+ labels[entry.value] = start;
110
+
111
+ entry.start = start;
112
+ entry.end = end;
113
+
114
+ duration = Math.max(end, duration);
115
+ }
116
+
117
+ this.timing.setDuration(duration);
118
+
119
+ this._initAnimations();
120
+
121
+ // console.log(this.entries.map(e => e.value._props[0]));
122
+ }
123
+
124
+ _initAnimations() {
125
+ // we don't won't to have a reversed timing
126
+ const reversed = this.timing.reversed;
127
+ this.timing.reversed = false;
128
+
129
+ // now initialize all animtaions
130
+ const startOrdered = this.entries.filter(e => e.type === 'animation');
131
+ startOrdered.sort((a, b) => a.start - b.start);
132
+
133
+ for (let i = 0; i < startOrdered.length; i++) {
134
+ const entry = startOrdered[i];
135
+
136
+ this.timing.seek(entry.start / this.timing.iterDuration);
137
+
138
+ const pos = this.timing.position * this.timing.iterDuration;
139
+
140
+ // seek all previous and render
141
+ for (let y = 0; y < i; y++) {
142
+ const prevEntry = startOrdered[y];
143
+ const prevAnimation = prevEntry.value;
144
+
145
+ const p = (pos - prevEntry.start) / prevAnimation.duration;
146
+
147
+ prevAnimation.seek(p);
148
+ prevAnimation.render();
149
+ }
150
+
151
+ entry.value.init();
152
+ }
153
+
154
+ this.timing.seek(-1);
155
+ this.timing.reversed = reversed;
156
+ startOrdered.forEach(e => {
157
+ e.value.seek(-1);
158
+ e.value.render();
159
+ });
160
+ }
161
+
162
+ render() {
163
+ // the rendering order needs to be as follows
164
+ // upcoming
165
+ // passed
166
+ // active
167
+
168
+ for (const animation of this._renderQueue.upcoming) {
169
+ animation.render();
170
+ }
171
+
172
+ for (const animation of this._renderQueue.passed) {
173
+ animation.render();
174
+ }
175
+
176
+ for (const animation of this._renderQueue.active) {
177
+ animation.render();
178
+ }
179
+ }
180
+
181
+ _updateTimings() {
182
+ this._renderQueue = {
183
+ upcoming: [],
184
+ passed: [],
185
+ active: []
186
+ };
187
+
188
+ const pos = this.timing.position * this.timing.iterDuration;
189
+
190
+ for (const entry of this.entries) {
191
+ if (entry.type !== 'animation')
192
+ continue;
193
+
194
+ const animation = entry.value;
195
+
196
+ const p = (pos - entry.start) / animation.duration;
197
+
198
+ const prevState = animation.timing.state;
199
+
200
+ animation.seek(p);
201
+
202
+ if (p > 1) {
203
+ this._renderQueue.passed.push(animation);
204
+ } else if (p < 0) {
205
+ this._renderQueue.upcoming.push(animation);
206
+ } else {
207
+ this._renderQueue.active.push(animation);
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+
214
+ class Offset {
215
+ constructor(offset = null) {
216
+ this.type = 'none';
217
+
218
+ if (typeof offset === 'number') {
219
+ this.type = 'absolute';
220
+ this.value = offset;
221
+ } else if (typeof offset === 'string') {
222
+ if (offset.startsWith('-=')) {
223
+ this.type = 'sub';
224
+ this.value = parseFloat(offset.substring(2));
225
+ } else if (offset.startsWith('+=')) {
226
+ this.type = 'add';
227
+ this.value = parseFloat(offset.substring(2));
228
+ } else {
229
+ this.type = 'label';
230
+ this.value = offset;
231
+ }
232
+ }
233
+ }
234
+
235
+ calculate(pos) {
236
+ if (this.type === 'none')
237
+ return pos;
238
+
239
+ if (this.type === 'absolute')
240
+ return this.value;
241
+
242
+ if (this.type === 'sub')
243
+ return pos - this.value;
244
+
245
+ if (this.type === 'add')
246
+ return pos + this.value;
247
+
248
+ throw new Error('cannot calculate for type ' + this.type);
249
+ }
250
+ }
@@ -0,0 +1,16 @@
1
+ export default class Ease {
2
+ constructor(prop) {
3
+ // parse prop
4
+ if (prop !== null && typeof prop !== 'function')
5
+ throw new Error('ease needs to be a function');
6
+
7
+ this.fn = prop;
8
+ }
9
+
10
+ apply(t) {
11
+ if (this.fn === null)
12
+ return t;
13
+
14
+ return this.fn(t);
15
+ }
16
+ }
@@ -0,0 +1,292 @@
1
+ /*
2
+ Adapted from https://github.com/sveltejs/svelte
3
+ Distributed under MIT License https://github.com/sveltejs/svelte/blob/main/LICENSE.md
4
+ */
5
+
6
+ import bezierEasing from 'bezier-easing';
7
+
8
+ /**
9
+ * @param {number} t
10
+ * @returns {number}
11
+ */
12
+ export function linear(t) {
13
+ return t;
14
+ }
15
+
16
+ /**
17
+ * @param {number} t
18
+ * @returns {number}
19
+ */
20
+ export function backInOut(t) {
21
+ const s = 1.70158 * 1.525;
22
+ if ((t *= 2) < 1) return 0.5 * (t * t * ((s + 1) * t - s));
23
+ return 0.5 * ((t -= 2) * t * ((s + 1) * t + s) + 2);
24
+ }
25
+
26
+ /**
27
+ * @param {number} t
28
+ * @returns {number}
29
+ */
30
+ export function backIn(t) {
31
+ const s = 1.70158;
32
+ return t * t * ((s + 1) * t - s);
33
+ }
34
+
35
+ /**
36
+ * @param {number} t
37
+ * @returns {number}
38
+ */
39
+ export function backOut(t) {
40
+ const s = 1.70158;
41
+ return --t * t * ((s + 1) * t + s) + 1;
42
+ }
43
+
44
+ /**
45
+ * @param {number} t
46
+ * @returns {number}
47
+ */
48
+ export function bounceOut(t) {
49
+ const a = 4.0 / 11.0;
50
+ const b = 8.0 / 11.0;
51
+ const c = 9.0 / 10.0;
52
+ const ca = 4356.0 / 361.0;
53
+ const cb = 35442.0 / 1805.0;
54
+ const cc = 16061.0 / 1805.0;
55
+ const t2 = t * t;
56
+ return t < a
57
+ ? 7.5625 * t2
58
+ : t < b
59
+ ? 9.075 * t2 - 9.9 * t + 3.4
60
+ : t < c
61
+ ? ca * t2 - cb * t + cc
62
+ : 10.8 * t * t - 20.52 * t + 10.72;
63
+ }
64
+
65
+ /**
66
+ * @param {number} t
67
+ * @returns {number}
68
+ */
69
+ export function bounceInOut(t) {
70
+ return t < 0.5 ? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0)) : 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5;
71
+ }
72
+
73
+ /**
74
+ * @param {number} t
75
+ * @returns {number}
76
+ */
77
+ export function bounceIn(t) {
78
+ return 1.0 - bounceOut(1.0 - t);
79
+ }
80
+
81
+ /**
82
+ * @param {number} t
83
+ * @returns {number}
84
+ */
85
+ export function circInOut(t) {
86
+ if ((t *= 2) < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1);
87
+ return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
88
+ }
89
+
90
+ /**
91
+ * @param {number} t
92
+ * @returns {number}
93
+ */
94
+ export function circIn(t) {
95
+ return 1.0 - Math.sqrt(1.0 - t * t);
96
+ }
97
+
98
+ /**
99
+ * @param {number} t
100
+ * @returns {number}
101
+ */
102
+ export function circOut(t) {
103
+ return Math.sqrt(1 - --t * t);
104
+ }
105
+
106
+ /**
107
+ * @param {number} t
108
+ * @returns {number}
109
+ */
110
+ export function cubicInOut(t) {
111
+ return t < 0.5 ? 4.0 * t * t * t : 0.5 * Math.pow(2.0 * t - 2.0, 3.0) + 1.0;
112
+ }
113
+
114
+ /**
115
+ * @param {number} t
116
+ * @returns {number}
117
+ */
118
+ export function cubicIn(t) {
119
+ return t * t * t;
120
+ }
121
+
122
+ /**
123
+ * @param {number} t
124
+ * @returns {number}
125
+ */
126
+ export function cubicOut(t) {
127
+ const f = t - 1.0;
128
+ return f * f * f + 1.0;
129
+ }
130
+
131
+ /**
132
+ * @param {number} t
133
+ * @returns {number}
134
+ */
135
+ export function elasticInOut(t) {
136
+ return t < 0.5
137
+ ? 0.5 * Math.sin(((+13.0 * Math.PI) / 2) * 2.0 * t) * Math.pow(2.0, 10.0 * (2.0 * t - 1.0))
138
+ : 0.5 *
139
+ Math.sin(((-13.0 * Math.PI) / 2) * (2.0 * t - 1.0 + 1.0)) *
140
+ Math.pow(2.0, -10.0 * (2.0 * t - 1.0)) +
141
+ 1.0;
142
+ }
143
+
144
+ /**
145
+ * @param {number} t
146
+ * @returns {number}
147
+ */
148
+ export function elasticIn(t) {
149
+ return Math.sin((13.0 * t * Math.PI) / 2) * Math.pow(2.0, 10.0 * (t - 1.0));
150
+ }
151
+
152
+ /**
153
+ * @param {number} t
154
+ * @returns {number}
155
+ */
156
+ export function elasticOut(t) {
157
+ return Math.sin((-13.0 * (t + 1.0) * Math.PI) / 2) * Math.pow(2.0, -10.0 * t) + 1.0;
158
+ }
159
+
160
+ /**
161
+ * @param {number} t
162
+ * @returns {number}
163
+ */
164
+ export function expoInOut(t) {
165
+ return t === 0.0 || t === 1.0
166
+ ? t
167
+ : t < 0.5
168
+ ? +0.5 * Math.pow(2.0, 20.0 * t - 10.0)
169
+ : -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0;
170
+ }
171
+
172
+ /**
173
+ * @param {number} t
174
+ * @returns {number}
175
+ */
176
+ export function expoIn(t) {
177
+ return t === 0.0 ? t : Math.pow(2.0, 10.0 * (t - 1.0));
178
+ }
179
+
180
+ /**
181
+ * @param {number} t
182
+ * @returns {number}
183
+ */
184
+ export function expoOut(t) {
185
+ return t === 1.0 ? t : 1.0 - Math.pow(2.0, -10.0 * t);
186
+ }
187
+
188
+ /**
189
+ * @param {number} t
190
+ * @returns {number}
191
+ */
192
+ export function quadInOut(t) {
193
+ t /= 0.5;
194
+ if (t < 1) return 0.5 * t * t;
195
+ t--;
196
+ return -0.5 * (t * (t - 2) - 1);
197
+ }
198
+
199
+ /**
200
+ * @param {number} t
201
+ * @returns {number}
202
+ */
203
+ export function quadIn(t) {
204
+ return t * t;
205
+ }
206
+
207
+ /**
208
+ * @param {number} t
209
+ * @returns {number}
210
+ */
211
+ export function quadOut(t) {
212
+ return -t * (t - 2.0);
213
+ }
214
+
215
+ /**
216
+ * @param {number} t
217
+ * @returns {number}
218
+ */
219
+ export function quartInOut(t) {
220
+ return t < 0.5 ? +8.0 * Math.pow(t, 4.0) : -8.0 * Math.pow(t - 1.0, 4.0) + 1.0;
221
+ }
222
+
223
+ /**
224
+ * @param {number} t
225
+ * @returns {number}
226
+ */
227
+ export function quartIn(t) {
228
+ return Math.pow(t, 4.0);
229
+ }
230
+
231
+ /**
232
+ * @param {number} t
233
+ * @returns {number}
234
+ */
235
+ export function quartOut(t) {
236
+ return Math.pow(t - 1.0, 3.0) * (1.0 - t) + 1.0;
237
+ }
238
+
239
+ /**
240
+ * @param {number} t
241
+ * @returns {number}
242
+ */
243
+ export function quintInOut(t) {
244
+ if ((t *= 2) < 1) return 0.5 * t * t * t * t * t;
245
+ return 0.5 * ((t -= 2) * t * t * t * t + 2);
246
+ }
247
+
248
+ /**
249
+ * @param {number} t
250
+ * @returns {number}
251
+ */
252
+ export function quintIn(t) {
253
+ return t * t * t * t * t;
254
+ }
255
+
256
+ /**
257
+ * @param {number} t
258
+ * @returns {number}
259
+ */
260
+ export function quintOut(t) {
261
+ return --t * t * t * t * t + 1;
262
+ }
263
+
264
+ /**
265
+ * @param {number} t
266
+ * @returns {number}
267
+ */
268
+ export function sineInOut(t) {
269
+ return -0.5 * (Math.cos(Math.PI * t) - 1);
270
+ }
271
+
272
+ /**
273
+ * @param {number} t
274
+ * @returns {number}
275
+ */
276
+ export function sineIn(t) {
277
+ const v = Math.cos(t * Math.PI * 0.5);
278
+ if (Math.abs(v) < 1e-14) return 1;
279
+ else return 1 - v;
280
+ }
281
+
282
+ /**
283
+ * @param {number} t
284
+ * @returns {number}
285
+ */
286
+ export function sineOut(t) {
287
+ return Math.sin((t * Math.PI) / 2);
288
+ }
289
+
290
+ export function cubicBezier(x1, y1, x2, y2) {
291
+ return bezierEasing(x1, y1, x2, y2);
292
+ }
@@ -0,0 +1,114 @@
1
+ import Value from '../utils/value.js';
2
+ import { newTarget } from '../utils/target.js';
3
+
4
+
5
+ export default class Ticker {
6
+ constructor() {
7
+ this._listeners = new Set;
8
+ this._targets = new GlobalTargets;
9
+
10
+ this.running = false;
11
+
12
+ this._previousTick = null;
13
+ }
14
+
15
+ static global() {
16
+ if (typeof window === 'undefined')
17
+ return null;
18
+
19
+ if (typeof window.chnobliTicker === 'undefined')
20
+ window.chnobliTicker = new Ticker;
21
+ return window.chnobliTicker;
22
+ }
23
+
24
+ add(fn) {
25
+ this._listeners.add(fn);
26
+
27
+ if (!this.running) {
28
+ this.running = true;
29
+ requestAnimationFrame(a => this._tick(a));
30
+ }
31
+
32
+ return {
33
+ remove: () => {
34
+ this.remove(fn);
35
+ }
36
+ };
37
+ }
38
+
39
+ remove(fn) {
40
+ this._listeners.delete(fn);
41
+ }
42
+
43
+ _tick(elapsed) {
44
+ if (this._listeners.size === 0) {
45
+ this.running = false;
46
+ this._previousTick = null;
47
+ return;
48
+ }
49
+
50
+ if (this._previousTick === null)
51
+ this._previousTick = elapsed;
52
+
53
+ let change = elapsed - this._previousTick;
54
+ // lagSmoothing
55
+ if (change > 500) {
56
+ console.log('ticker lagged');
57
+ change = 33;
58
+ }
59
+ this._previousTick = elapsed;
60
+
61
+ // const changes = new PropertyChanges;
62
+
63
+ for (const fn of this._listeners) {
64
+ fn(change, {
65
+ // changes,
66
+ remove: () => this.remove(fn)
67
+ });
68
+ }
69
+
70
+ this._targets.apply();
71
+
72
+ requestAnimationFrame(a => this._tick(a));
73
+ }
74
+ }
75
+
76
+ // class PropertyChanges {
77
+ // constructor() {
78
+ // this._targets = new Map;
79
+ // }
80
+
81
+ // // target is expected to be an html element
82
+ // get(target) {
83
+
84
+ // }
85
+ // }
86
+
87
+ // class TargetChanges {
88
+ // constructor() {
89
+ // // x, y, rotate, skew, scale
90
+ // this.transform = {};
91
+ // }
92
+ // }
93
+
94
+ class GlobalTargets {
95
+ constructor() {
96
+ this._targets = new Map;
97
+ }
98
+
99
+ register(target) {
100
+ let storedTarget = this._targets.get(target);
101
+ if (!storedTarget) {
102
+ storedTarget = newTarget(target);
103
+ this._targets.set(target, storedTarget);
104
+ }
105
+
106
+ return storedTarget;
107
+ }
108
+
109
+ apply() {
110
+ for (const target of this._targets.values()) {
111
+ target.apply();
112
+ }
113
+ }
114
+ }