chnobli 0.1.10 → 0.1.12

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.
Files changed (120) hide show
  1. package/LICENSE.md +21 -0
  2. package/dist/animation/Animation.d.ts +2 -1
  3. package/dist/animation/Animation.d.ts.map +1 -1
  4. package/dist/animation/Animation.js +5 -3
  5. package/dist/animation/Animation.js.map +1 -1
  6. package/dist/animator/Animator.d.ts +30 -0
  7. package/dist/animator/Animator.d.ts.map +1 -0
  8. package/dist/animator/Animator.js +97 -0
  9. package/dist/animator/Animator.js.map +1 -0
  10. package/dist/animator/index.d.ts +5 -0
  11. package/dist/animator/index.d.ts.map +1 -0
  12. package/dist/animator/index.js +53 -0
  13. package/dist/animator/index.js.map +1 -0
  14. package/dist/animator/public.d.ts +16 -0
  15. package/dist/animator/public.d.ts.map +1 -0
  16. package/dist/animator/public.js +50 -0
  17. package/dist/animator/public.js.map +1 -0
  18. package/dist/chnobli.d.ts +3 -3
  19. package/dist/chnobli.d.ts.map +1 -1
  20. package/dist/chnobli.js.map +1 -1
  21. package/dist/property/ClassProperty.js +1 -1
  22. package/dist/property/ClassProperty.js.map +1 -1
  23. package/dist/property/TransformXYProperty.d.ts.map +1 -1
  24. package/dist/scroll/Scroll.d.ts +24 -3
  25. package/dist/scroll/Scroll.d.ts.map +1 -1
  26. package/dist/scroll/Scroll.js +157 -43
  27. package/dist/scroll/Scroll.js.map +1 -1
  28. package/dist/scroll/ScrollEvent.d.ts.map +1 -1
  29. package/dist/scroll/ScrollEvent.js +1 -0
  30. package/dist/scroll/ScrollEvent.js.map +1 -1
  31. package/dist/scroll/index.d.ts +2 -0
  32. package/dist/scroll/index.d.ts.map +1 -1
  33. package/dist/scroll/index.js +1 -0
  34. package/dist/scroll/index.js.map +1 -1
  35. package/dist/scroll/public.d.ts +19 -1
  36. package/dist/scroll/public.d.ts.map +1 -1
  37. package/dist/scroll/public.js +26 -1
  38. package/dist/scroll/public.js.map +1 -1
  39. package/dist/target/DomTarget.js +1 -1
  40. package/dist/target/DomTarget.js.map +1 -1
  41. package/dist/target/ObjectTarget.d.ts +11 -10
  42. package/dist/target/ObjectTarget.d.ts.map +1 -1
  43. package/dist/target/ObjectTarget.js +20 -13
  44. package/dist/target/ObjectTarget.js.map +1 -1
  45. package/dist/target/TestDomNode.d.ts.map +1 -1
  46. package/dist/timeline/Timeline.d.ts +2 -1
  47. package/dist/timeline/Timeline.d.ts.map +1 -1
  48. package/dist/timeline/Timeline.js +4 -4
  49. package/dist/timeline/Timeline.js.map +1 -1
  50. package/dist/timeline/public.d.ts +11 -4
  51. package/dist/timeline/public.d.ts.map +1 -1
  52. package/dist/timeline/public.js +19 -11
  53. package/dist/timeline/public.js.map +1 -1
  54. package/dist/timing/AnimationFrameTicker.d.ts.map +1 -1
  55. package/dist/timing/AnimationFrameTicker.js +5 -1
  56. package/dist/timing/AnimationFrameTicker.js.map +1 -1
  57. package/dist/timing/TestTicker.d.ts.map +1 -1
  58. package/dist/timing/TestTicker.js +5 -1
  59. package/dist/timing/TestTicker.js.map +1 -1
  60. package/dist/timing/Ticker.d.ts +4 -2
  61. package/dist/timing/Ticker.d.ts.map +1 -1
  62. package/dist/timing/Timing.d.ts +47 -2
  63. package/dist/timing/Timing.d.ts.map +1 -1
  64. package/dist/timing/Timing.js +118 -84
  65. package/dist/timing/Timing.js.map +1 -1
  66. package/dist/timing/easing.d.ts +1 -2
  67. package/dist/timing/easing.d.ts.map +1 -1
  68. package/dist/timing/easing.js.map +1 -1
  69. package/dist/utils/Events.js +1 -1
  70. package/dist/utils/Events.js.map +1 -1
  71. package/dist/utils/motionpath.js +5 -5
  72. package/dist/utils/motionpath.js.map +1 -1
  73. package/dist/utils/utils.d.ts.map +1 -1
  74. package/dist/utils/utils.js +0 -8
  75. package/dist/utils/utils.js.map +1 -1
  76. package/dist/values/ColorValue.d.ts.map +1 -1
  77. package/dist/values/ColorValue.js +1 -0
  78. package/dist/values/ColorValue.js.map +1 -1
  79. package/dist/values/StyleValue.d.ts.map +1 -1
  80. package/dist/values/Value.d.ts.map +1 -1
  81. package/dist/values/Value.js +1 -1
  82. package/dist/values/Value.js.map +1 -1
  83. package/eslint.config.js +23 -0
  84. package/jsr.json +16 -0
  85. package/package.json +12 -4
  86. package/src/animation/Animation.ts +6 -3
  87. package/src/animator/Animator.ts +130 -0
  88. package/src/animator/index.ts +56 -0
  89. package/src/animator/public.ts +66 -0
  90. package/src/chnobli.ts +3 -2
  91. package/src/property/ClassProperty.ts +1 -1
  92. package/src/property/TransformXYProperty.ts +1 -1
  93. package/src/scroll/Scroll.ts +204 -51
  94. package/src/scroll/ScrollEvent.ts +1 -0
  95. package/src/scroll/index.ts +3 -0
  96. package/src/scroll/public.ts +35 -2
  97. package/src/target/DomTarget.ts +1 -1
  98. package/src/target/ObjectTarget.ts +19 -13
  99. package/src/target/TestDomNode.ts +3 -3
  100. package/src/tests/properties.test.ts +86 -3
  101. package/src/tests/scroll.test.ts +36 -0
  102. package/src/tests/timeline.test.ts +1 -1
  103. package/src/tests/timing.test.ts +32 -0
  104. package/src/tests/usecases.test.ts +40 -2
  105. package/src/tests/values.test.ts +9 -9
  106. package/src/timeline/Timeline.ts +5 -4
  107. package/src/timeline/public.ts +34 -12
  108. package/src/timing/AnimationFrameTicker.ts +6 -1
  109. package/src/timing/TestTicker.ts +7 -1
  110. package/src/timing/Ticker.ts +5 -1
  111. package/src/timing/Timing.ts +133 -97
  112. package/src/timing/easing.ts +6 -1
  113. package/src/utils/Events.ts +1 -1
  114. package/src/utils/motionpath.ts +5 -5
  115. package/src/utils/utils.ts +13 -16
  116. package/src/values/ColorValue.ts +5 -4
  117. package/src/values/StyleValue.ts +4 -4
  118. package/src/values/Value.ts +8 -8
  119. package/.eslintrc.cjs +0 -39
  120. package/src/animator.js +0 -187
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import ScrollTimeline from '../scroll/Scroll.js';
3
+
4
+ function scrollWith(offsets: [number, number, number][]) {
5
+ const s = new ScrollTimeline();
6
+ s.offsets = offsets.map(([y, view, position]) => ({ y, view, position }));
7
+ return (y: number) => (s as any).calcPosition({ y, height: 100 });
8
+ }
9
+
10
+ describe('scroll', () => {
11
+ it('position', () => {
12
+ // from 0 to 900, like a page that can't scroll past the end
13
+ const pos = scrollWith([
14
+ [0, 0, 0],
15
+ [900, 0, 1],
16
+ ]);
17
+
18
+ expect(pos(-1)).toBe(-1);
19
+ expect(pos(0)).toBe(0);
20
+ expect(pos(450)).toBe(0.5);
21
+ expect(pos(900)).toBe(1);
22
+ expect(pos(901)).toBe(2);
23
+ });
24
+
25
+ it('position with three offsets', () => {
26
+ const pos = scrollWith([
27
+ [0, 0, 0],
28
+ [200, 1, 0.5],
29
+ [500, 0, 1],
30
+ ]);
31
+
32
+ expect(pos(100)).toBe(0.5);
33
+ expect(pos(300)).toBe(0.75);
34
+ expect(pos(500)).toBe(1);
35
+ });
36
+ });
@@ -254,7 +254,7 @@ describe('timeline', () => {
254
254
  const ticker = new TestTicker();
255
255
 
256
256
  const target = el();
257
- const tl = timeline()
257
+ const _tl = timeline()
258
258
  .add(target, {
259
259
  width: 50,
260
260
  duration: 10,
@@ -150,6 +150,7 @@ describe('timing', () => {
150
150
  timing.reverse();
151
151
  expect(timing.state).toBe(STATE_BEFORE);
152
152
  expect(timing.position).toBe(0);
153
+ expect(timing.positionAbsolute()).toBe(0);
153
154
 
154
155
  // >
155
156
  timing.advance(25);
@@ -270,4 +271,35 @@ describe('timing', () => {
270
271
  timing.advance(50);
271
272
  expect(timing.position).toBe(0.25);
272
273
  });
274
+
275
+ it('forward and back', () => {
276
+ const timing = new Timing({
277
+ duration: 100,
278
+ reversed: true,
279
+ });
280
+ // I wan't the start value to be 0
281
+ timing.seek(1);
282
+ expect(timing.reversed).toBe(true);
283
+ expect(timing.state).toBe(STATE_ENDED);
284
+ expect(timing.position).toBe(0);
285
+
286
+ timing.reverse();
287
+ expect(timing.state).toBe(STATE_START);
288
+ timing.advance(25);
289
+ expect(timing.reversed).toBe(false);
290
+ expect(timing.state).toBe(STATE_RUNNING);
291
+ expect(timing.position).toBe(0.25);
292
+
293
+ timing.reverse();
294
+ timing.advance(20);
295
+ expect(timing.reversed).toBe(true);
296
+ expect(timing.state).toBe(STATE_RUNNING);
297
+ expect(timing.position.toFixed(2)).toBe('0.05');
298
+
299
+ timing.reverse();
300
+ timing.advance(20);
301
+ expect(timing.reversed).toBe(false);
302
+ expect(timing.state).toBe(STATE_RUNNING);
303
+ expect(timing.position.toFixed(2)).toBe('0.25');
304
+ });
273
305
  });
@@ -1,8 +1,8 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import TestTicker from '../timing/TestTicker.js';
3
3
  import TestResponsiveEvent from '../responsive/TestEvent.js';
4
- import { animate, timeline } from '../chnobli.js';
5
- import { responsive } from '../utils/utils.js';
4
+ import { timeline } from '../chnobli.js';
5
+ import { fromTo, responsive } from '../utils/utils.js';
6
6
  import { el } from '../target/TestDomNode.js';
7
7
 
8
8
  describe('usecases', () => {
@@ -64,4 +64,42 @@ describe('usecases', () => {
64
64
  expect(ctn.style.maxHeight).toBe(undefined);
65
65
  expect(itm.classList.contains('open')).toBe(false);
66
66
  });
67
+
68
+ it('accordion2', () => {
69
+ const ticker = new TestTicker();
70
+ const _respEv = new TestResponsiveEvent();
71
+
72
+ const ctn = el();
73
+ ctn.computedStyle.maxHeight = 'none';
74
+ // @ts-ignore
75
+ ctn.scrollHeight = '100px';
76
+
77
+ const tl = timeline({ reversed: true }).add(ctn, {
78
+ height: fromTo(
79
+ 0,
80
+ responsive(el => el.scrollHeight),
81
+ ),
82
+ duration: 100,
83
+ });
84
+
85
+ const toggle = () => {
86
+ tl.reverse();
87
+ tl.play();
88
+ };
89
+
90
+ // on click reverse or not
91
+ toggle();
92
+ ticker.run(1);
93
+ expect(ctn.style.height).toBe('1.000px');
94
+ ticker.run(49);
95
+ expect(ctn.style.height).toBe('50.000px');
96
+ toggle();
97
+ ticker.run(1);
98
+ expect(ctn.style.height).toBe('49.000px');
99
+ ticker.run(29);
100
+ expect(ctn.style.height).toBe('20.000px');
101
+ toggle();
102
+ ticker.run(1);
103
+ expect(ctn.style.height).toBe('21.000px');
104
+ });
67
105
  });
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import TestTicker from '../timing/TestTicker.js';
3
- import { animate, timeline } from '../chnobli.js';
4
- import { responsive, from, fromTo } from '../utils/utils.js';
3
+ import { timeline } from '../chnobli.js';
4
+ import { from, fromTo } from '../utils/utils.js';
5
5
  import { el, getRootElement } from '../target/TestDomNode.js';
6
6
 
7
7
  describe('values', () => {
@@ -12,7 +12,7 @@ describe('values', () => {
12
12
  root.computedStyle.fontSize = '13px';
13
13
 
14
14
  const div = el();
15
- const tl = timeline()
15
+ const _tl = timeline()
16
16
  .add(div, {
17
17
  width: '10rem',
18
18
  duration: 5,
@@ -35,7 +35,7 @@ describe('values', () => {
35
35
 
36
36
  const div = el();
37
37
  div.computedStyle.width = '10px';
38
- const tl = timeline()
38
+ const _tl = timeline()
39
39
  .add(div, {
40
40
  x: '10%',
41
41
  width: '50%',
@@ -62,7 +62,7 @@ describe('values', () => {
62
62
 
63
63
  const div = el();
64
64
  div.computedStyle.padding = '5px';
65
- const tl = timeline()
65
+ const _tl = timeline()
66
66
  .add(div, {
67
67
  padding: '10px 20rem',
68
68
  duration: 10,
@@ -81,7 +81,7 @@ describe('values', () => {
81
81
 
82
82
  const div = el();
83
83
  div.computedStyle.display = 'block';
84
- const tl = timeline()
84
+ const _tl = timeline()
85
85
  .add(div, {
86
86
  display: 'none',
87
87
  position: from('absolute'),
@@ -100,7 +100,7 @@ describe('values', () => {
100
100
  expect(div.style.position).toBe(undefined);
101
101
  expect(div.style.visibility).toBe('visible');
102
102
 
103
- tl.seek(-1);
103
+ _tl.seek(-1);
104
104
  ticker.run();
105
105
  expect(div.style.display).toBe(undefined);
106
106
  expect(div.style.position).toBe(undefined);
@@ -112,7 +112,7 @@ describe('values', () => {
112
112
  const div = el();
113
113
  div.computedStyle.color = 'rgb(120, 130, 20)';
114
114
 
115
- const tl = timeline()
115
+ const _tl = timeline()
116
116
  .add(div, {
117
117
  color: 'black',
118
118
  backgroundColor: 'hsl(10, 80%, 40%)',
@@ -139,7 +139,7 @@ describe('values', () => {
139
139
  div.computedStyle['--color'] = 'rgb(120, 130, 20)';
140
140
  div.computedStyle['--border'] = '20px';
141
141
 
142
- const tl = timeline()
142
+ const _tl = timeline()
143
143
  .add(div, {
144
144
  '--color': 'black',
145
145
  '--border': '1px 20px',
@@ -10,6 +10,7 @@ import Timing, {
10
10
  import Ticker from '../timing/Ticker.js';
11
11
  import Animation from '../animation/Animation.js';
12
12
  import AnimationFrameTicker from '../timing/AnimationFrameTicker.js';
13
+ import { DestroyOptions } from './public.js';
13
14
 
14
15
  type Entry = {
15
16
  type: 'label' | 'animation' | 'timeline';
@@ -185,7 +186,7 @@ export default class Timeline {
185
186
 
186
187
  const pos = this.entries[i - 1 - entry.relativeOffset]?.end ?? 0;
187
188
 
188
- let start = offset.calculate(pos);
189
+ const start = offset.calculate(pos);
189
190
  if (start < 0) throw new Error('cannot go in the past start < 0');
190
191
 
191
192
  let end = start;
@@ -325,17 +326,17 @@ export default class Timeline {
325
326
  this.ticker.applyTargets();
326
327
  }
327
328
 
328
- destroy() {
329
+ destroy(opts: DestroyOptions) {
329
330
  for (const entry of this.entries) {
330
331
  if (entry.type === 'timeline') {
331
- entry.value.destroy();
332
+ entry.value.destroy(opts);
332
333
  continue;
333
334
  }
334
335
 
335
336
  if (entry.type !== 'animation') continue;
336
337
 
337
338
  const animation = entry.value;
338
- animation.destroy();
339
+ animation.destroy(opts);
339
340
  }
340
341
 
341
342
  this.entries = [];
@@ -38,12 +38,21 @@ export type TimelineProps = {
38
38
  // alternate
39
39
  // reversed
40
40
 
41
+ /**
42
+ * When set will keep all values when destroy is called
43
+ */
44
+ keepValues?: boolean;
45
+
41
46
  // other properties
42
47
  [key: string]: any;
43
48
  };
44
49
 
45
50
  export type Offset = number | string | Stagger<number | string>;
46
51
 
52
+ export type DestroyOptions = {
53
+ keepValues?: boolean;
54
+ };
55
+
47
56
  export default class PublicTimeline {
48
57
  /**
49
58
  * @ignore
@@ -60,13 +69,14 @@ export default class PublicTimeline {
60
69
  private _triggeredStart: boolean;
61
70
 
62
71
  private _state: number;
63
- private _renderedOnce: boolean;
64
72
  private _runningTicker: any;
65
73
  private _responsiveBlocks: { responsive: () => void }[];
66
74
  private _responsiveEvent: any;
67
75
  private _smoothSeek: SmoothSeek | null;
68
76
  private _seekTo: number;
69
77
 
78
+ private _destroyOpts: DestroyOptions;
79
+
70
80
  constructor(props: TimelineProps = {}) {
71
81
  this._defaults = takeProp(props, 'defaults', {});
72
82
  this._responsive = takeProp(props, 'responsive', true);
@@ -81,6 +91,12 @@ export default class PublicTimeline {
81
91
  );
82
92
  this._seekTo = 0;
83
93
 
94
+ this._destroyOpts = {
95
+ // we set this to undefined so we can have other default values
96
+ // for example in chnobli-svelte
97
+ keepValues: takeProp(props, 'keepValues', undefined),
98
+ };
99
+
84
100
  this._inner = new Timeline(props);
85
101
 
86
102
  // 'start', 'end'
@@ -88,7 +104,6 @@ export default class PublicTimeline {
88
104
  this._triggeredStart = false;
89
105
 
90
106
  this._state = STATE_PAUSED;
91
- this._renderedOnce = false;
92
107
  this._runningTicker = null;
93
108
  this._responsiveBlocks = [];
94
109
  if (this._responsive) {
@@ -227,7 +242,7 @@ export default class PublicTimeline {
227
242
  this._inner.init();
228
243
 
229
244
  if (this._smoothSeek) {
230
- this._smoothSeek.update(this._inner.timing.positionLinear(), pos);
245
+ this._smoothSeek.update(this._inner.timing.positionAbsolute(), pos);
231
246
 
232
247
  if (this._state !== STATE_PLAYING) this._state = STATE_SEEK;
233
248
  } else {
@@ -331,9 +346,11 @@ export default class PublicTimeline {
331
346
  /**
332
347
  * Destroys this timeline and resets all props
333
348
  */
334
- destroy() {
349
+ destroy(opts: DestroyOptions = {}) {
350
+ opts.keepValues = opts.keepValues ?? this._destroyOpts.keepValues;
351
+
335
352
  this._stopTicker();
336
- this._inner.destroy();
353
+ this._inner.destroy(opts);
337
354
  this._events.destroy();
338
355
  this._responsiveEvent?.remove();
339
356
  this._responsiveBlocks = [];
@@ -360,7 +377,7 @@ export default class PublicTimeline {
360
377
  const smoothSeekEnded = () =>
361
378
  !this._smoothSeek || this._smoothSeek.state >= STATE_ENDED;
362
379
 
363
- this._runningTicker = this._inner.ticker.add(change => {
380
+ this._runningTicker = this._inner.ticker.add((change, api) => {
364
381
  if (!this._triggeredStart) {
365
382
  // todo check that we are at the start
366
383
  if (this._inner.timing.state <= STATE_BEFORE) {
@@ -385,11 +402,19 @@ export default class PublicTimeline {
385
402
  }
386
403
 
387
404
  this._inner.render();
405
+ const pos = this._inner.timing.position;
406
+ api.onApplied(() => {
407
+ this._events.trigger('update', pos);
408
+ });
388
409
 
389
410
  // we rendered once let's stop
390
411
  const renderOnce = this._state === STATE_RENDER_ONCE;
391
412
 
392
- if (renderOnce || (timingEnded() && smoothSeekEnded())) {
413
+ if (
414
+ renderOnce ||
415
+ (smoothSeekEnded() &&
416
+ (this._state === STATE_SEEK || timingEnded()))
417
+ ) {
393
418
  this._maybeTriggerEndEvent();
394
419
 
395
420
  this._state = STATE_PAUSED;
@@ -413,12 +438,9 @@ export default class PublicTimeline {
413
438
  * @ignore
414
439
  */
415
440
  _onResponsive(
416
- a: { width: number; height: number },
417
- b: { remove: () => void },
441
+ _a: { width: number; height: number },
442
+ _b: { remove: () => void },
418
443
  ) {
419
- // // make sure we don't render something if we never did
420
- // if (!this._renderedOnce)
421
- // return;
422
444
  this._inner.update();
423
445
  }
424
446
  }
@@ -91,16 +91,21 @@ export default class AnimationFrameTicker implements Ticker {
91
91
  this._previousTick = elapsed;
92
92
 
93
93
  // const changes = new PropertyChanges;
94
+ const onApplied: (() => void)[] = [];
94
95
 
95
96
  for (const fn of this._listeners) {
96
97
  fn(change, {
97
- // changes,
98
98
  remove: () => this.remove(fn),
99
+ onApplied: fnOnApplied => onApplied.push(fnOnApplied),
99
100
  });
100
101
  }
101
102
 
102
103
  this._targets.apply();
103
104
 
105
+ for (const fn of onApplied) {
106
+ fn();
107
+ }
108
+
104
109
  requestAnimationFrame(a => this._tick(a));
105
110
  }
106
111
  }
@@ -81,15 +81,21 @@ export default class TestTicker implements Ticker {
81
81
  }
82
82
  this._previousTick = elapsed;
83
83
 
84
+ const onApplied: (() => void)[] = [];
85
+
84
86
  for (const fn of this._listeners) {
85
87
  fn(change, {
86
- // changes,
87
88
  remove: () => this.remove(fn),
89
+ onApplied: fn => onApplied.push(fn),
88
90
  });
89
91
  }
90
92
 
91
93
  this.targets.apply();
92
94
 
95
+ for (const fn of onApplied) {
96
+ fn();
97
+ }
98
+
93
99
  if (this._listeners.size === 0) {
94
100
  this.running = false;
95
101
  this._previousTick = null;
@@ -1,6 +1,10 @@
1
1
  import { Target } from '../target/Target.js';
2
2
 
3
- export type Callback = (change: number, api: { remove: () => void }) => void;
3
+ export type CallbackApi = {
4
+ remove: () => void;
5
+ onApplied: (fn: () => void) => void;
6
+ };
7
+ export type Callback = (change: number, api: CallbackApi) => void;
4
8
 
5
9
  export default interface Ticker {
6
10
  registerTarget(target: any): Target;