aptechka 0.1.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/.prettierignore +16 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +4 -0
- package/.vscode/launch.json +11 -0
- package/.vscode/settings.json +18 -0
- package/README.md +0 -0
- package/index.html +32 -0
- package/package.json +272 -0
- package/public/vite.svg +1 -0
- package/src/packages/animation/Animated.ts +189 -0
- package/src/packages/animation/Damped.ts +39 -0
- package/src/packages/animation/Tweened.ts +51 -0
- package/src/packages/animation/index.ts +10 -0
- package/src/packages/attribute/index.ts +59 -0
- package/src/packages/canvas-2d/index.ts +137 -0
- package/src/packages/controls/Controls.ts +15 -0
- package/src/packages/controls/KeyboardControls.ts +63 -0
- package/src/packages/controls/LinearControls.ts +27 -0
- package/src/packages/controls/User.ts +20 -0
- package/src/packages/controls/WheelControls.ts +92 -0
- package/src/packages/controls/index.ts +5 -0
- package/src/packages/css-unit-parser/index.ts +32 -0
- package/src/packages/custom-element/index.ts +19 -0
- package/src/packages/device/Device.ts +113 -0
- package/src/packages/device/Viewport.ts +67 -0
- package/src/packages/device/index.ts +2 -0
- package/src/packages/element-constructor/ElementConstructor.ts +577 -0
- package/src/packages/element-constructor/htmlTags.ts +679 -0
- package/src/packages/element-constructor/index.ts +4 -0
- package/src/packages/element-constructor/specialObjects.ts +8 -0
- package/src/packages/element-constructor/svgTags.ts +588 -0
- package/src/packages/en3/attachments/En3SourceManager.ts +116 -0
- package/src/packages/en3/core/en3.ts +306 -0
- package/src/packages/en3/index.ts +52 -0
- package/src/packages/en3/instances/en3LazyLoader.ts +22 -0
- package/src/packages/en3/libs/MeshoptDecoder.js +138 -0
- package/src/packages/en3/loaders/en3GLTFLoader.ts +54 -0
- package/src/packages/en3/loaders/en3TextureLoader.ts +3 -0
- package/src/packages/en3/objects/En3Clip.ts +53 -0
- package/src/packages/en3/objects/En3ClipHelpers.ts +12 -0
- package/src/packages/en3/objects/En3GLTF.ts +35 -0
- package/src/packages/en3/objects/En3Image.ts +18 -0
- package/src/packages/en3/objects/En3ImageLike.ts +101 -0
- package/src/packages/en3/objects/En3SourceConsumer.ts +5 -0
- package/src/packages/en3/objects/En3Video.ts +88 -0
- package/src/packages/en3/test/En3HTML.ts +55 -0
- package/src/packages/en3/test/En3ModifiedMaterial.ts +221 -0
- package/src/packages/en3/test/En3Raycaster.ts +187 -0
- package/src/packages/en3/utils/coverTexture.ts +29 -0
- package/src/packages/en3/utils/dispose.ts +27 -0
- package/src/packages/en3/utils/traverseMaterials.ts +10 -0
- package/src/packages/en3/utils/traverseMeshes.ts +9 -0
- package/src/packages/image/index.ts +19 -0
- package/src/packages/intersector/index.ts +83 -0
- package/src/packages/ladder/index.ts +112 -0
- package/src/packages/layout-box/index.ts +417 -0
- package/src/packages/loading/index.ts +131 -0
- package/src/packages/measurer/CumulativeOffsetLeft.ts +8 -0
- package/src/packages/measurer/CumulativeOffsetTop.ts +8 -0
- package/src/packages/measurer/Meaurer.ts +38 -0
- package/src/packages/measurer/index.ts +3 -0
- package/src/packages/media/index.ts +38 -0
- package/src/packages/morph/Link.ts +32 -0
- package/src/packages/morph/Morph.ts +246 -0
- package/src/packages/morph/index.ts +10 -0
- package/src/packages/notifier/index.ts +41 -0
- package/src/packages/order/index.ts +14 -0
- package/src/packages/resizer/index.ts +55 -0
- package/src/packages/router/Link.ts +33 -0
- package/src/packages/router/Route.ts +152 -0
- package/src/packages/router/RouteElement.ts +34 -0
- package/src/packages/router/Router.ts +190 -0
- package/src/packages/router/index.ts +13 -0
- package/src/packages/scroll/ScrollElement.ts +618 -0
- package/src/packages/scroll/ScrollUserElement.ts +21 -0
- package/src/packages/scroll/ScrollbarElement.ts +170 -0
- package/src/packages/scroll/index.ts +2 -0
- package/src/packages/scroll-entries/index.ts +74 -0
- package/src/packages/source/SourceClass.ts +77 -0
- package/src/packages/source/SourceElement.ts +177 -0
- package/src/packages/source/SourceManager.ts +61 -0
- package/src/packages/source/SourceSet.ts +52 -0
- package/src/packages/source/index.ts +8 -0
- package/src/packages/store/Composed.ts +33 -0
- package/src/packages/store/Derived.ts +24 -0
- package/src/packages/store/DerivedArray.ts +36 -0
- package/src/packages/store/Resource.ts +38 -0
- package/src/packages/store/Store.ts +144 -0
- package/src/packages/store/StoreRegistry.ts +105 -0
- package/src/packages/store/index.ts +23 -0
- package/src/packages/ticker/index.ts +173 -0
- package/src/packages/utils/array.ts +3 -0
- package/src/packages/utils/attributes.ts +19 -0
- package/src/packages/utils/browser.ts +2 -0
- package/src/packages/utils/canvas.ts +46 -0
- package/src/packages/utils/collisions.ts +12 -0
- package/src/packages/utils/coordinates.ts +40 -0
- package/src/packages/utils/decoding.ts +11 -0
- package/src/packages/utils/dev.ts +5 -0
- package/src/packages/utils/dom.ts +48 -0
- package/src/packages/utils/easings.ts +69 -0
- package/src/packages/utils/file.ts +17 -0
- package/src/packages/utils/function.ts +29 -0
- package/src/packages/utils/index.ts +61 -0
- package/src/packages/utils/layout.ts +22 -0
- package/src/packages/utils/math.ts +74 -0
- package/src/packages/utils/number.ts +26 -0
- package/src/packages/utils/object.ts +108 -0
- package/src/packages/utils/string.ts +49 -0
- package/src/packages/utils/ts-shape.ts +25 -0
- package/src/packages/utils/ts-utility.ts +47 -0
- package/src/packages/video/index.ts +39 -0
- package/src/playground/index.ts +0 -0
- package/tsconfig.json +31 -0
- package/vite.config.ts +65 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Store, StoreOptions } from './Store'
|
|
2
|
+
|
|
3
|
+
export type DerivedArrayCallback<StoreType, DerivedType> = (value: StoreType) => DerivedType
|
|
4
|
+
|
|
5
|
+
export class DerivedArray<DerivedType, StoreType extends Array<any> = Array<any>> extends Store<
|
|
6
|
+
DerivedType[]
|
|
7
|
+
> {
|
|
8
|
+
#unsubscriber: Function
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
store: Store<StoreType>,
|
|
12
|
+
callback: DerivedArrayCallback<StoreType[number], DerivedType>,
|
|
13
|
+
parameters?: StoreOptions<DerivedType[]>
|
|
14
|
+
) {
|
|
15
|
+
super(null!, parameters)
|
|
16
|
+
|
|
17
|
+
this.#unsubscriber = store.subscribe((e) => {
|
|
18
|
+
const res: Array<DerivedType> = []
|
|
19
|
+
|
|
20
|
+
e.current.forEach((v, i) => {
|
|
21
|
+
if (e.current[i] === e.previous?.[i] && this.current[i]) {
|
|
22
|
+
res.push(this.current[i])
|
|
23
|
+
} else {
|
|
24
|
+
res.push(callback(v))
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
this.current = res
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public override close() {
|
|
33
|
+
super.close()
|
|
34
|
+
this.#unsubscriber()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Store, StoreOptions } from './Store'
|
|
2
|
+
|
|
3
|
+
export type ResourceFetcher<StoreType = unknown> = () => Promise<StoreType>
|
|
4
|
+
|
|
5
|
+
export class Resource<StoreType> extends Store<StoreType> {
|
|
6
|
+
#fetcher: ResourceFetcher<StoreType>
|
|
7
|
+
|
|
8
|
+
#isPending: Store<boolean>
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
defaultValue: StoreType,
|
|
12
|
+
fetcher: ResourceFetcher<StoreType>,
|
|
13
|
+
parameters?: StoreOptions<StoreType>
|
|
14
|
+
) {
|
|
15
|
+
super(defaultValue, parameters)
|
|
16
|
+
|
|
17
|
+
this.#isPending = new Store(false)
|
|
18
|
+
|
|
19
|
+
this.#fetcher = fetcher
|
|
20
|
+
this.refetch()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public get isPending() {
|
|
24
|
+
return this.#isPending
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Calls fetcher again and sets isPending to true.
|
|
29
|
+
*/
|
|
30
|
+
public refetch() {
|
|
31
|
+
this.#isPending.current = true
|
|
32
|
+
|
|
33
|
+
this.#fetcher().then((v) => {
|
|
34
|
+
this.#isPending.current = false
|
|
35
|
+
this.current = v
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { storeRegistry } from './StoreRegistry'
|
|
2
|
+
|
|
3
|
+
export interface StoreEntry<StoreType> {
|
|
4
|
+
current: StoreType
|
|
5
|
+
previous: StoreType | undefined
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type StoreCallback<Entry extends StoreEntry<any>> = (entry: Entry) => void
|
|
9
|
+
|
|
10
|
+
export type StoreEqualityCheckCallback<StoreType> = (
|
|
11
|
+
currentValue: StoreType,
|
|
12
|
+
newValue: StoreType
|
|
13
|
+
) => boolean
|
|
14
|
+
export type StoreValidateCallback<StoreType> = (value: StoreType) => StoreType
|
|
15
|
+
|
|
16
|
+
export interface StorePassport {
|
|
17
|
+
name: string
|
|
18
|
+
description?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface StoreOptions<StoreType> {
|
|
22
|
+
equalityCheck?: StoreEqualityCheckCallback<StoreType>
|
|
23
|
+
passport?: StorePassport
|
|
24
|
+
validate?: StoreValidateCallback<StoreType>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class Store<
|
|
28
|
+
StoreType = unknown,
|
|
29
|
+
Entry extends StoreEntry<StoreType> = StoreEntry<StoreType>
|
|
30
|
+
> {
|
|
31
|
+
#passport: StorePassport | undefined
|
|
32
|
+
#initial: StoreType
|
|
33
|
+
#previous: StoreType | undefined
|
|
34
|
+
#current: StoreType
|
|
35
|
+
#equalityCheck: StoreEqualityCheckCallback<StoreType>
|
|
36
|
+
#callbacks = new Set<StoreCallback<Entry>>()
|
|
37
|
+
#validate: StoreValidateCallback<StoreType>
|
|
38
|
+
|
|
39
|
+
constructor(value: StoreType, options?: StoreOptions<StoreType>) {
|
|
40
|
+
this.#passport = options?.passport
|
|
41
|
+
this.#initial = value
|
|
42
|
+
this.#previous = undefined
|
|
43
|
+
|
|
44
|
+
this.#current = value
|
|
45
|
+
|
|
46
|
+
this.#equalityCheck =
|
|
47
|
+
options?.equalityCheck || ((currentValue, newValue) => currentValue === newValue)
|
|
48
|
+
|
|
49
|
+
this.#validate = options?.validate || ((value) => value)
|
|
50
|
+
|
|
51
|
+
if (this.#passport) {
|
|
52
|
+
storeRegistry.updateStore(this)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public get passport() {
|
|
57
|
+
return this.#passport
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public get initial() {
|
|
61
|
+
return this.#initial
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public get previous() {
|
|
65
|
+
return this.#previous
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public get current() {
|
|
69
|
+
return this.#current
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public set current(value: StoreType) {
|
|
73
|
+
if (!this.#equalityCheck(this.#current, value)) {
|
|
74
|
+
this.#previous = this.#current
|
|
75
|
+
this.#current = this.#validate(value)
|
|
76
|
+
this.#notify()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public get subscribers() {
|
|
81
|
+
return this.#callbacks
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public get entry() {
|
|
85
|
+
return {
|
|
86
|
+
current: this.#current,
|
|
87
|
+
previous: this.#previous,
|
|
88
|
+
} as Entry
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public subscribe(callback: StoreCallback<Entry>) {
|
|
92
|
+
if (this.#passport && !this.#callbacks.size) {
|
|
93
|
+
shareStore(this)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.#callbacks.add(callback)
|
|
97
|
+
|
|
98
|
+
callback(this.entry)
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
this.unsubscribe(callback)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public unsubscribe(callback: StoreCallback<Entry>) {
|
|
106
|
+
this.#callbacks.delete(callback)
|
|
107
|
+
|
|
108
|
+
if (this.#passport && !this.#callbacks.size) {
|
|
109
|
+
unshareStore(this)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public reset() {
|
|
114
|
+
this.current = this.initial
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public close() {
|
|
118
|
+
this.#callbacks.clear()
|
|
119
|
+
|
|
120
|
+
if (this.#passport) {
|
|
121
|
+
unshareStore(this)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#notify() {
|
|
126
|
+
for (const callback of this.#callbacks) {
|
|
127
|
+
callback(this.entry)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const activeStores = new Store<Array<Store<any, any>>>([])
|
|
133
|
+
|
|
134
|
+
function shareStore(store: Store<any, any>) {
|
|
135
|
+
if (!activeStores.current.find((s) => s.passport!.name === store.passport!.name)) {
|
|
136
|
+
activeStores.current = [...activeStores.current, store]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function unshareStore(store: Store<any, any>) {
|
|
141
|
+
if (activeStores.current.includes(store)) {
|
|
142
|
+
activeStores.current = activeStores.current.filter((s) => s !== store)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { isBrowser } from '$packages/utils'
|
|
2
|
+
import { Store, activeStores } from './Store'
|
|
3
|
+
|
|
4
|
+
export interface StoreRegistryStateStore {
|
|
5
|
+
name: string
|
|
6
|
+
value: any
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type StoreRegistryState = {
|
|
10
|
+
stores: Array<StoreRegistryStateStore>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class StoreRegistry {
|
|
14
|
+
#loadedState: StoreRegistryState | null = null
|
|
15
|
+
#projectName: string | undefined
|
|
16
|
+
#localStoreRegistryName = ''
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
if (isBrowser) {
|
|
20
|
+
this.#projectName = document.documentElement.getAttribute('data-project') || undefined
|
|
21
|
+
this.#localStoreRegistryName = this.#projectName
|
|
22
|
+
? this.#projectName + '-store-registry'
|
|
23
|
+
: 'store-registry'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public get projectName() {
|
|
28
|
+
return this.#projectName
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public get localStoreRegistryName() {
|
|
32
|
+
return this.#localStoreRegistryName
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public get loadedState() {
|
|
36
|
+
return this.#loadedState
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public saveState() {
|
|
40
|
+
const fullState = this.getState()
|
|
41
|
+
const stringState = JSON.stringify(fullState)
|
|
42
|
+
localStorage.setItem(this.#localStoreRegistryName, stringState)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public loadState(
|
|
46
|
+
state: string | StoreRegistryState | null = localStorage.getItem(this.#localStoreRegistryName)
|
|
47
|
+
) {
|
|
48
|
+
if (state) {
|
|
49
|
+
if (typeof state === 'string') {
|
|
50
|
+
this.#loadedState = JSON.parse(state) as StoreRegistryState
|
|
51
|
+
} else {
|
|
52
|
+
this.#loadedState = state
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
activeStores.current.forEach((store) => {
|
|
56
|
+
this.updateStore(store)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public resetState() {
|
|
62
|
+
activeStores.current.forEach((store) => {
|
|
63
|
+
store.reset()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
this.saveState()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public updateStore(store: Store<any, any>) {
|
|
70
|
+
if (!Array.isArray(this.#loadedState?.stores)) {
|
|
71
|
+
return store
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const passport = store.passport
|
|
75
|
+
|
|
76
|
+
if (passport) {
|
|
77
|
+
const match = this.#loadedState?.stores.find((s) => s.name === passport.name)
|
|
78
|
+
|
|
79
|
+
if (match) {
|
|
80
|
+
store.current = match.value
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return store
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public getState() {
|
|
88
|
+
const state: StoreRegistryState = {
|
|
89
|
+
stores: [],
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
activeStores.current.forEach((store) => {
|
|
93
|
+
if (store.passport) {
|
|
94
|
+
state.stores.push({
|
|
95
|
+
value: store.current as any,
|
|
96
|
+
name: store.passport.name,
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
return state
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const storeRegistry = new StoreRegistry()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export {
|
|
2
|
+
Store,
|
|
3
|
+
activeStores,
|
|
4
|
+
type StoreEntry,
|
|
5
|
+
type StoreCallback,
|
|
6
|
+
type StorePassport,
|
|
7
|
+
type StoreOptions,
|
|
8
|
+
type StoreEqualityCheckCallback,
|
|
9
|
+
} from './Store'
|
|
10
|
+
|
|
11
|
+
export { Derived, type DerivedCallback } from './Derived'
|
|
12
|
+
|
|
13
|
+
export { DerivedArray, type DerivedArrayCallback } from './DerivedArray'
|
|
14
|
+
|
|
15
|
+
export { Resource, type ResourceFetcher } from './Resource'
|
|
16
|
+
|
|
17
|
+
export { Composed, type ComposedCallback } from './Composed'
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
storeRegistry,
|
|
21
|
+
type StoreRegistryState,
|
|
22
|
+
type StoreRegistryStateStore,
|
|
23
|
+
} from './StoreRegistry'
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { intersector } from '$packages/intersector'
|
|
2
|
+
import { ElementOrSelector, isBrowser } from '$packages/utils'
|
|
3
|
+
|
|
4
|
+
export interface TickerCallbackEntry {
|
|
5
|
+
timestamp: number
|
|
6
|
+
elapsed: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type TickerCallback = (entry: TickerCallbackEntry) => void
|
|
10
|
+
|
|
11
|
+
export interface TickerAddOptions {
|
|
12
|
+
maxFPS?: number
|
|
13
|
+
order?: number
|
|
14
|
+
culling?: ElementOrSelector
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class TickerSubscriber {
|
|
18
|
+
#callback: TickerCallback
|
|
19
|
+
#maxFPS: number | undefined
|
|
20
|
+
#order: number
|
|
21
|
+
#lastTimestamp = 0
|
|
22
|
+
#elapsed = 0
|
|
23
|
+
#isVisible = false
|
|
24
|
+
|
|
25
|
+
constructor(callback: TickerCallback, options?: TickerAddOptions) {
|
|
26
|
+
this.#callback = callback
|
|
27
|
+
this.#maxFPS = options?.maxFPS
|
|
28
|
+
this.#order = options?.order || 0
|
|
29
|
+
|
|
30
|
+
if (options?.culling && isBrowser) {
|
|
31
|
+
intersector.subscribe(options.culling, this.#intersectionListener)
|
|
32
|
+
} else {
|
|
33
|
+
this.#isVisible = true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public get callback() {
|
|
38
|
+
return this.#callback
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public get order() {
|
|
42
|
+
return this.#order
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public sync(timestamp: number) {
|
|
46
|
+
this.#lastTimestamp = timestamp - this.#elapsed
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public tick(timestamp: number) {
|
|
50
|
+
this.#elapsed = Math.max(0, timestamp - this.#lastTimestamp)
|
|
51
|
+
|
|
52
|
+
if (this.#maxFPS) {
|
|
53
|
+
if (this.#elapsed >= 1000 / this.#maxFPS) {
|
|
54
|
+
this.#lastTimestamp = timestamp - (this.#elapsed % this.#maxFPS)
|
|
55
|
+
} else {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
this.#lastTimestamp = timestamp
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (this.#isVisible) {
|
|
63
|
+
this.#callback({
|
|
64
|
+
elapsed: this.#elapsed,
|
|
65
|
+
timestamp: timestamp,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public destroy() {
|
|
71
|
+
intersector.unsubscribe(this.#intersectionListener)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#intersectionListener = (entry: IntersectionObserverEntry) => {
|
|
75
|
+
this.#isVisible = entry.isIntersecting
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export class Ticker {
|
|
80
|
+
#idleTime = 0
|
|
81
|
+
#timestamp = 0
|
|
82
|
+
#lastFrameId: number | undefined
|
|
83
|
+
|
|
84
|
+
#isDocumentHidden = false
|
|
85
|
+
|
|
86
|
+
#subscribers: Array<TickerSubscriber> = []
|
|
87
|
+
|
|
88
|
+
constructor() {
|
|
89
|
+
if (isBrowser) {
|
|
90
|
+
document.addEventListener('visibilitychange', this.#documentVisibilityChangeListener)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public subscribe(callback: TickerCallback, options?: TickerAddOptions) {
|
|
95
|
+
if (!isBrowser) {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!this.#subscribers.find((s) => s.callback === callback)) {
|
|
100
|
+
const subscriber = new TickerSubscriber(callback, options)
|
|
101
|
+
subscriber.sync(performance.now())
|
|
102
|
+
this.#subscribers.push(subscriber)
|
|
103
|
+
this.#subscribers = this.#subscribers.sort((a, b) => a.order - b.order)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.#requestAnimationFrame()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public unsubscribe(callback: TickerCallback) {
|
|
110
|
+
if (!isBrowser) {
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const matches = this.#subscribers.filter((subscriber) => subscriber.callback === callback)
|
|
115
|
+
|
|
116
|
+
if (matches.length) {
|
|
117
|
+
matches.forEach((m) => m.destroy())
|
|
118
|
+
|
|
119
|
+
this.#subscribers = this.#subscribers.filter((subscriber) => subscriber.callback !== callback)
|
|
120
|
+
|
|
121
|
+
if (!this.#subscribers.length) {
|
|
122
|
+
this.#cancelAnimationFrame()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public destroy() {
|
|
128
|
+
if (isBrowser) {
|
|
129
|
+
this.#cancelAnimationFrame()
|
|
130
|
+
document.removeEventListener('visibilitychange', this.#documentVisibilityChangeListener)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#requestAnimationFrame() {
|
|
135
|
+
if (this.#subscribers.length && !this.#lastFrameId) {
|
|
136
|
+
this.#lastFrameId = requestAnimationFrame(this.#animationFrameListener)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#cancelAnimationFrame() {
|
|
141
|
+
if (this.#lastFrameId) {
|
|
142
|
+
cancelAnimationFrame(this.#lastFrameId)
|
|
143
|
+
this.#lastFrameId = undefined
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#animationFrameListener = (timestamp: number) => {
|
|
148
|
+
if (this.#isDocumentHidden) {
|
|
149
|
+
this.#isDocumentHidden = false
|
|
150
|
+
this.#idleTime = timestamp - this.#timestamp
|
|
151
|
+
|
|
152
|
+
for (const subscriber of this.#subscribers) {
|
|
153
|
+
subscriber.sync(this.#timestamp)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.#lastFrameId = requestAnimationFrame(this.#animationFrameListener)
|
|
158
|
+
|
|
159
|
+
this.#timestamp = timestamp - this.#idleTime
|
|
160
|
+
|
|
161
|
+
for (const subscriber of this.#subscribers) {
|
|
162
|
+
subscriber.tick(this.#timestamp)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#documentVisibilityChangeListener = () => {
|
|
167
|
+
if (document.hidden) {
|
|
168
|
+
this.#isDocumentHidden = true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const ticker = new Ticker()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function parseAttribute(element: HTMLElement, attributeName: string) {
|
|
2
|
+
return parseAttributeValue(element.getAttribute(attributeName))
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function parseAttributeValue(value: string | null | undefined) {
|
|
6
|
+
if (value == undefined) {
|
|
7
|
+
return null
|
|
8
|
+
} else if (!value) {
|
|
9
|
+
return true
|
|
10
|
+
} else if (!isNaN(Number(value))) {
|
|
11
|
+
return Number(value)
|
|
12
|
+
} else if (value === 'true') {
|
|
13
|
+
return true
|
|
14
|
+
} else if (value === 'false') {
|
|
15
|
+
return false
|
|
16
|
+
} else {
|
|
17
|
+
return value
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export function measureText(context: CanvasRenderingContext2D, text: string) {
|
|
2
|
+
const metrics = context.measureText(text)
|
|
3
|
+
const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent
|
|
4
|
+
const width = metrics.width
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
height,
|
|
8
|
+
width,
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function fixPosition(position: number) {
|
|
13
|
+
return Math.floor(position) + 0.5
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function cover(
|
|
17
|
+
contentWidth: number,
|
|
18
|
+
contentHeight: number,
|
|
19
|
+
containerWidth: number,
|
|
20
|
+
containerHeight: number,
|
|
21
|
+
offsetLeft?: number,
|
|
22
|
+
offsetTop?: number
|
|
23
|
+
) {
|
|
24
|
+
let contentRatio = contentWidth / contentHeight
|
|
25
|
+
let containerRatio = containerWidth / containerHeight
|
|
26
|
+
let resultHeight = 0
|
|
27
|
+
let resultWidth = 0
|
|
28
|
+
|
|
29
|
+
offsetLeft = typeof offsetLeft === 'undefined' ? 0.5 : offsetLeft
|
|
30
|
+
offsetTop = typeof offsetTop === 'undefined' ? 0.5 : offsetTop
|
|
31
|
+
|
|
32
|
+
if (contentRatio > containerRatio) {
|
|
33
|
+
resultHeight = containerHeight
|
|
34
|
+
resultWidth = containerHeight * contentRatio
|
|
35
|
+
} else {
|
|
36
|
+
resultWidth = containerWidth
|
|
37
|
+
resultHeight = containerWidth / contentRatio
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return [
|
|
41
|
+
(containerWidth - resultWidth) * offsetLeft,
|
|
42
|
+
(containerHeight - resultHeight) * offsetTop,
|
|
43
|
+
resultWidth,
|
|
44
|
+
resultHeight,
|
|
45
|
+
] as const
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Circle, Dot2D, Rect2D } from './ts-shape.js'
|
|
2
|
+
|
|
3
|
+
export function dotRectCollision(dot: Dot2D, rect: Rect2D) {
|
|
4
|
+
return (
|
|
5
|
+
dot.x < rect.x + rect.width && dot.x > rect.x && dot.y < rect.y + rect.height && dot.y > rect.y
|
|
6
|
+
)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function dotCircleCollision(dot: Dot2D, circle: Circle) {
|
|
10
|
+
const distance = Math.sqrt((dot.x - circle.x) ** 2 + (dot.y - circle.y) ** 2)
|
|
11
|
+
return distance < circle.radius
|
|
12
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Dot2D, Rect2D } from './ts-shape.js'
|
|
2
|
+
|
|
3
|
+
export function screenToCartesian(
|
|
4
|
+
screenCoordinate: Dot2D,
|
|
5
|
+
container: Pick<Rect2D, 'width' | 'height'>,
|
|
6
|
+
normalize = false,
|
|
7
|
+
) {
|
|
8
|
+
let x = screenCoordinate.x - container.width / 2
|
|
9
|
+
let y = container.height / 2 - screenCoordinate.y
|
|
10
|
+
|
|
11
|
+
if (normalize) {
|
|
12
|
+
x = x / (container.width / 2)
|
|
13
|
+
y = y / (container.height / 2)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return { x, y }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function normalize(coordinate: Dot2D, size: Dot2D) {
|
|
20
|
+
const x = coordinate.x / size.x
|
|
21
|
+
const y = coordinate.y / size.y
|
|
22
|
+
|
|
23
|
+
return { x, y }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getPointerPosition(event: MouseEvent | PointerEvent | Dot2D, rect?: Rect2D) {
|
|
27
|
+
rect = rect
|
|
28
|
+
? rect
|
|
29
|
+
: {
|
|
30
|
+
x: 0,
|
|
31
|
+
y: 0,
|
|
32
|
+
width: document.documentElement.offsetWidth,
|
|
33
|
+
height: innerHeight,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
x: ((event.x - rect.x) / rect.width) * rect.width,
|
|
38
|
+
y: ((event.y - rect.y) / rect.height) * rect.height,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function encode(string: string) {
|
|
2
|
+
const decodedStr = window.atob(string)
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
return decodeURIComponent(window.escape(decodedStr))
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function decode(string: string) {
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
const encodedStr = window.unescape(encodeURIComponent(string))
|
|
10
|
+
return window.btoa(encodedStr)
|
|
11
|
+
}
|