@vyr/engine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/package.json +19 -0
  2. package/src/ArrayUtils.ts +65 -0
  3. package/src/AsyncTask.ts +72 -0
  4. package/src/Category.ts +119 -0
  5. package/src/Color.ts +111 -0
  6. package/src/Engine.ts +101 -0
  7. package/src/Generate.ts +40 -0
  8. package/src/InputSystem.ts +108 -0
  9. package/src/Listener.ts +59 -0
  10. package/src/ObjectPool.ts +84 -0
  11. package/src/ObjectUtils.ts +49 -0
  12. package/src/Scriptable.ts +27 -0
  13. package/src/Serialization.ts +49 -0
  14. package/src/Traverser.ts +39 -0
  15. package/src/actor/Actor.ts +28 -0
  16. package/src/actor/AnimationUnitActor.ts +289 -0
  17. package/src/actor/DivActor.ts +70 -0
  18. package/src/actor/FragmentActor.ts +56 -0
  19. package/src/actor/HTMActor.ts +166 -0
  20. package/src/actor/HTMServiceActor.ts +57 -0
  21. package/src/actor/HTMTransformControllerActor.ts +404 -0
  22. package/src/actor/StyleActor.ts +96 -0
  23. package/src/actor/index.ts +8 -0
  24. package/src/asset/Asset.ts +271 -0
  25. package/src/asset/AssetGraph.ts +246 -0
  26. package/src/asset/index.ts +2 -0
  27. package/src/descriptor/AnimationUnitDescriptor.ts +65 -0
  28. package/src/descriptor/CameraDescriptor.ts +12 -0
  29. package/src/descriptor/ControllerDescriptor.ts +16 -0
  30. package/src/descriptor/DatasetDescriptor.ts +92 -0
  31. package/src/descriptor/Descriptor.ts +415 -0
  32. package/src/descriptor/DivDescriptor.ts +18 -0
  33. package/src/descriptor/DynamicDescriptor.ts +27 -0
  34. package/src/descriptor/HTMLDescriptor.ts +87 -0
  35. package/src/descriptor/HTMLServiceDescriptor.ts +19 -0
  36. package/src/descriptor/HTMLTransformControllerDescriptor.ts +34 -0
  37. package/src/descriptor/NodeDescriptor.ts +32 -0
  38. package/src/descriptor/PrefabDescriptor.ts +53 -0
  39. package/src/descriptor/PrefabInstanceDescriptor.ts +32 -0
  40. package/src/descriptor/RoutineDescriptor.ts +54 -0
  41. package/src/descriptor/ServiceDescriptor.ts +32 -0
  42. package/src/descriptor/ServiceSchedulerDescriptor.ts +32 -0
  43. package/src/descriptor/StyleDescriptor.ts +213 -0
  44. package/src/descriptor/index.ts +17 -0
  45. package/src/graphics/Collection.ts +25 -0
  46. package/src/graphics/Compilation.ts +82 -0
  47. package/src/graphics/Graphics.ts +475 -0
  48. package/src/graphics/Observer.ts +36 -0
  49. package/src/graphics/Unit.ts +83 -0
  50. package/src/graphics/VariableProxy.ts +92 -0
  51. package/src/graphics/index.ts +5 -0
  52. package/src/index.ts +26 -0
  53. package/src/interpreter/AnimationUnitInterpreter.ts +53 -0
  54. package/src/interpreter/DatasetInterpreter.ts +11 -0
  55. package/src/interpreter/DivInterpreter.ts +44 -0
  56. package/src/interpreter/DynamicInterpreter.ts +207 -0
  57. package/src/interpreter/FragmentInterpreter.ts +34 -0
  58. package/src/interpreter/HTMLServiceInterpreter.ts +47 -0
  59. package/src/interpreter/HTMLTransformControllerInterpreter.ts +40 -0
  60. package/src/interpreter/Interpreter.ts +69 -0
  61. package/src/interpreter/PrefaInterpreter.ts +11 -0
  62. package/src/interpreter/PrefabInstanceInterpreter.ts +12 -0
  63. package/src/interpreter/RoutineInterpreter.ts +88 -0
  64. package/src/interpreter/ServiceInterpreter.ts +24 -0
  65. package/src/interpreter/ServiceSchedulerInterpreter.ts +42 -0
  66. package/src/interpreter/StyleInterpreter.ts +66 -0
  67. package/src/interpreter/index.ts +14 -0
  68. package/src/locale/Language.ts +10 -0
  69. package/src/locale/LanguageProvider.ts +48 -0
  70. package/src/locale/index.ts +2 -0
  71. package/src/math/Euler.ts +303 -0
  72. package/src/math/Matrix4.ts +1123 -0
  73. package/src/math/Quaternion.ts +737 -0
  74. package/src/math/Vector2.ts +680 -0
  75. package/src/math/Vector3.ts +1062 -0
  76. package/src/math/index.ts +5 -0
  77. package/src/math/utils.ts +17 -0
  78. package/src/preset/execute/dataset/index.ts +1 -0
  79. package/src/preset/execute/dataset/update.ts +52 -0
  80. package/src/preset/execute/graphics/index.ts +1 -0
  81. package/src/preset/execute/graphics/invoke.ts +49 -0
  82. package/src/preset/execute/index.ts +4 -0
  83. package/src/preset/execute/net/index.ts +1 -0
  84. package/src/preset/execute/net/request.ts +103 -0
  85. package/src/preset/execute/scheduler/index.ts +1 -0
  86. package/src/preset/execute/scheduler/switch.ts +46 -0
  87. package/src/preset/index.ts +7 -0
  88. package/src/preset/routine/graphics/index.ts +1 -0
  89. package/src/preset/routine/graphics/invoke.ts +27 -0
  90. package/src/preset/routine/index.ts +2 -0
  91. package/src/preset/routine/scheduler/index.ts +1 -0
  92. package/src/preset/routine/scheduler/switch.ts +27 -0
  93. package/src/setup/index.ts +17 -0
  94. package/src/utils/AssetProvider.ts +72 -0
  95. package/src/utils/index.ts +1 -0
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@vyr/engine",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "./src/index.ts",
6
+ "author": "",
7
+ "license": "MIT",
8
+ "dependencies": {
9
+ "@vyr/locale": "0.0.1",
10
+ "tinycolor2": "1.6.0"
11
+ },
12
+ "devDependencies": {
13
+ "@types/tinycolor2": "1.4.6"
14
+ },
15
+ "files": [
16
+ "package.json",
17
+ "src/"
18
+ ]
19
+ }
@@ -0,0 +1,65 @@
1
+ import { ObjectUtils } from "./ObjectUtils"
2
+
3
+ class ArrayUtils {
4
+
5
+ static insert<T extends any = any>(arr: T[], item: T) {
6
+ const i = arr.indexOf(item)
7
+ if (i === -1) arr.push(item)
8
+ }
9
+
10
+ static remove<T extends any = any>(arr: T[], item: T) {
11
+ const i = arr.indexOf(item)
12
+ if (i > -1) arr.splice(i, 1)
13
+ }
14
+
15
+ static auto<T extends any = any>(arr: T[], item: T) {
16
+ const i = arr.indexOf(item)
17
+ i === -1 ? arr.push(item) : arr.splice(i, 1)
18
+ }
19
+
20
+ static insertByKey<T extends any = any>(arr: T[], key: keyof T, item: T) {
21
+ const count = arr.length;
22
+ for (let i = 0; i < count; i++) {
23
+ if (arr[i][key] === item[key]) return
24
+ }
25
+ arr.push(item)
26
+ }
27
+
28
+ static removeByKey<T extends any = any, K extends keyof T = keyof T>(arr: T[], key: keyof T, value: T[K]) {
29
+ const count = arr.length;
30
+ for (let i = 0; i < count; i++) {
31
+ const item = arr[i]
32
+ if (item[key] === value) {
33
+ arr.splice(i, 1)
34
+ return
35
+ }
36
+ }
37
+ }
38
+
39
+ static equals(left: { [k: string]: any }, right: { [k: string]: any }, ignore: string[] = []) {
40
+ if (left.length !== right.length) return false
41
+
42
+ for (let i = 0; i < left.length; i++) {
43
+ const value = left[i]
44
+ const t = typeof value
45
+
46
+ if (t === 'string' || t === 'boolean' || t === 'number' || t === 'bigint' || t === 'undefined' || t === 'symbol' || t === null) {
47
+ if (value !== right[i]) return false
48
+ }
49
+
50
+ if (Array.isArray(value)) {
51
+ if (ArrayUtils.equals(value, right[i], ignore) == false) return false
52
+ continue
53
+ }
54
+
55
+ if (t === 'object') {
56
+ if (ObjectUtils.equals(value, right[i], ignore) === false) return false
57
+ continue
58
+ }
59
+ }
60
+
61
+ return true
62
+ }
63
+ }
64
+
65
+ export { ArrayUtils }
@@ -0,0 +1,72 @@
1
+ import { language } from './locale'
2
+
3
+ enum STATUS {
4
+ PENDING = 1,
5
+ RUNNING = 2,
6
+ RESOLVED = 3,
7
+ REJECTED = 4,
8
+ CANCELLED = 5
9
+ }
10
+
11
+ type AsyncTaskFn<T> = () => T | Promise<T>
12
+
13
+ class AsyncTask<T = any> {
14
+ private _task: AsyncTaskFn<T>;
15
+ private _status: STATUS;
16
+ private _promise: Promise<T>;
17
+ private _resolve!: ((value: T | PromiseLike<T>) => void) | null;
18
+ private _reject!: ((reason?: any) => void) | null;
19
+
20
+ constructor(task?: AsyncTaskFn<T>) {
21
+ this._task = task ?? (async () => { }) as AsyncTaskFn<T>;
22
+ this._status = STATUS.PENDING;
23
+ this._promise = new Promise<T>((resolve, reject) => {
24
+ this._resolve = resolve;
25
+ this._reject = reject;
26
+ })
27
+ }
28
+
29
+ /**运行任务 */
30
+ run(): Promise<T> {
31
+ if (this._status !== STATUS.PENDING) return this._promise
32
+
33
+ this._status = STATUS.RUNNING;
34
+
35
+ Promise.resolve(this._task())
36
+ .then(result => {
37
+ if (this._status === STATUS.RUNNING) {
38
+ this._status = STATUS.RESOLVED;
39
+ this._resolve!(result);
40
+ }
41
+ })
42
+ .catch(error => {
43
+ if (this._status === STATUS.RUNNING) {
44
+ this._status = STATUS.REJECTED;
45
+ this._reject!(error);
46
+ }
47
+ });
48
+
49
+ return this._promise;
50
+ }
51
+
52
+ /**取消运行中的任务 */
53
+ cancel(): boolean {
54
+ if (this._status === STATUS.PENDING || this._status === STATUS.RUNNING) {
55
+ this._status = STATUS.CANCELLED;
56
+ if (this._reject) {
57
+ this._reject(new Error(language.get('asyncTask.task.was.cancelled')));
58
+ }
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+
64
+ /**等待任务完成 */
65
+ done(): Promise<T> {
66
+ return this._promise;
67
+ }
68
+ }
69
+
70
+ export {
71
+ AsyncTask
72
+ }
@@ -0,0 +1,119 @@
1
+ /**资产的类别 */
2
+ class Category {
3
+ static ts = 'ts'
4
+ static prefab = 'prefab'
5
+ static dataset = 'dataset'
6
+ static style = 'style'
7
+ static font = 'font'
8
+ static routine = 'routine'
9
+ static texture = 'texture'
10
+ static material = 'material'
11
+ static geometry = 'geometry'
12
+ static scene = 'scene'
13
+ static json = 'json'
14
+ static image = 'image'
15
+ static audio = 'audio'
16
+ static video = 'video'
17
+ static model = 'model'
18
+ static other = 'other'
19
+
20
+ static tsSuffix = `.${this.ts}`
21
+ static prefabSuffix = `.${this.prefab}.json`
22
+ static datasetSuffix = `.${this.dataset}.json`
23
+ static styleSuffix = `.${this.style}.json`
24
+ static fontSuffix = `.${this.font}.json`
25
+ static routineSuffix = `.${this.routine}.json`
26
+ static textureSuffix = `.${this.texture}.json`
27
+ static materialSuffix = `.${this.material}.json`
28
+ static geometrySuffix = `.${this.geometry}.json`
29
+ static sceneSuffix = `.${this.scene}.json`
30
+ static manifestSuffix = `.manifest.json`
31
+
32
+ static jsonSuffixs = ['.json']
33
+ static imageSuffixs = ['.png', '.jpg', '.jpeg', '.bmp', '.gif']
34
+ static audioSuffixs = ['.mp3', '.wav', '.wmv']
35
+ static videoSuffixs = ['.mp4', '.m2v', '.mkv', '.rmvb', '.wmv', '.avi', '.flv', '.mov', '.m4v']
36
+ static modelSuffixs = ['.glb', '.gltf']
37
+
38
+ static descriptorSuffixs = [
39
+ this.prefabSuffix,
40
+ this.datasetSuffix,
41
+ this.styleSuffix,
42
+ this.routineSuffix,
43
+ this.textureSuffix,
44
+ this.materialSuffix,
45
+ this.geometrySuffix,
46
+ this.sceneSuffix,
47
+ ]
48
+ static descriptorCategorys = [
49
+ this.prefab,
50
+ this.dataset,
51
+ this.style,
52
+ this.routine,
53
+ this.texture,
54
+ this.material,
55
+ this.geometry,
56
+ this.scene,
57
+ ]
58
+
59
+ /**根据后缀获取资产类型 */
60
+ static getCategory(suffix: string) {
61
+ if (suffix === this.tsSuffix) {
62
+ return this.ts
63
+ } else if (suffix === this.prefabSuffix) {
64
+ return this.prefab
65
+ } else if (suffix === this.datasetSuffix) {
66
+ return this.dataset
67
+ } else if (suffix === this.styleSuffix) {
68
+ return this.style
69
+ } else if (suffix === this.fontSuffix) {
70
+ return this.font
71
+ } else if (suffix === this.routineSuffix) {
72
+ return this.routine
73
+ } else if (suffix === this.textureSuffix) {
74
+ return this.texture
75
+ } else if (suffix === this.materialSuffix) {
76
+ return this.material
77
+ } else if (suffix === this.geometrySuffix) {
78
+ return this.geometry
79
+ } else if (suffix === this.sceneSuffix) {
80
+ return this.scene
81
+ } else if (this.jsonSuffixs.includes(suffix)) {
82
+ return this.json
83
+ } else if (this.imageSuffixs.includes(suffix)) {
84
+ return this.image
85
+ } else if (this.audioSuffixs.includes(suffix)) {
86
+ return this.audio
87
+ } else if (this.videoSuffixs.includes(suffix)) {
88
+ return this.video
89
+ } else if (this.modelSuffixs.includes(suffix)) {
90
+ return this.model
91
+ } else {
92
+ return this.other
93
+ }
94
+ }
95
+
96
+ /**根据名称解析资产信息 */
97
+ static parseName(name?: string) {
98
+ const result = { name: '', suffix: '', category: 'other' }
99
+ if (name === undefined || name === '') return result
100
+
101
+ const nameClips = name.split('.')
102
+ result.name = nameClips[0] ?? ''
103
+
104
+ for (let i = 1; i < nameClips.length; i++) {
105
+ result.suffix += `.${nameClips[i]}`
106
+ }
107
+ result.category = this.getCategory(result.suffix)
108
+
109
+ return result
110
+ }
111
+
112
+ /**根据后缀判断是否为描述器类资产(场景,组合,材质,纹理,几何体) */
113
+ static isDescriptor(suffix: string, type: 'suffix' | 'category' = 'suffix') {
114
+ const tests = type === 'suffix' ? Category.descriptorSuffixs : Category.descriptorCategorys
115
+ return tests.includes(suffix)
116
+ }
117
+ }
118
+
119
+ export { Category }
package/src/Color.ts ADDED
@@ -0,0 +1,111 @@
1
+ import tinycolor from "tinycolor2"
2
+ import { DeserializationObject, Serialization } from "./Serialization"
3
+
4
+ class Color extends tinycolor { }
5
+
6
+ interface ColorItem {
7
+ value: string
8
+ opacity: number
9
+ ratio: number
10
+ }
11
+
12
+ class BasicStyleColor {
13
+ static type = 'basic'
14
+ static create(color: DeserializationObject<BasicStyleColor>) {
15
+ switch (color.type) {
16
+ case "linear":
17
+ return new LinearStyleColor(color as LinearStyleColor)
18
+ case "radial":
19
+ return new RadialStyleColor(color as RadialStyleColor)
20
+ default:
21
+ return new DefaultStyleColor(color as DefaultStyleColor)
22
+ }
23
+ }
24
+ static getColor(color: BasicStyleColor) {
25
+ return 'rgba(0,0,0,0)'
26
+ }
27
+ type: string
28
+
29
+ constructor() {
30
+ this.type = (this.constructor as typeof BasicStyleColor).type
31
+ }
32
+
33
+ getColor() {
34
+ return (this.constructor as typeof BasicStyleColor).getColor(this)
35
+ }
36
+
37
+ }
38
+
39
+ class DefaultStyleColor extends BasicStyleColor {
40
+ static type = 'default'
41
+ static getColor(color: DefaultStyleColor) {
42
+ return new Color(color.value).setAlpha(color.opacity).toRgbString()
43
+ }
44
+ declare type: 'default'
45
+ value: string
46
+ opacity: number
47
+
48
+ constructor(color: Partial<DeserializationObject<DefaultStyleColor>> = {}) {
49
+ super()
50
+ this.value = color.value ?? '#ffffff'
51
+ this.opacity = color.opacity ?? 1
52
+ }
53
+ }
54
+
55
+ class LinearStyleColor extends BasicStyleColor {
56
+ static type = 'linear'
57
+ static getColor(color: LinearStyleColor) {
58
+ let items = ''
59
+ for (const item of color.value) {
60
+ items += `, ${new Color(item.value).setAlpha(item.opacity).toRgbString()} ${item.ratio * 100}%`
61
+ }
62
+
63
+ return `linear-gradient(${color.angle}deg ${items})`
64
+ }
65
+ declare type: 'linear'
66
+ angle: number
67
+ value: ColorItem[]
68
+ constructor(color: Partial<DeserializationObject<LinearStyleColor>> = {}) {
69
+ super()
70
+ this.angle = color.angle ?? 0
71
+ this.value = color.value ? Serialization.deepClone(color.value) : [{ value: '#ffffff', opacity: 1, ratio: 0 }, { value: '#ffffff', opacity: 1, ratio: 1 }]
72
+ }
73
+
74
+ }
75
+
76
+ class RadialStyleColor extends BasicStyleColor {
77
+ static type = 'radial'
78
+ static getColor(color: RadialStyleColor) {
79
+ let radis = ''
80
+ if (color.radius < 0) {
81
+ radis = 'farthest-corner'
82
+ } else {
83
+ radis = `${color.radius}px`
84
+ }
85
+
86
+ let center = ''
87
+ center = `${color.center.x * 100}% ${color.center.y * 100}%`
88
+
89
+ let items = ''
90
+ for (const item of color.value) {
91
+ items += `, ${new Color(item.value).setAlpha(item.opacity).toRgbString()} ${item.ratio * 100}%`
92
+ }
93
+
94
+ return `radial-gradient(circle ${radis} at ${center} ${items})`
95
+ }
96
+ declare type: 'radial'
97
+ center: { x: number; y: number }
98
+ radius: number
99
+ value: ColorItem[]
100
+
101
+ constructor(color: Partial<DeserializationObject<RadialStyleColor>> = {}) {
102
+ super()
103
+ this.center = color.center ? Serialization.deepClone(color.center) : { x: 0.5, y: 0.5 }
104
+ this.radius = color.radius ?? -1
105
+ this.value = color.value ? Serialization.deepClone(color.value) : [{ value: '#ffffff', opacity: 1, ratio: 0 }, { value: '#ffffff', opacity: 1, ratio: 1 }]
106
+ }
107
+ }
108
+
109
+ type StyleColor = DefaultStyleColor | LinearStyleColor | RadialStyleColor
110
+
111
+ export { ColorItem, BasicStyleColor, DefaultStyleColor, LinearStyleColor, RadialStyleColor, StyleColor, Color }
package/src/Engine.ts ADDED
@@ -0,0 +1,101 @@
1
+ import { language } from './locale'
2
+ import { Descriptor, RoutineDescriptor, ServiceSchedulerDescriptor, StyleDescriptor, UpdateArgs } from './descriptor'
3
+ import { Generate } from './Generate'
4
+ import { InputSystem } from './InputSystem'
5
+ import { Listener } from './Listener'
6
+ import { Compilation, Graphics } from './graphics'
7
+ import { setupStyle } from './setup';
8
+ import { Asset } from './asset'
9
+ import { RoutineInterpreter } from './interpreter'
10
+
11
+ interface EngineData { }
12
+
13
+ interface EngineListener {
14
+ beforeRender: (args: UpdateArgs) => void
15
+ afterRender: (args: UpdateArgs) => void
16
+ }
17
+
18
+ interface PickupUpdateArgs extends UpdateArgs {
19
+ mouse: { x: number; y: number }
20
+ }
21
+
22
+ class Engine extends Listener<EngineListener> {
23
+ private compilation = new Compilation()
24
+ private prevTimestamp = 0
25
+ private frame = 0
26
+
27
+ readonly uuid = Generate.uuid()
28
+ readonly DOM = document.createElement('div')
29
+ readonly inputSystem = new InputSystem(this.DOM)
30
+
31
+ constructor(data: Partial<EngineData> = {}) {
32
+ super()
33
+ this.DOM.style.width = '100%'
34
+ this.DOM.style.height = '100%'
35
+ this.DOM.style.position = 'relative'
36
+ this.DOM.style.top = '0'
37
+ this.DOM.style.left = '0'
38
+ this.DOM.style.overflow = 'hidden'
39
+ this.DOM.classList.add('vyr-engine', StyleDescriptor.basicName)
40
+ this.DOM.setAttribute('data-vyr', `vyr-engine`)
41
+ this.DOM.addEventListener('contextmenu', (e) => e.preventDefault())
42
+ }
43
+
44
+ private getDelta(timestamp: number) {
45
+ return (timestamp - this.prevTimestamp) / 1000
46
+ }
47
+
48
+ private tick(timestamp: number) {
49
+ const delta = this.getDelta(timestamp)
50
+ this.prevTimestamp = timestamp
51
+ this.frame = requestAnimationFrame((nextTimestamp) => this.tick(nextTimestamp))
52
+
53
+ const args = { delta }
54
+
55
+ this.inputSystem.process()
56
+
57
+ this.compilation.process(this, args)
58
+ }
59
+
60
+ run(el: string | HTMLElement) {
61
+ if (this.frame !== 0) return
62
+
63
+ if (typeof el === 'string') {
64
+ const ele = document.getElementById(el)
65
+ if (ele === null) throw language.get('engine.run.container.notFound')
66
+ el = ele
67
+ }
68
+ el.appendChild(this.DOM)
69
+ setupStyle()
70
+
71
+ this.prevTimestamp = performance.now()
72
+ this.tick(this.prevTimestamp)
73
+ }
74
+
75
+ stop() {
76
+ if (this.frame === 0) return
77
+
78
+ cancelAnimationFrame(this.frame)
79
+ this.frame = 0
80
+ }
81
+
82
+ switch(scheduler: ServiceSchedulerDescriptor) {
83
+ this.compilation.clear(this)
84
+
85
+ this.compilation.listen(scheduler, this)
86
+
87
+ window.dispatchEvent(new Event('resize'))
88
+
89
+ this.compilation.process(this, { delta: 0 })
90
+ }
91
+
92
+ clear() {
93
+ this.compilation.clear(this)
94
+ }
95
+
96
+ getGraphics(scheduler: ServiceSchedulerDescriptor) {
97
+ return this.compilation.get(scheduler)
98
+ }
99
+ }
100
+
101
+ export { PickupUpdateArgs, Engine }
@@ -0,0 +1,40 @@
1
+ let id = 1;
2
+ const _lut = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff'];
3
+
4
+ class Generate {
5
+ static id() {
6
+ return id++
7
+ }
8
+
9
+ static uuid() {
10
+ const d0 = Math.random() * 0xffffffff | 0;
11
+ const d1 = Math.random() * 0xffffffff | 0;
12
+ const d2 = Math.random() * 0xffffffff | 0;
13
+ const d3 = Math.random() * 0xffffffff | 0;
14
+ const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + '-' +
15
+ _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + '-' + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + '-' +
16
+ _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + '-' + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] +
17
+ _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff];
18
+
19
+ return uuid.toLowerCase()
20
+ }
21
+
22
+ static delayExecute(cb: (...args: any[]) => any) {
23
+ const delaySatate = {
24
+ args: [] as any[],
25
+ execute: false
26
+ }
27
+ const execute = (...args: any[]) => {
28
+ delaySatate.args = args
29
+ if (delaySatate.execute === true) return
30
+ delaySatate.execute = true
31
+ requestAnimationFrame(() => {
32
+ cb(...delaySatate.args)
33
+ delaySatate.execute = false
34
+ })
35
+ }
36
+ return execute
37
+ }
38
+ }
39
+
40
+ export { Generate }
@@ -0,0 +1,108 @@
1
+ type IEM = GlobalEventHandlersEventMap
2
+
3
+ interface InputConfig {
4
+ /**事件只触发一次,默认`false` */
5
+ once: boolean
6
+ /**将事件的处理顺序设为事件捕获,默认`false` */
7
+ capture: boolean
8
+ /**处理函数不会调用`preventDefault`来阻止默认行为时应设为true,默认`false` */
9
+ passive: boolean
10
+ /**事件触发的对象 */
11
+ target: Window | Document | HTMLElement
12
+ }
13
+
14
+ const privateState = {
15
+ id: 1,
16
+ listener: {
17
+ default: (input: InputEvent, system: InputSystem) => {
18
+ return (event: Event) => {
19
+ input.event = event
20
+ if (input.enable) return
21
+ input.enable = true
22
+ system.quaua.push(input)
23
+ }
24
+ },
25
+ once: (input: InputEvent, system: InputSystem) => {
26
+ const execute = privateState.listener.default(input, system)
27
+ return (e: Event) => {
28
+ execute(e)
29
+ system.unlisten(input.id)
30
+ }
31
+ },
32
+ }
33
+ }
34
+
35
+ class InputEvent {
36
+ readonly id = privateState.id++
37
+ readonly type
38
+ readonly target
39
+ readonly callback: (e: Event) => void
40
+ readonly listener: (e: Event) => void
41
+ enable = false
42
+ event!: Event
43
+
44
+ constructor(type: keyof IEM, callback: (e: any) => void, system: InputSystem, config: InputConfig) {
45
+ this.type = type
46
+ this.target = config.target
47
+ this.callback = callback
48
+ this.listener = config.once ? privateState.listener.once(this, system) : privateState.listener.default(this, system)
49
+ }
50
+
51
+ execute() {
52
+ this.callback(this.event)
53
+ this.enable = false
54
+ }
55
+ }
56
+
57
+ class InputSystem {
58
+ private _target
59
+ private _registry: InputEvent[] = []
60
+ readonly quaua: InputEvent[] = []
61
+
62
+ constructor(target: InputConfig['target']) {
63
+ this._target = target
64
+ }
65
+
66
+ /**
67
+ * 监听事件输入,事件在一帧中只执行一次
68
+ * @param type 事件类型
69
+ * @param callback 事件触发时的回调
70
+ * @param options.once 只执行一次回调
71
+ * @param options.target 监听事件的绑定对象
72
+ * @param options.capture 事件执行的阶段,true:捕获阶段执行,false:冒泡阶段执行
73
+ */
74
+ listen<T extends keyof IEM>(type: T, callback: (event: IEM[T]) => void, options: Partial<InputConfig> = {}) {
75
+ const config: InputConfig = { once: false, target: this._target, passive: false, capture: false, ...options }
76
+
77
+ for (const input of this._registry) {
78
+ if (input.type === type && input.callback === callback && options.target === input.target) return input.id
79
+ }
80
+
81
+ const input = new InputEvent(type, callback, this, config)
82
+ this._registry.push(input)
83
+ input.target.addEventListener(type, input.listener, config.capture)
84
+
85
+ return input.id
86
+ }
87
+
88
+ /**解除事件监听 */
89
+ unlisten(id: number) {
90
+ for (let i = 0; i < this._registry.length; i++) {
91
+ const input = this._registry[i]
92
+ if (input.id === id) {
93
+ this._registry.splice(i, 1)
94
+ input.target.removeEventListener(input.type, input.listener)
95
+ return
96
+ }
97
+ }
98
+ }
99
+
100
+ /**处理输入事件 */
101
+ process() {
102
+ const quaua = [...this.quaua]
103
+ for (const input of quaua) input.execute()
104
+ this.quaua.length = 0
105
+ }
106
+ }
107
+
108
+ export { InputSystem }