@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.
- package/package.json +19 -0
- package/src/ArrayUtils.ts +65 -0
- package/src/AsyncTask.ts +72 -0
- package/src/Category.ts +119 -0
- package/src/Color.ts +111 -0
- package/src/Engine.ts +101 -0
- package/src/Generate.ts +40 -0
- package/src/InputSystem.ts +108 -0
- package/src/Listener.ts +59 -0
- package/src/ObjectPool.ts +84 -0
- package/src/ObjectUtils.ts +49 -0
- package/src/Scriptable.ts +27 -0
- package/src/Serialization.ts +49 -0
- package/src/Traverser.ts +39 -0
- package/src/actor/Actor.ts +28 -0
- package/src/actor/AnimationUnitActor.ts +289 -0
- package/src/actor/DivActor.ts +70 -0
- package/src/actor/FragmentActor.ts +56 -0
- package/src/actor/HTMActor.ts +166 -0
- package/src/actor/HTMServiceActor.ts +57 -0
- package/src/actor/HTMTransformControllerActor.ts +404 -0
- package/src/actor/StyleActor.ts +96 -0
- package/src/actor/index.ts +8 -0
- package/src/asset/Asset.ts +271 -0
- package/src/asset/AssetGraph.ts +246 -0
- package/src/asset/index.ts +2 -0
- package/src/descriptor/AnimationUnitDescriptor.ts +65 -0
- package/src/descriptor/CameraDescriptor.ts +12 -0
- package/src/descriptor/ControllerDescriptor.ts +16 -0
- package/src/descriptor/DatasetDescriptor.ts +92 -0
- package/src/descriptor/Descriptor.ts +415 -0
- package/src/descriptor/DivDescriptor.ts +18 -0
- package/src/descriptor/DynamicDescriptor.ts +27 -0
- package/src/descriptor/HTMLDescriptor.ts +87 -0
- package/src/descriptor/HTMLServiceDescriptor.ts +19 -0
- package/src/descriptor/HTMLTransformControllerDescriptor.ts +34 -0
- package/src/descriptor/NodeDescriptor.ts +32 -0
- package/src/descriptor/PrefabDescriptor.ts +53 -0
- package/src/descriptor/PrefabInstanceDescriptor.ts +32 -0
- package/src/descriptor/RoutineDescriptor.ts +54 -0
- package/src/descriptor/ServiceDescriptor.ts +32 -0
- package/src/descriptor/ServiceSchedulerDescriptor.ts +32 -0
- package/src/descriptor/StyleDescriptor.ts +213 -0
- package/src/descriptor/index.ts +17 -0
- package/src/graphics/Collection.ts +25 -0
- package/src/graphics/Compilation.ts +82 -0
- package/src/graphics/Graphics.ts +475 -0
- package/src/graphics/Observer.ts +36 -0
- package/src/graphics/Unit.ts +83 -0
- package/src/graphics/VariableProxy.ts +92 -0
- package/src/graphics/index.ts +5 -0
- package/src/index.ts +26 -0
- package/src/interpreter/AnimationUnitInterpreter.ts +53 -0
- package/src/interpreter/DatasetInterpreter.ts +11 -0
- package/src/interpreter/DivInterpreter.ts +44 -0
- package/src/interpreter/DynamicInterpreter.ts +207 -0
- package/src/interpreter/FragmentInterpreter.ts +34 -0
- package/src/interpreter/HTMLServiceInterpreter.ts +47 -0
- package/src/interpreter/HTMLTransformControllerInterpreter.ts +40 -0
- package/src/interpreter/Interpreter.ts +69 -0
- package/src/interpreter/PrefaInterpreter.ts +11 -0
- package/src/interpreter/PrefabInstanceInterpreter.ts +12 -0
- package/src/interpreter/RoutineInterpreter.ts +88 -0
- package/src/interpreter/ServiceInterpreter.ts +24 -0
- package/src/interpreter/ServiceSchedulerInterpreter.ts +42 -0
- package/src/interpreter/StyleInterpreter.ts +66 -0
- package/src/interpreter/index.ts +14 -0
- package/src/locale/Language.ts +10 -0
- package/src/locale/LanguageProvider.ts +48 -0
- package/src/locale/index.ts +2 -0
- package/src/math/Euler.ts +303 -0
- package/src/math/Matrix4.ts +1123 -0
- package/src/math/Quaternion.ts +737 -0
- package/src/math/Vector2.ts +680 -0
- package/src/math/Vector3.ts +1062 -0
- package/src/math/index.ts +5 -0
- package/src/math/utils.ts +17 -0
- package/src/preset/execute/dataset/index.ts +1 -0
- package/src/preset/execute/dataset/update.ts +52 -0
- package/src/preset/execute/graphics/index.ts +1 -0
- package/src/preset/execute/graphics/invoke.ts +49 -0
- package/src/preset/execute/index.ts +4 -0
- package/src/preset/execute/net/index.ts +1 -0
- package/src/preset/execute/net/request.ts +103 -0
- package/src/preset/execute/scheduler/index.ts +1 -0
- package/src/preset/execute/scheduler/switch.ts +46 -0
- package/src/preset/index.ts +7 -0
- package/src/preset/routine/graphics/index.ts +1 -0
- package/src/preset/routine/graphics/invoke.ts +27 -0
- package/src/preset/routine/index.ts +2 -0
- package/src/preset/routine/scheduler/index.ts +1 -0
- package/src/preset/routine/scheduler/switch.ts +27 -0
- package/src/setup/index.ts +17 -0
- package/src/utils/AssetProvider.ts +72 -0
- 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 }
|
package/src/AsyncTask.ts
ADDED
|
@@ -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
|
+
}
|
package/src/Category.ts
ADDED
|
@@ -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 }
|
package/src/Generate.ts
ADDED
|
@@ -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 }
|