@oscarpalmer/timer 0.18.0 → 0.20.0
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/README.md +34 -29
- package/dist/timer.js +107 -166
- package/package.json +10 -37
- package/src/index.ts +107 -117
- package/types/index.d.ts +22 -64
- package/dist/timer.iife.js +0 -169
package/README.md
CHANGED
|
@@ -8,8 +8,6 @@ A better solution for timeout- and interval-based timers.
|
|
|
8
8
|
|
|
9
9
|
Timer is available on _npm_ as [`@oscarpalmer/timer`](https://www.npmjs.com/package/@oscarpalmer/timer) to be bundled with your awesome projects.
|
|
10
10
|
|
|
11
|
-
If you don't need to bundle things, you can use the CDN-version and include a script in your proejcts right away, thanks to [jsDelivr](https://www.jsdelivr.com/package/npm/@oscarpalmer/timer) and [UNPKG](https://unpkg.com/@oscarpalmer/timer).
|
|
12
|
-
|
|
13
11
|
## Getting started
|
|
14
12
|
|
|
15
13
|
This is fairly lightweight package, so hopefully you'll be up and running in seconds :blush:
|
|
@@ -21,55 +19,62 @@ The timers can be called with nice helper methods, which also auto-starts the ti
|
|
|
21
19
|
```typescript
|
|
22
20
|
import {repeat, wait} from '@oscarpalmer/timer';
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const waited = wait(waitedCallback);
|
|
23
|
+
const repeated = repeat(repeatedCallback, 10);
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
Or they can be created using
|
|
26
|
+
Or they can be created using the `new`-keyword, but without being auto-started:
|
|
29
27
|
|
|
30
28
|
```typescript
|
|
31
|
-
import {
|
|
29
|
+
import {Timer} from '@oscarpalmer/timer';
|
|
32
30
|
|
|
33
|
-
waited = new
|
|
34
|
-
repeated = new
|
|
31
|
+
const waited = new Timer(waitedCallback);
|
|
32
|
+
const repeated = new Timer(repeatedCallback, 10);
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
## Parameters
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
When creating a _Timer_, either with the new `new`-keyword or using the functions, you can configure the timer with a few parameters:
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
|Parameter|Description|
|
|
40
|
+
|--------:|:----------|
|
|
41
|
+
|`callback`|Callback function to be invoked for each run that are __required__ for all timers.<br>For more information on callbacks, please read [the callbacks section](#callbacks).|
|
|
42
|
+
|`count`|How many times the timer should run.<br>If no value is provided, it will default to `1` when using the `new`-keyword and the `wait`-method, but throws an error for the `repeat`-method.|
|
|
43
|
+
|`time`|How many milliseconds between each invokations of the provided callback.<br>Defaults to `0`, which is not really _0_ milliseconds, but close enough :wink:|
|
|
44
|
+
|`after`|A callback to run after the timer finishes, both when cancelled and completed.<br>If _count_ is greater than `1` and _after_ __is not__ `undefined`, a function is expected.|
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
// With auto-start
|
|
45
|
-
var waited = Timer.wait(callback, time);
|
|
46
|
-
var repeated = Timer.repeat(callback, time, count);
|
|
46
|
+
## Methods and properties
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
waited = new Timer.Waited(callback, time);
|
|
50
|
-
repeated = new Timer.Repeated(callback, time, count);
|
|
51
|
-
```
|
|
48
|
+
An instance of _Timer_ also has a few helpful methods and properties:
|
|
52
49
|
|
|
53
|
-
|
|
50
|
+
|Name|Type|Description|
|
|
51
|
+
|---:|----|:----------|
|
|
52
|
+
|`active`|_Property_|A `boolean` value to check if the timer is running|
|
|
53
|
+
|`finished`|_Property_|A `boolean` value to check if the timer was able to finish|
|
|
54
|
+
|`start()`|_Method_|Starts the timer.<br>Necessary when creating a timer using the class syntax _(e.g. `new Waited...`)_, but helpful when the timer needs to be started at other times, as well.|
|
|
55
|
+
|`stop()`|_Method_|Stops the timer|
|
|
56
|
+
|`restart()`|_Method_|Restarts the timer|
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
## Callbacks
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|-----:|:----------|
|
|
59
|
-
|`start()`|Starts the timer: necessary when creating a timer using the class syntax _(e.g. `new Waited...`)_, but helpful when the timer needs to be started at other times, as well|
|
|
60
|
-
|`stop()`|Stops the timer|
|
|
61
|
-
|`restart()`|Restarts the timer|
|
|
60
|
+
Callbacks for both waited and repeated timers receive one parameter:
|
|
62
61
|
|
|
63
|
-
|
|
62
|
+
```typescript
|
|
63
|
+
function callback(index) {
|
|
64
|
+
// 'index' is the current step
|
|
65
|
+
// starts at 0, goes up to a maximum of count - 1
|
|
66
|
+
// for this example: 0 → 9
|
|
67
|
+
};
|
|
68
|
+
```
|
|
64
69
|
|
|
65
|
-
When you create a repeated timer, you can also provide a
|
|
70
|
+
When you create a repeated timer, you can also provide a callback to run when the timer stops, as below:
|
|
66
71
|
|
|
67
72
|
```typescript
|
|
68
73
|
function after(finished: boolean) {
|
|
69
74
|
// Let's do something fun!
|
|
70
75
|
}
|
|
71
76
|
|
|
72
|
-
repeat(() => {},
|
|
77
|
+
repeat(() => {}, 10, after);
|
|
73
78
|
```
|
|
74
79
|
|
|
75
80
|
The `finished`-parameter for the `after`-function can be used to determine if the timer was stopped manually, or if it was able to finish its work.
|
package/dist/timer.js
CHANGED
|
@@ -1,170 +1,111 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
elapsedMinimum < timedConfiguration.time &&
|
|
41
|
-
timedConfiguration.time < elapsedMaximum
|
|
42
|
-
) {
|
|
43
|
-
if (timedState.active) {
|
|
44
|
-
timedCallbacks.default(isRepeated ? index : undefined);
|
|
45
|
-
}
|
|
46
|
-
index += 1;
|
|
47
|
-
if (isRepeated && index < timedConfiguration.count) {
|
|
48
|
-
start = undefined;
|
|
49
|
-
} else {
|
|
50
|
-
timedState.finished = true;
|
|
51
|
-
timed.stop();
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
timedState.frame = request(step);
|
|
56
|
-
}
|
|
57
|
-
timedState.frame = request(step);
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var run = function(timer) {
|
|
3
|
+
const { _configuration, _state } = timer;
|
|
4
|
+
_state.active = true;
|
|
5
|
+
_state.finished = false;
|
|
6
|
+
const isRepeated = _configuration.count > 1;
|
|
7
|
+
let index = 0;
|
|
8
|
+
let start;
|
|
9
|
+
function step(timestamp) {
|
|
10
|
+
if (!_state.active) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
start ??= timestamp;
|
|
14
|
+
const elapsed = timestamp - start;
|
|
15
|
+
const elapsedMinimum = elapsed - milliseconds;
|
|
16
|
+
const elapsedMaximum = elapsed + milliseconds;
|
|
17
|
+
if (elapsedMinimum < _configuration.time && _configuration.time < elapsedMaximum) {
|
|
18
|
+
if (_state.active) {
|
|
19
|
+
_configuration.callbacks.default(index);
|
|
20
|
+
}
|
|
21
|
+
index += 1;
|
|
22
|
+
if (isRepeated && index < _configuration.count) {
|
|
23
|
+
start = undefined;
|
|
24
|
+
} else {
|
|
25
|
+
_state.finished = true;
|
|
26
|
+
timer.stop();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
_state.frame = requestAnimationFrame(step);
|
|
31
|
+
}
|
|
32
|
+
_state.frame = requestAnimationFrame(step);
|
|
33
|
+
};
|
|
34
|
+
function repeat(callback, count, afterOrTime, after) {
|
|
35
|
+
if (typeof count !== "number" || count < 2) {
|
|
36
|
+
throw new TypeError("A repeated timer must have a number greater than or equal to 2 as its run count");
|
|
37
|
+
}
|
|
38
|
+
const afterOrTimeIsFunction = typeof afterOrTime === "function";
|
|
39
|
+
return new Timer(callback, afterOrTimeIsFunction ? 0 : afterOrTime, count, afterOrTimeIsFunction ? afterOrTime : after).start();
|
|
58
40
|
}
|
|
59
|
-
class Timed {
|
|
60
|
-
get active() {
|
|
61
|
-
return state.get(this)?.active ?? false;
|
|
62
|
-
}
|
|
63
|
-
get finished() {
|
|
64
|
-
return !this.active && (state.get(this)?.finished ?? false);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* @param {Callback} callback
|
|
68
|
-
* @param {number} time
|
|
69
|
-
* @param {number} count
|
|
70
|
-
* @param {AfterCallback=} afterCallback
|
|
71
|
-
*/
|
|
72
|
-
constructor(callback, time, count, afterCallback) {
|
|
73
|
-
const isRepeated = this instanceof Repeated;
|
|
74
|
-
const type = isRepeated ? 'repeated' : 'waited';
|
|
75
|
-
if (typeof callback !== 'function') {
|
|
76
|
-
throw new TypeError(`A ${type} timer must have a callback function`);
|
|
77
|
-
}
|
|
78
|
-
if (typeof time !== 'number' || time < 0) {
|
|
79
|
-
throw new TypeError(
|
|
80
|
-
`A ${type} timer must have a non-negative number as its time`,
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
if (isRepeated && (typeof count !== 'number' || count < 2)) {
|
|
84
|
-
throw new TypeError(
|
|
85
|
-
'A repeated timer must have a number above 1 as its repeat count',
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
if (
|
|
89
|
-
isRepeated &&
|
|
90
|
-
afterCallback !== undefined &&
|
|
91
|
-
typeof afterCallback !== 'function'
|
|
92
|
-
) {
|
|
93
|
-
throw new TypeError(
|
|
94
|
-
"A repeated timer's after-callback must be a function",
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
callbacks.set(this, {
|
|
98
|
-
after: afterCallback,
|
|
99
|
-
default: callback,
|
|
100
|
-
});
|
|
101
|
-
configuration.set(this, {count, time});
|
|
102
|
-
state.set(this, {
|
|
103
|
-
active: false,
|
|
104
|
-
finished: false,
|
|
105
|
-
frame: undefined,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
restart() {
|
|
109
|
-
this.stop();
|
|
110
|
-
run(this);
|
|
111
|
-
return this;
|
|
112
|
-
}
|
|
113
|
-
start() {
|
|
114
|
-
if (!this.active) {
|
|
115
|
-
run(this);
|
|
116
|
-
}
|
|
117
|
-
return this;
|
|
118
|
-
}
|
|
119
|
-
stop() {
|
|
120
|
-
const timedCallbacks = callbacks.get(this);
|
|
121
|
-
const timedState = state.get(this);
|
|
122
|
-
timedState.active = false;
|
|
123
|
-
if (timedState.frame === undefined) {
|
|
124
|
-
return this;
|
|
125
|
-
}
|
|
126
|
-
(cancelAnimationFrame ?? clearTimeout)?.(timedState.frame);
|
|
127
|
-
timedCallbacks.after?.(this.finished);
|
|
128
|
-
timedState.frame = undefined;
|
|
129
|
-
return this;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* A timer that waits and runs repeatedly
|
|
134
|
-
*/
|
|
135
|
-
class Repeated extends Timed {}
|
|
136
|
-
/**
|
|
137
|
-
* A timer that waits and runs once
|
|
138
|
-
*/
|
|
139
|
-
class Waited extends Timed {
|
|
140
|
-
/**
|
|
141
|
-
* Creates a new waited timer
|
|
142
|
-
* @param {() => void} callback
|
|
143
|
-
* @param {number} time
|
|
144
|
-
*/
|
|
145
|
-
constructor(callback, time) {
|
|
146
|
-
super(callback, time, 1);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Creates and starts a new repeated timer
|
|
151
|
-
* @param {RepeatedCallback} callback
|
|
152
|
-
* @param {number} time
|
|
153
|
-
* @param {number} count
|
|
154
|
-
* @param {AfterCallback=} afterCallback
|
|
155
|
-
* @return {Repeated}
|
|
156
|
-
*/
|
|
157
|
-
function repeat(callback, time, count, afterCallback) {
|
|
158
|
-
return new Repeated(callback, time, count, afterCallback).start();
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Creates and starts a new waited timer
|
|
162
|
-
* @param {() => void} callback
|
|
163
|
-
* @param {number} time
|
|
164
|
-
* @return {Waited}
|
|
165
|
-
*/
|
|
166
41
|
function wait(callback, time) {
|
|
167
|
-
|
|
42
|
+
return new Timer(callback, time).start();
|
|
168
43
|
}
|
|
44
|
+
var milliseconds = Math.round(16.666666666666668);
|
|
169
45
|
|
|
170
|
-
|
|
46
|
+
class Timer {
|
|
47
|
+
get active() {
|
|
48
|
+
return this._state.active;
|
|
49
|
+
}
|
|
50
|
+
get finished() {
|
|
51
|
+
return this._state.finished;
|
|
52
|
+
}
|
|
53
|
+
constructor(callback, time, count, afterCallback) {
|
|
54
|
+
if (typeof callback !== "function") {
|
|
55
|
+
throw new TypeError("A timer must have a callback function");
|
|
56
|
+
}
|
|
57
|
+
const actualTime = typeof time === "number" ? time : 0;
|
|
58
|
+
if (actualTime < 0) {
|
|
59
|
+
throw new TypeError("A timer must have a non-negative number as its time");
|
|
60
|
+
}
|
|
61
|
+
const actualCount = typeof count === "number" ? count : 1;
|
|
62
|
+
if (actualCount < 1) {
|
|
63
|
+
throw new TypeError("A timer must have a number greater than or equal to 1 as its run count");
|
|
64
|
+
}
|
|
65
|
+
if (actualCount > 1 && afterCallback !== undefined && typeof afterCallback !== "function") {
|
|
66
|
+
throw new TypeError("A repeated timer's after-callback must be a function");
|
|
67
|
+
}
|
|
68
|
+
Object.defineProperty(this, "_configuration", {
|
|
69
|
+
value: {
|
|
70
|
+
callbacks: {
|
|
71
|
+
after: afterCallback,
|
|
72
|
+
default: callback
|
|
73
|
+
},
|
|
74
|
+
count: actualCount,
|
|
75
|
+
time: actualTime
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
Object.defineProperty(this, "_state", {
|
|
79
|
+
value: {
|
|
80
|
+
active: false,
|
|
81
|
+
finished: false
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
restart() {
|
|
86
|
+
this.stop();
|
|
87
|
+
run(this);
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
start() {
|
|
91
|
+
if (!this._state.active) {
|
|
92
|
+
run(this);
|
|
93
|
+
}
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
stop() {
|
|
97
|
+
this._state.active = false;
|
|
98
|
+
if (this._state.frame === undefined) {
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
cancelAnimationFrame(this._state.frame);
|
|
102
|
+
this._configuration.callbacks.after?.(this._state.finished);
|
|
103
|
+
this._state.frame = undefined;
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export {
|
|
108
|
+
wait,
|
|
109
|
+
repeat,
|
|
110
|
+
Timer
|
|
111
|
+
};
|
package/package.json
CHANGED
|
@@ -3,31 +3,17 @@
|
|
|
3
3
|
"name": "Oscar Palmér",
|
|
4
4
|
"url": "https://oscarpalmer.se"
|
|
5
5
|
},
|
|
6
|
-
"browser": "dist/timer.iife.js",
|
|
7
6
|
"description": "A better solution for timeout- and interval-based timers.",
|
|
8
7
|
"devDependencies": {
|
|
9
|
-
"@
|
|
10
|
-
"@
|
|
8
|
+
"@biomejs/biome": "^1.4",
|
|
9
|
+
"@happy-dom/global-registrator": "^12.10",
|
|
11
10
|
"bun": "^1.0",
|
|
12
|
-
"
|
|
13
|
-
"rollup": "^3.29",
|
|
14
|
-
"typescript": "^5.2",
|
|
15
|
-
"xo": "^0.56"
|
|
11
|
+
"typescript": "^5.3"
|
|
16
12
|
},
|
|
17
|
-
"files": [
|
|
18
|
-
|
|
19
|
-
"src",
|
|
20
|
-
"types"
|
|
21
|
-
],
|
|
22
|
-
"jsdelivr": "dist/timer.iife.js",
|
|
23
|
-
"keywords": [
|
|
24
|
-
"timer",
|
|
25
|
-
"setTimeout",
|
|
26
|
-
"setInterval",
|
|
27
|
-
"requestAnimationFrame"
|
|
28
|
-
],
|
|
13
|
+
"files": ["dist", "src", "types"],
|
|
14
|
+
"keywords": ["timer", "setTimeout", "setInterval", "requestAnimationFrame"],
|
|
29
15
|
"license": "MIT",
|
|
30
|
-
"main": "dist/timer.
|
|
16
|
+
"main": "dist/timer.js",
|
|
31
17
|
"module": "dist/timer.js",
|
|
32
18
|
"name": "@oscarpalmer/timer",
|
|
33
19
|
"prettier": {
|
|
@@ -43,26 +29,13 @@
|
|
|
43
29
|
"url": "git+https://github.com/oscarpalmer/timer.git"
|
|
44
30
|
},
|
|
45
31
|
"scripts": {
|
|
46
|
-
"build": "
|
|
47
|
-
"build:esm": "rollup -c",
|
|
48
|
-
"build:iife": "rollup -c --environment ROLLUP_FORMAT:iife",
|
|
32
|
+
"build": "bun build ./src/index.ts --outfile ./dist/timer.js",
|
|
49
33
|
"test": "bun test --coverage",
|
|
50
|
-
"types": "tsc ./
|
|
34
|
+
"types": "bunx tsc -p ./tsconfig.json",
|
|
51
35
|
"watch": "rollup -c -w",
|
|
52
36
|
"xo": "xo ./src/*.ts --env browser"
|
|
53
37
|
},
|
|
54
38
|
"type": "module",
|
|
55
39
|
"types": "src/index.d.ts",
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
"xo": {
|
|
59
|
-
"envs": [
|
|
60
|
-
"browser"
|
|
61
|
-
],
|
|
62
|
-
"prettier": true,
|
|
63
|
-
"rules": {
|
|
64
|
-
"import/extensions": "off",
|
|
65
|
-
"import/no-cycle": "off"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
40
|
+
"version": "0.20.0"
|
|
41
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,59 +1,41 @@
|
|
|
1
|
+
export type AfterCallback = (finished: boolean) => void;
|
|
2
|
+
|
|
1
3
|
type Callbacks = {
|
|
2
4
|
after: AfterCallback | undefined;
|
|
3
|
-
default:
|
|
5
|
+
default: IndexedCallback;
|
|
4
6
|
};
|
|
5
7
|
|
|
6
8
|
type Configuration = {
|
|
9
|
+
callbacks: Callbacks;
|
|
7
10
|
count: number;
|
|
8
11
|
time: number;
|
|
9
12
|
};
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
* @param {boolean} finished Did the timer finish?
|
|
13
|
-
*/
|
|
14
|
-
export type AfterCallback = (finished: boolean) => void;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @param {number} index The index of the current iteration
|
|
18
|
-
*/
|
|
19
|
-
export type RepeatedCallback = (index: number) => void;
|
|
14
|
+
type IndexedCallback = (index: number) => void;
|
|
20
15
|
|
|
21
16
|
type State = {
|
|
22
17
|
active: boolean;
|
|
23
18
|
finished: boolean;
|
|
24
|
-
frame?:
|
|
19
|
+
frame?: number;
|
|
25
20
|
};
|
|
26
21
|
|
|
27
|
-
const callbacks = new WeakMap<Timed<never, never>, Callbacks>();
|
|
28
|
-
const configuration = new WeakMap<Timed<never, never>, Configuration>();
|
|
29
|
-
const state = new WeakMap<Timed<never, never>, State>();
|
|
30
|
-
|
|
31
22
|
const milliseconds = Math.round(1000 / 60);
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return setTimeout?.(() => {
|
|
37
|
-
callback(Date.now());
|
|
38
|
-
}, milliseconds);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
function run(timed: Timed<never, never>): void {
|
|
42
|
-
const timedConfiguration = configuration.get(timed)!;
|
|
43
|
-
const timedCallbacks = callbacks.get(timed)!;
|
|
44
|
-
const timedState = state.get(timed)!;
|
|
24
|
+
function run(timer: Timer): void {
|
|
25
|
+
// @ts-expect-error Keep private status, but allow access
|
|
26
|
+
const {_configuration, _state} = timer;
|
|
45
27
|
|
|
46
|
-
|
|
47
|
-
|
|
28
|
+
_state.active = true;
|
|
29
|
+
_state.finished = false;
|
|
48
30
|
|
|
49
|
-
const isRepeated =
|
|
31
|
+
const isRepeated = _configuration.count > 1;
|
|
50
32
|
|
|
51
33
|
let index = 0;
|
|
52
34
|
|
|
53
35
|
let start;
|
|
54
36
|
|
|
55
|
-
function step(timestamp: DOMHighResTimeStamp) {
|
|
56
|
-
if (!
|
|
37
|
+
function step(timestamp: DOMHighResTimeStamp): void {
|
|
38
|
+
if (!_state.active) {
|
|
57
39
|
return;
|
|
58
40
|
}
|
|
59
41
|
|
|
@@ -65,39 +47,42 @@ function run(timed: Timed<never, never>): void {
|
|
|
65
47
|
const elapsedMaximum = elapsed + milliseconds;
|
|
66
48
|
|
|
67
49
|
if (
|
|
68
|
-
elapsedMinimum <
|
|
69
|
-
|
|
50
|
+
elapsedMinimum < _configuration.time &&
|
|
51
|
+
_configuration.time < elapsedMaximum
|
|
70
52
|
) {
|
|
71
|
-
if (
|
|
72
|
-
|
|
53
|
+
if (_state.active) {
|
|
54
|
+
_configuration.callbacks.default(index);
|
|
73
55
|
}
|
|
74
56
|
|
|
75
57
|
index += 1;
|
|
76
58
|
|
|
77
|
-
if (isRepeated && index <
|
|
59
|
+
if (isRepeated && index < _configuration.count) {
|
|
78
60
|
start = undefined;
|
|
79
61
|
} else {
|
|
80
|
-
|
|
62
|
+
_state.finished = true;
|
|
81
63
|
|
|
82
|
-
|
|
64
|
+
timer.stop();
|
|
83
65
|
|
|
84
66
|
return;
|
|
85
67
|
}
|
|
86
68
|
}
|
|
87
69
|
|
|
88
|
-
|
|
70
|
+
_state.frame = requestAnimationFrame(step);
|
|
89
71
|
}
|
|
90
72
|
|
|
91
|
-
|
|
73
|
+
_state.frame = requestAnimationFrame(step);
|
|
92
74
|
}
|
|
93
75
|
|
|
94
|
-
class
|
|
76
|
+
export class Timer {
|
|
77
|
+
private declare readonly _configuration: Configuration;
|
|
78
|
+
private declare readonly _state: State;
|
|
79
|
+
|
|
95
80
|
get active(): boolean {
|
|
96
|
-
return
|
|
81
|
+
return this._state.active;
|
|
97
82
|
}
|
|
98
83
|
|
|
99
84
|
get finished(): boolean {
|
|
100
|
-
return
|
|
85
|
+
return this._state.finished;
|
|
101
86
|
}
|
|
102
87
|
|
|
103
88
|
/**
|
|
@@ -107,33 +92,33 @@ class Timed<Type, Callback> {
|
|
|
107
92
|
* @param {AfterCallback=} afterCallback
|
|
108
93
|
*/
|
|
109
94
|
constructor(
|
|
110
|
-
callback:
|
|
111
|
-
time
|
|
112
|
-
count
|
|
95
|
+
callback: IndexedCallback,
|
|
96
|
+
time?: number,
|
|
97
|
+
count?: number,
|
|
113
98
|
afterCallback?: AfterCallback,
|
|
114
99
|
) {
|
|
115
|
-
const isRepeated = this instanceof Repeated;
|
|
116
|
-
|
|
117
|
-
const type = isRepeated ? 'repeated' : 'waited';
|
|
118
|
-
|
|
119
100
|
if (typeof callback !== 'function') {
|
|
120
|
-
throw new TypeError(
|
|
101
|
+
throw new TypeError('A timer must have a callback function');
|
|
121
102
|
}
|
|
122
103
|
|
|
123
|
-
|
|
104
|
+
const actualTime = typeof time === 'number' ? time : 0;
|
|
105
|
+
|
|
106
|
+
if (actualTime < 0) {
|
|
124
107
|
throw new TypeError(
|
|
125
|
-
|
|
108
|
+
'A timer must have a non-negative number as its time',
|
|
126
109
|
);
|
|
127
110
|
}
|
|
128
111
|
|
|
129
|
-
|
|
112
|
+
const actualCount = typeof count === 'number' ? count : 1;
|
|
113
|
+
|
|
114
|
+
if (actualCount < 1) {
|
|
130
115
|
throw new TypeError(
|
|
131
|
-
'A
|
|
116
|
+
'A timer must have a number greater than or equal to 1 as its run count',
|
|
132
117
|
);
|
|
133
118
|
}
|
|
134
119
|
|
|
135
120
|
if (
|
|
136
|
-
|
|
121
|
+
actualCount > 1 &&
|
|
137
122
|
afterCallback !== undefined &&
|
|
138
123
|
typeof afterCallback !== 'function'
|
|
139
124
|
) {
|
|
@@ -142,98 +127,103 @@ class Timed<Type, Callback> {
|
|
|
142
127
|
);
|
|
143
128
|
}
|
|
144
129
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
130
|
+
Object.defineProperty(this, '_configuration', {
|
|
131
|
+
value: {
|
|
132
|
+
callbacks: {
|
|
133
|
+
after: afterCallback,
|
|
134
|
+
default: callback,
|
|
135
|
+
},
|
|
136
|
+
count: actualCount,
|
|
137
|
+
time: actualTime,
|
|
138
|
+
},
|
|
148
139
|
});
|
|
149
140
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
frame: undefined,
|
|
141
|
+
Object.defineProperty(this, '_state', {
|
|
142
|
+
value: {
|
|
143
|
+
active: false,
|
|
144
|
+
finished: false,
|
|
145
|
+
},
|
|
156
146
|
});
|
|
157
147
|
}
|
|
158
148
|
|
|
159
|
-
restart():
|
|
149
|
+
restart(): Timer {
|
|
160
150
|
this.stop();
|
|
161
151
|
|
|
162
|
-
run(this
|
|
152
|
+
run(this);
|
|
163
153
|
|
|
164
|
-
return this
|
|
154
|
+
return this;
|
|
165
155
|
}
|
|
166
156
|
|
|
167
|
-
start():
|
|
168
|
-
if (!this.active) {
|
|
169
|
-
run(this
|
|
157
|
+
start(): Timer {
|
|
158
|
+
if (!this._state.active) {
|
|
159
|
+
run(this);
|
|
170
160
|
}
|
|
171
161
|
|
|
172
|
-
return this
|
|
162
|
+
return this;
|
|
173
163
|
}
|
|
174
164
|
|
|
175
|
-
stop():
|
|
176
|
-
|
|
177
|
-
const timedState = state.get(this as never)!;
|
|
178
|
-
|
|
179
|
-
timedState.active = false;
|
|
165
|
+
stop(): Timer {
|
|
166
|
+
this._state.active = false;
|
|
180
167
|
|
|
181
|
-
if (
|
|
182
|
-
return this
|
|
168
|
+
if (this._state.frame === undefined) {
|
|
169
|
+
return this;
|
|
183
170
|
}
|
|
184
171
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
timedCallbacks.after?.(this.finished);
|
|
188
|
-
|
|
189
|
-
timedState.frame = undefined;
|
|
172
|
+
cancelAnimationFrame(this._state.frame);
|
|
190
173
|
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
}
|
|
174
|
+
this._configuration.callbacks.after?.(this._state.finished);
|
|
194
175
|
|
|
195
|
-
|
|
196
|
-
* A timer that waits and runs repeatedly
|
|
197
|
-
*/
|
|
198
|
-
export class Repeated extends Timed<Repeated, RepeatedCallback> {}
|
|
176
|
+
this._state.frame = undefined;
|
|
199
177
|
|
|
200
|
-
|
|
201
|
-
* A timer that waits and runs once
|
|
202
|
-
*/
|
|
203
|
-
export class Waited extends Timed<Waited, () => void> {
|
|
204
|
-
/**
|
|
205
|
-
* Creates a new waited timer
|
|
206
|
-
* @param {() => void} callback
|
|
207
|
-
* @param {number} time
|
|
208
|
-
*/
|
|
209
|
-
constructor(callback: () => void, time: number) {
|
|
210
|
-
super(callback, time, 1);
|
|
178
|
+
return this;
|
|
211
179
|
}
|
|
212
180
|
}
|
|
213
181
|
|
|
214
182
|
/**
|
|
215
183
|
* Creates and starts a new repeated timer
|
|
216
|
-
* @param {RepeatedCallback} callback
|
|
217
|
-
* @param {number} time
|
|
218
|
-
* @param {number} count
|
|
219
|
-
* @param {AfterCallback=} afterCallback
|
|
220
|
-
* @return {Repeated}
|
|
221
184
|
*/
|
|
185
|
+
export function repeat(callback: IndexedCallback, count: number): Timer;
|
|
222
186
|
export function repeat(
|
|
223
|
-
callback:
|
|
187
|
+
callback: IndexedCallback,
|
|
188
|
+
count: number,
|
|
189
|
+
afterCallback: AfterCallback,
|
|
190
|
+
): Timer;
|
|
191
|
+
export function repeat(
|
|
192
|
+
callback: IndexedCallback,
|
|
193
|
+
count: number,
|
|
224
194
|
time: number,
|
|
195
|
+
): Timer;
|
|
196
|
+
export function repeat(
|
|
197
|
+
callback: IndexedCallback,
|
|
225
198
|
count: number,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
199
|
+
time: number,
|
|
200
|
+
afterCallback: AfterCallback,
|
|
201
|
+
): Timer;
|
|
202
|
+
export function repeat(
|
|
203
|
+
callback: IndexedCallback,
|
|
204
|
+
count: number,
|
|
205
|
+
afterOrTime?: number | AfterCallback,
|
|
206
|
+
after?: AfterCallback,
|
|
207
|
+
): Timer {
|
|
208
|
+
if (typeof count !== 'number' || count < 2) {
|
|
209
|
+
throw new TypeError(
|
|
210
|
+
'A repeated timer must have a number greater than or equal to 2 as its run count',
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const afterOrTimeIsFunction = typeof afterOrTime === 'function';
|
|
215
|
+
|
|
216
|
+
return new Timer(
|
|
217
|
+
callback,
|
|
218
|
+
afterOrTimeIsFunction ? 0 : afterOrTime,
|
|
219
|
+
count,
|
|
220
|
+
afterOrTimeIsFunction ? afterOrTime : after,
|
|
221
|
+
).start();
|
|
229
222
|
}
|
|
230
223
|
|
|
231
224
|
/**
|
|
232
225
|
* Creates and starts a new waited timer
|
|
233
|
-
* @param {() => void} callback
|
|
234
|
-
* @param {number} time
|
|
235
|
-
* @return {Waited}
|
|
236
226
|
*/
|
|
237
|
-
export function wait(callback:
|
|
238
|
-
return new
|
|
227
|
+
export function wait(callback: IndexedCallback, time?: number): Timer {
|
|
228
|
+
return new Timer(callback, time).start();
|
|
239
229
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -1,72 +1,30 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {boolean} finished Did the timer finish?
|
|
3
|
-
*/
|
|
4
1
|
export type AfterCallback = (finished: boolean) => void;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
constructor(
|
|
23
|
-
callback: () => void | ((index: number) => void),
|
|
24
|
-
time: number,
|
|
25
|
-
count: number,
|
|
26
|
-
afterCallback?: AfterCallback,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
restart(): Type;
|
|
30
|
-
start(): Type;
|
|
31
|
-
stop(): Type;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* A timer that waits and runs repeatedly
|
|
36
|
-
*/
|
|
37
|
-
export declare class Repeated extends Timed<Repeated, RepeatedCallback> {}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* A timer that waits and runs once
|
|
41
|
-
*/
|
|
42
|
-
export declare class Waited extends Timed<Waited, () => void> {
|
|
43
|
-
/**
|
|
44
|
-
* Creates a new waited timer
|
|
45
|
-
* @param {() => void} callback
|
|
46
|
-
* @param {number} time
|
|
47
|
-
*/
|
|
48
|
-
constructor(callback: () => void, time: number);
|
|
2
|
+
type IndexedCallback = (index: number) => void;
|
|
3
|
+
export declare class Timer {
|
|
4
|
+
private readonly _configuration;
|
|
5
|
+
private readonly _state;
|
|
6
|
+
get active(): boolean;
|
|
7
|
+
get finished(): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* @param {Callback} callback
|
|
10
|
+
* @param {number} time
|
|
11
|
+
* @param {number} count
|
|
12
|
+
* @param {AfterCallback=} afterCallback
|
|
13
|
+
*/
|
|
14
|
+
constructor(callback: IndexedCallback, time?: number, count?: number, afterCallback?: AfterCallback);
|
|
15
|
+
restart(): Timer;
|
|
16
|
+
start(): Timer;
|
|
17
|
+
stop(): Timer;
|
|
49
18
|
}
|
|
50
|
-
|
|
51
19
|
/**
|
|
52
20
|
* Creates and starts a new repeated timer
|
|
53
|
-
* @param {RepeatedCallback} callback
|
|
54
|
-
* @param {number} time
|
|
55
|
-
* @param {number} count
|
|
56
|
-
* @param {AfterCallback=} afterCallback
|
|
57
|
-
* @return {Repeated}
|
|
58
21
|
*/
|
|
59
|
-
export declare function repeat(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
afterCallback?: AfterCallback,
|
|
64
|
-
): Repeated;
|
|
65
|
-
|
|
22
|
+
export declare function repeat(callback: IndexedCallback, count: number): Timer;
|
|
23
|
+
export declare function repeat(callback: IndexedCallback, count: number, afterCallback: AfterCallback): Timer;
|
|
24
|
+
export declare function repeat(callback: IndexedCallback, count: number, time: number): Timer;
|
|
25
|
+
export declare function repeat(callback: IndexedCallback, count: number, time: number, afterCallback: AfterCallback): Timer;
|
|
66
26
|
/**
|
|
67
27
|
* Creates and starts a new waited timer
|
|
68
|
-
* @param {() => void} callback
|
|
69
|
-
* @param {number} time
|
|
70
|
-
* @return {Waited}
|
|
71
28
|
*/
|
|
72
|
-
export declare function wait(callback:
|
|
29
|
+
export declare function wait(callback: IndexedCallback, time?: number): Timer;
|
|
30
|
+
export {};
|
package/dist/timer.iife.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
var Timer = (function (exports) {
|
|
2
|
-
('use strict');
|
|
3
|
-
|
|
4
|
-
const callbacks = new WeakMap();
|
|
5
|
-
const configuration = new WeakMap();
|
|
6
|
-
const state = new WeakMap();
|
|
7
|
-
const milliseconds = Math.round(1000 / 60);
|
|
8
|
-
const request =
|
|
9
|
-
requestAnimationFrame ??
|
|
10
|
-
function (callback) {
|
|
11
|
-
return setTimeout?.(() => {
|
|
12
|
-
callback(Date.now());
|
|
13
|
-
}, milliseconds);
|
|
14
|
-
};
|
|
15
|
-
function run(timed) {
|
|
16
|
-
const timedConfiguration = configuration.get(timed);
|
|
17
|
-
const timedCallbacks = callbacks.get(timed);
|
|
18
|
-
const timedState = state.get(timed);
|
|
19
|
-
timedState.active = true;
|
|
20
|
-
timedState.finished = false;
|
|
21
|
-
const isRepeated = timed instanceof Repeated;
|
|
22
|
-
let index = 0;
|
|
23
|
-
let start;
|
|
24
|
-
function step(timestamp) {
|
|
25
|
-
if (!timedState.active) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
start ?? (start = timestamp);
|
|
29
|
-
const elapsed = timestamp - start;
|
|
30
|
-
const elapsedMinimum = elapsed - milliseconds;
|
|
31
|
-
const elapsedMaximum = elapsed + milliseconds;
|
|
32
|
-
if (
|
|
33
|
-
elapsedMinimum < timedConfiguration.time &&
|
|
34
|
-
timedConfiguration.time < elapsedMaximum
|
|
35
|
-
) {
|
|
36
|
-
if (timedState.active) {
|
|
37
|
-
timedCallbacks.default(isRepeated ? index : undefined);
|
|
38
|
-
}
|
|
39
|
-
index += 1;
|
|
40
|
-
if (isRepeated && index < timedConfiguration.count) {
|
|
41
|
-
start = undefined;
|
|
42
|
-
} else {
|
|
43
|
-
timedState.finished = true;
|
|
44
|
-
timed.stop();
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
timedState.frame = request(step);
|
|
49
|
-
}
|
|
50
|
-
timedState.frame = request(step);
|
|
51
|
-
}
|
|
52
|
-
class Timed {
|
|
53
|
-
get active() {
|
|
54
|
-
return state.get(this)?.active ?? false;
|
|
55
|
-
}
|
|
56
|
-
get finished() {
|
|
57
|
-
return !this.active && (state.get(this)?.finished ?? false);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* @param {Callback} callback
|
|
61
|
-
* @param {number} time
|
|
62
|
-
* @param {number} count
|
|
63
|
-
* @param {AfterCallback=} afterCallback
|
|
64
|
-
*/
|
|
65
|
-
constructor(callback, time, count, afterCallback) {
|
|
66
|
-
const isRepeated = this instanceof Repeated;
|
|
67
|
-
const type = isRepeated ? 'repeated' : 'waited';
|
|
68
|
-
if (typeof callback !== 'function') {
|
|
69
|
-
throw new TypeError(`A ${type} timer must have a callback function`);
|
|
70
|
-
}
|
|
71
|
-
if (typeof time !== 'number' || time < 0) {
|
|
72
|
-
throw new TypeError(
|
|
73
|
-
`A ${type} timer must have a non-negative number as its time`,
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
if (isRepeated && (typeof count !== 'number' || count < 2)) {
|
|
77
|
-
throw new TypeError(
|
|
78
|
-
'A repeated timer must have a number above 1 as its repeat count',
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
if (
|
|
82
|
-
isRepeated &&
|
|
83
|
-
afterCallback !== undefined &&
|
|
84
|
-
typeof afterCallback !== 'function'
|
|
85
|
-
) {
|
|
86
|
-
throw new TypeError(
|
|
87
|
-
"A repeated timer's after-callback must be a function",
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
callbacks.set(this, {
|
|
91
|
-
after: afterCallback,
|
|
92
|
-
default: callback,
|
|
93
|
-
});
|
|
94
|
-
configuration.set(this, {count, time});
|
|
95
|
-
state.set(this, {
|
|
96
|
-
active: false,
|
|
97
|
-
finished: false,
|
|
98
|
-
frame: undefined,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
restart() {
|
|
102
|
-
this.stop();
|
|
103
|
-
run(this);
|
|
104
|
-
return this;
|
|
105
|
-
}
|
|
106
|
-
start() {
|
|
107
|
-
if (!this.active) {
|
|
108
|
-
run(this);
|
|
109
|
-
}
|
|
110
|
-
return this;
|
|
111
|
-
}
|
|
112
|
-
stop() {
|
|
113
|
-
const timedCallbacks = callbacks.get(this);
|
|
114
|
-
const timedState = state.get(this);
|
|
115
|
-
timedState.active = false;
|
|
116
|
-
if (timedState.frame === undefined) {
|
|
117
|
-
return this;
|
|
118
|
-
}
|
|
119
|
-
(cancelAnimationFrame ?? clearTimeout)?.(timedState.frame);
|
|
120
|
-
timedCallbacks.after?.(this.finished);
|
|
121
|
-
timedState.frame = undefined;
|
|
122
|
-
return this;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* A timer that waits and runs repeatedly
|
|
127
|
-
*/
|
|
128
|
-
class Repeated extends Timed {}
|
|
129
|
-
/**
|
|
130
|
-
* A timer that waits and runs once
|
|
131
|
-
*/
|
|
132
|
-
class Waited extends Timed {
|
|
133
|
-
/**
|
|
134
|
-
* Creates a new waited timer
|
|
135
|
-
* @param {() => void} callback
|
|
136
|
-
* @param {number} time
|
|
137
|
-
*/
|
|
138
|
-
constructor(callback, time) {
|
|
139
|
-
super(callback, time, 1);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Creates and starts a new repeated timer
|
|
144
|
-
* @param {RepeatedCallback} callback
|
|
145
|
-
* @param {number} time
|
|
146
|
-
* @param {number} count
|
|
147
|
-
* @param {AfterCallback=} afterCallback
|
|
148
|
-
* @return {Repeated}
|
|
149
|
-
*/
|
|
150
|
-
function repeat(callback, time, count, afterCallback) {
|
|
151
|
-
return new Repeated(callback, time, count, afterCallback).start();
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Creates and starts a new waited timer
|
|
155
|
-
* @param {() => void} callback
|
|
156
|
-
* @param {number} time
|
|
157
|
-
* @return {Waited}
|
|
158
|
-
*/
|
|
159
|
-
function wait(callback, time) {
|
|
160
|
-
return new Waited(callback, time).start();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
exports.Repeated = Repeated;
|
|
164
|
-
exports.Waited = Waited;
|
|
165
|
-
exports.repeat = repeat;
|
|
166
|
-
exports.wait = wait;
|
|
167
|
-
|
|
168
|
-
return exports;
|
|
169
|
-
})({});
|