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.
Files changed (115) hide show
  1. package/.prettierignore +16 -0
  2. package/.prettierrc +9 -0
  3. package/.vscode/extensions.json +4 -0
  4. package/.vscode/launch.json +11 -0
  5. package/.vscode/settings.json +18 -0
  6. package/README.md +0 -0
  7. package/index.html +32 -0
  8. package/package.json +272 -0
  9. package/public/vite.svg +1 -0
  10. package/src/packages/animation/Animated.ts +189 -0
  11. package/src/packages/animation/Damped.ts +39 -0
  12. package/src/packages/animation/Tweened.ts +51 -0
  13. package/src/packages/animation/index.ts +10 -0
  14. package/src/packages/attribute/index.ts +59 -0
  15. package/src/packages/canvas-2d/index.ts +137 -0
  16. package/src/packages/controls/Controls.ts +15 -0
  17. package/src/packages/controls/KeyboardControls.ts +63 -0
  18. package/src/packages/controls/LinearControls.ts +27 -0
  19. package/src/packages/controls/User.ts +20 -0
  20. package/src/packages/controls/WheelControls.ts +92 -0
  21. package/src/packages/controls/index.ts +5 -0
  22. package/src/packages/css-unit-parser/index.ts +32 -0
  23. package/src/packages/custom-element/index.ts +19 -0
  24. package/src/packages/device/Device.ts +113 -0
  25. package/src/packages/device/Viewport.ts +67 -0
  26. package/src/packages/device/index.ts +2 -0
  27. package/src/packages/element-constructor/ElementConstructor.ts +577 -0
  28. package/src/packages/element-constructor/htmlTags.ts +679 -0
  29. package/src/packages/element-constructor/index.ts +4 -0
  30. package/src/packages/element-constructor/specialObjects.ts +8 -0
  31. package/src/packages/element-constructor/svgTags.ts +588 -0
  32. package/src/packages/en3/attachments/En3SourceManager.ts +116 -0
  33. package/src/packages/en3/core/en3.ts +306 -0
  34. package/src/packages/en3/index.ts +52 -0
  35. package/src/packages/en3/instances/en3LazyLoader.ts +22 -0
  36. package/src/packages/en3/libs/MeshoptDecoder.js +138 -0
  37. package/src/packages/en3/loaders/en3GLTFLoader.ts +54 -0
  38. package/src/packages/en3/loaders/en3TextureLoader.ts +3 -0
  39. package/src/packages/en3/objects/En3Clip.ts +53 -0
  40. package/src/packages/en3/objects/En3ClipHelpers.ts +12 -0
  41. package/src/packages/en3/objects/En3GLTF.ts +35 -0
  42. package/src/packages/en3/objects/En3Image.ts +18 -0
  43. package/src/packages/en3/objects/En3ImageLike.ts +101 -0
  44. package/src/packages/en3/objects/En3SourceConsumer.ts +5 -0
  45. package/src/packages/en3/objects/En3Video.ts +88 -0
  46. package/src/packages/en3/test/En3HTML.ts +55 -0
  47. package/src/packages/en3/test/En3ModifiedMaterial.ts +221 -0
  48. package/src/packages/en3/test/En3Raycaster.ts +187 -0
  49. package/src/packages/en3/utils/coverTexture.ts +29 -0
  50. package/src/packages/en3/utils/dispose.ts +27 -0
  51. package/src/packages/en3/utils/traverseMaterials.ts +10 -0
  52. package/src/packages/en3/utils/traverseMeshes.ts +9 -0
  53. package/src/packages/image/index.ts +19 -0
  54. package/src/packages/intersector/index.ts +83 -0
  55. package/src/packages/ladder/index.ts +112 -0
  56. package/src/packages/layout-box/index.ts +417 -0
  57. package/src/packages/loading/index.ts +131 -0
  58. package/src/packages/measurer/CumulativeOffsetLeft.ts +8 -0
  59. package/src/packages/measurer/CumulativeOffsetTop.ts +8 -0
  60. package/src/packages/measurer/Meaurer.ts +38 -0
  61. package/src/packages/measurer/index.ts +3 -0
  62. package/src/packages/media/index.ts +38 -0
  63. package/src/packages/morph/Link.ts +32 -0
  64. package/src/packages/morph/Morph.ts +246 -0
  65. package/src/packages/morph/index.ts +10 -0
  66. package/src/packages/notifier/index.ts +41 -0
  67. package/src/packages/order/index.ts +14 -0
  68. package/src/packages/resizer/index.ts +55 -0
  69. package/src/packages/router/Link.ts +33 -0
  70. package/src/packages/router/Route.ts +152 -0
  71. package/src/packages/router/RouteElement.ts +34 -0
  72. package/src/packages/router/Router.ts +190 -0
  73. package/src/packages/router/index.ts +13 -0
  74. package/src/packages/scroll/ScrollElement.ts +618 -0
  75. package/src/packages/scroll/ScrollUserElement.ts +21 -0
  76. package/src/packages/scroll/ScrollbarElement.ts +170 -0
  77. package/src/packages/scroll/index.ts +2 -0
  78. package/src/packages/scroll-entries/index.ts +74 -0
  79. package/src/packages/source/SourceClass.ts +77 -0
  80. package/src/packages/source/SourceElement.ts +177 -0
  81. package/src/packages/source/SourceManager.ts +61 -0
  82. package/src/packages/source/SourceSet.ts +52 -0
  83. package/src/packages/source/index.ts +8 -0
  84. package/src/packages/store/Composed.ts +33 -0
  85. package/src/packages/store/Derived.ts +24 -0
  86. package/src/packages/store/DerivedArray.ts +36 -0
  87. package/src/packages/store/Resource.ts +38 -0
  88. package/src/packages/store/Store.ts +144 -0
  89. package/src/packages/store/StoreRegistry.ts +105 -0
  90. package/src/packages/store/index.ts +23 -0
  91. package/src/packages/ticker/index.ts +173 -0
  92. package/src/packages/utils/array.ts +3 -0
  93. package/src/packages/utils/attributes.ts +19 -0
  94. package/src/packages/utils/browser.ts +2 -0
  95. package/src/packages/utils/canvas.ts +46 -0
  96. package/src/packages/utils/collisions.ts +12 -0
  97. package/src/packages/utils/coordinates.ts +40 -0
  98. package/src/packages/utils/decoding.ts +11 -0
  99. package/src/packages/utils/dev.ts +5 -0
  100. package/src/packages/utils/dom.ts +48 -0
  101. package/src/packages/utils/easings.ts +69 -0
  102. package/src/packages/utils/file.ts +17 -0
  103. package/src/packages/utils/function.ts +29 -0
  104. package/src/packages/utils/index.ts +61 -0
  105. package/src/packages/utils/layout.ts +22 -0
  106. package/src/packages/utils/math.ts +74 -0
  107. package/src/packages/utils/number.ts +26 -0
  108. package/src/packages/utils/object.ts +108 -0
  109. package/src/packages/utils/string.ts +49 -0
  110. package/src/packages/utils/ts-shape.ts +25 -0
  111. package/src/packages/utils/ts-utility.ts +47 -0
  112. package/src/packages/video/index.ts +39 -0
  113. package/src/playground/index.ts +0 -0
  114. package/tsconfig.json +31 -0
  115. 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,3 @@
1
+ export function insert(arr: Array<any>, index: number, ...newItems: any[]) {
2
+ return [...arr.slice(0, index), ...newItems, ...arr.slice(index)]
3
+ }
@@ -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,2 @@
1
+ export const isBrowser: boolean =
2
+ typeof window !== 'undefined' && typeof window.document !== 'undefined'
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ export function resizeInterval({ ms = 200 } = {}) {
2
+ setInterval(() => {
3
+ window.dispatchEvent(new Event('resize'))
4
+ }, ms)
5
+ }