canvasengine 1.3.0 → 2.0.1-beta.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 (125) hide show
  1. package/.cursorrules +0 -0
  2. package/.github/workflows/ci.yml +29 -0
  3. package/README.md +79 -0
  4. package/dist/compiler/vite.js +119 -0
  5. package/dist/compiler/vite.js.map +1 -0
  6. package/dist/index.d.ts +846 -0
  7. package/dist/index.js +3340 -0
  8. package/dist/index.js.map +1 -0
  9. package/index.d.ts +6 -0
  10. package/logo.png +0 -0
  11. package/package.json +84 -18
  12. package/src/compiler/grammar.pegjs +180 -0
  13. package/src/compiler/vite.ts +166 -0
  14. package/src/components/Canvas.ts +134 -0
  15. package/src/components/Container.ts +46 -0
  16. package/src/components/DisplayObject.ts +458 -0
  17. package/src/components/DrawMap/index.ts +65 -0
  18. package/src/components/Graphic.ts +147 -0
  19. package/src/components/NineSliceSprite.ts +46 -0
  20. package/src/components/ParticleEmitter.ts +39 -0
  21. package/src/components/Scene.ts +6 -0
  22. package/src/components/Sprite.ts +493 -0
  23. package/src/components/Text.ts +145 -0
  24. package/src/components/Tilemap/Tile.ts +79 -0
  25. package/src/components/Tilemap/TileGroup.ts +207 -0
  26. package/src/components/Tilemap/TileLayer.ts +163 -0
  27. package/src/components/Tilemap/TileSet.ts +41 -0
  28. package/src/components/Tilemap/index.ts +80 -0
  29. package/src/components/TilingSprite.ts +39 -0
  30. package/src/components/Viewport.ts +159 -0
  31. package/src/components/index.ts +12 -0
  32. package/src/components/types/DisplayObject.ts +68 -0
  33. package/src/components/types/MouseEvent.ts +3 -0
  34. package/src/components/types/Spritesheet.ts +389 -0
  35. package/src/components/types/index.ts +4 -0
  36. package/src/directives/Drag.ts +84 -0
  37. package/src/directives/KeyboardControls.ts +922 -0
  38. package/src/directives/Scheduler.ts +112 -0
  39. package/src/directives/Sound.ts +91 -0
  40. package/src/directives/Transition.ts +45 -0
  41. package/src/directives/ViewportCull.ts +40 -0
  42. package/src/directives/ViewportFollow.ts +26 -0
  43. package/src/directives/index.ts +7 -0
  44. package/src/engine/animation.ts +113 -0
  45. package/src/engine/bootstrap.ts +19 -0
  46. package/src/engine/directive.ts +23 -0
  47. package/src/engine/reactive.ts +379 -0
  48. package/src/engine/signal.ts +138 -0
  49. package/src/engine/trigger.ts +40 -0
  50. package/src/engine/utils.ts +135 -0
  51. package/src/hooks/addContext.ts +6 -0
  52. package/src/hooks/useProps.ts +155 -0
  53. package/src/hooks/useRef.ts +21 -0
  54. package/src/index.ts +14 -0
  55. package/src/presets/Bar.ts +89 -0
  56. package/src/presets/Button.ts +0 -0
  57. package/src/presets/Joystick.ts +286 -0
  58. package/src/presets/NightAmbiant.ts +122 -0
  59. package/src/presets/Particle.ts +53 -0
  60. package/src/utils/Ease.ts +33 -0
  61. package/src/utils/RadialGradient.ts +86 -0
  62. package/starter/assets/logo.png +0 -0
  63. package/starter/components/app.ce +18 -0
  64. package/starter/components/hello.ce +35 -0
  65. package/starter/index.html +21 -0
  66. package/starter/main.ts +6 -0
  67. package/starter/package.json +20 -0
  68. package/starter/tsconfig.json +32 -0
  69. package/starter/vite.config.ts +12 -0
  70. package/tsconfig.json +32 -0
  71. package/tsconfig.node.json +10 -0
  72. package/tsup.config.ts +28 -0
  73. package/vitest.config.ts +12 -0
  74. package/.gitattributes +0 -22
  75. package/.npmignore +0 -163
  76. package/canvasengine-1.3.0.all.min.js +0 -21
  77. package/canvasengine.js +0 -5802
  78. package/core/DB.js +0 -24
  79. package/core/ModelServer.js +0 -348
  80. package/core/Users.js +0 -190
  81. package/core/engine-common.js +0 -952
  82. package/doc/cocoonjs.md +0 -36
  83. package/doc/doc-lang.yml +0 -43
  84. package/doc/doc-router.yml +0 -14
  85. package/doc/doc-tuto.yml +0 -9
  86. package/doc/doc.yml +0 -39
  87. package/doc/element.md +0 -37
  88. package/doc/entity.md +0 -90
  89. package/doc/extend.md +0 -47
  90. package/doc/get_started.md +0 -19
  91. package/doc/images/entity.png +0 -0
  92. package/doc/multitouch.md +0 -58
  93. package/doc/nodejs.md +0 -142
  94. package/doc/scene.md +0 -44
  95. package/doc/text.md +0 -156
  96. package/examples/server/client.html +0 -31
  97. package/examples/server/server.js +0 -16
  98. package/examples/tiled_server/client.html +0 -52
  99. package/examples/tiled_server/images/tiles_spritesheet.png +0 -0
  100. package/examples/tiled_server/server/map.json +0 -50
  101. package/examples/tiled_server/server/map.tmx +0 -16
  102. package/examples/tiled_server/server/server.js +0 -16
  103. package/extends/Animation.js +0 -910
  104. package/extends/Effect.js +0 -252
  105. package/extends/Gleed2d.js +0 -252
  106. package/extends/Hit.js +0 -1509
  107. package/extends/Input.js +0 -699
  108. package/extends/Marshal.js +0 -716
  109. package/extends/Scrolling.js +0 -388
  110. package/extends/Soundmanager2.js +0 -5466
  111. package/extends/Spritesheet.js +0 -196
  112. package/extends/Text.js +0 -366
  113. package/extends/Tiled.js +0 -403
  114. package/extends/Window.js +0 -575
  115. package/extends/gamepad.js +0 -397
  116. package/extends/socket.io.min.js +0 -2
  117. package/extends/swf/soundmanager2.swf +0 -0
  118. package/extends/swf/soundmanager2_debug.swf +0 -0
  119. package/extends/swf/soundmanager2_flash9.swf +0 -0
  120. package/extends/swf/soundmanager2_flash9_debug.swf +0 -0
  121. package/extends/swf/soundmanager2_flash_xdomain.zip +0 -0
  122. package/extends/workers/transition.js +0 -43
  123. package/index.js +0 -46
  124. package/license.txt +0 -19
  125. package/readme.md +0 -483
@@ -0,0 +1,112 @@
1
+ import { WritableSignal } from '@signe/reactive';
2
+ import Stats from 'stats.js';
3
+ import { Directive, registerDirective } from '../engine/directive';
4
+ import { Element } from '../engine/reactive';
5
+ import * as Utils from '../engine/utils';
6
+
7
+ export interface Tick {
8
+ timestamp: number
9
+ deltaTime: number
10
+ frame: number
11
+ deltaRatio: number
12
+ }
13
+
14
+ export class Scheduler extends Directive {
15
+ private maxFps?: number
16
+ private fps: number = 60
17
+ private deltaTime: number = 0
18
+ public frame: number = 0
19
+ private timestamp: number = 0
20
+ private requestedDelay: number = 0
21
+ private lastTimestamp: number = 0
22
+ private _stop: boolean = false
23
+ private tick: WritableSignal<Tick | null>
24
+
25
+ private stats = new Stats()
26
+
27
+ onInit(element: Element) {
28
+ this.tick = element.propObservables?.tick as any
29
+ }
30
+
31
+ onDestroy() { }
32
+ onMount(element: Element) { }
33
+ onUpdate(props: any) { }
34
+
35
+ nextTick(timestamp: number) {
36
+ this.lastTimestamp = this.lastTimestamp || this.timestamp // first
37
+ this.deltaTime = Utils.preciseNow() - this.timestamp
38
+ this.timestamp = timestamp
39
+ this.stats.begin()
40
+ this.tick.set({
41
+ timestamp: this.timestamp,
42
+ deltaTime: this.deltaTime,
43
+ frame: this.frame,
44
+ deltaRatio: ~~this.deltaTime / ~~Utils.fps2ms(this.fps)
45
+ })
46
+ this.stats.end()
47
+ this.lastTimestamp = this.timestamp
48
+ this.frame++
49
+ }
50
+
51
+ private showPanel() {
52
+ this.stats.showPanel(0)
53
+ }
54
+
55
+ /**
56
+ * start the schedule
57
+ * @return {Scheduler} returns this scheduler instance
58
+ */
59
+ start(options: {
60
+ maxFps?: number
61
+ fps?: number,
62
+ delay?: number
63
+ } = {}) {
64
+ if (options.maxFps) this.maxFps = options.maxFps
65
+ if (options.fps) this.fps = options.fps
66
+ if (options.delay) this.requestedDelay = options.delay
67
+ this.showPanel()
68
+ const requestAnimationFrame = (fn: (timestamp: number) => void) => {
69
+ if (Utils.isBrowser()) {
70
+ window.requestAnimationFrame(fn.bind(this))
71
+ }
72
+ else {
73
+ setTimeout(() => {
74
+ this.requestedDelay = 0
75
+ fn(Utils.preciseNow())
76
+ }, Utils.fps2ms(this.fps) + this.requestedDelay)
77
+ }
78
+ }
79
+
80
+ if (!this.maxFps) {
81
+ const loop = (timestamp: number) => {
82
+ requestAnimationFrame(loop)
83
+ this.nextTick(timestamp)
84
+ }
85
+ requestAnimationFrame(loop)
86
+ }
87
+ else {
88
+ const msInterval = Utils.fps2ms(this.maxFps)
89
+ let now = Utils.preciseNow()
90
+ let then = Utils.preciseNow()
91
+ const loop = (timestamp: number) => {
92
+ if (this._stop) return
93
+ requestAnimationFrame(loop)
94
+ now = Utils.preciseNow()
95
+ const elapsed = now - then
96
+ if (elapsed > msInterval) {
97
+ then = now - (elapsed % msInterval)
98
+ this.nextTick(timestamp)
99
+ }
100
+ }
101
+ requestAnimationFrame(loop)
102
+ }
103
+
104
+ return this;
105
+ }
106
+
107
+ stop() {
108
+ this._stop = true
109
+ }
110
+ }
111
+
112
+ registerDirective('tick', Scheduler)
@@ -0,0 +1,91 @@
1
+ import { effect } from '@signe/reactive';
2
+ import { Howl } from 'howler';
3
+ import { Container } from 'pixi.js';
4
+ import { Subscription } from 'rxjs';
5
+ import { Directive, registerDirective } from '../engine/directive';
6
+ import { Element } from '../engine/reactive';
7
+ import { calculateDistance, error } from '../engine/utils';
8
+
9
+ const EVENTS = ['load', 'loaderror', 'playerror', 'play', 'end', 'pause', 'stop', 'mute', 'volume', 'rate', 'seek', 'fade', 'unlock']
10
+
11
+ export class Sound extends Directive {
12
+ private sound: Howl
13
+ private eventsFn: ((...args: any[]) => void)[] = []
14
+ private maxVolume: number = 1
15
+ private maxDistance: number = 100
16
+ private tickSubscription?: Subscription
17
+
18
+ onInit(element: Element<Container>) { }
19
+
20
+ onMount(element: Element<Container>) {
21
+ const { props } = element
22
+ const tick = props.context.tick
23
+ const { src, autoplay, loop, volume, spatial } = props.sound
24
+ this.sound = new Howl({
25
+ src,
26
+ autoplay,
27
+ loop,
28
+ volume
29
+ })
30
+ for (let event of EVENTS) {
31
+ if (!props.sound[event]) continue
32
+ const fn = props.sound[event]
33
+ this.eventsFn.push(fn)
34
+ this.sound.on(event, fn);
35
+ }
36
+
37
+ if (spatial) {
38
+ const { soundListenerPosition } = props.context
39
+ if (!soundListenerPosition) {
40
+ throw new error('SoundListenerPosition directive is required for spatial sound in component parent')
41
+ }
42
+ const { x: listenerX, y: listenerY } = soundListenerPosition
43
+ this.tickSubscription = effect(() => {
44
+ tick()
45
+ const { x, y } = element.componentInstance
46
+ const distance = calculateDistance(x, y, listenerX(), listenerY());
47
+ const volume = Math.max(this.maxVolume - (distance / this.maxDistance), 0)
48
+ this.sound.volume(volume)
49
+ }).subscription
50
+ }
51
+ }
52
+
53
+ onUpdate(props: any) {
54
+ const { volume, loop, mute, seek, playing, rate, spatial } = props
55
+ if (volume != undefined) this.sound.volume(volume)
56
+ if (loop != undefined) this.sound.loop(loop)
57
+ if (mute != undefined) this.sound.mute(mute)
58
+ if (seek != undefined) this.sound.seek(seek)
59
+ if (playing != undefined) {
60
+ if (playing) this.sound.play()
61
+ else this.sound.pause()
62
+ }
63
+ if (spatial) {
64
+ this.maxVolume = spatial.maxVolume ?? this.maxVolume
65
+ this.maxDistance = spatial.maxDistance ?? this.maxDistance
66
+ }
67
+ if (rate != undefined) this.sound.rate(rate)
68
+ }
69
+
70
+ onDestroy() {
71
+ this.sound.stop()
72
+ this.tickSubscription?.unsubscribe()
73
+ for (let event of EVENTS) {
74
+ if (this.eventsFn[event]) {
75
+ this.sound.off(event, this.eventsFn[event]);
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ class SoundListenerPosition extends Directive {
82
+ onMount(element: Element<any>) {
83
+ element.props.context.soundListenerPosition = element.propObservables?.soundListenerPosition
84
+ }
85
+ onInit(element: Element<any>) { }
86
+ onUpdate(props: any) { }
87
+ onDestroy() { }
88
+ }
89
+
90
+ registerDirective('sound', Sound)
91
+ registerDirective('soundListenerPosition', SoundListenerPosition)
@@ -0,0 +1,45 @@
1
+ import { Container, DisplacementFilter, Sprite, Texture, WRAP_MODES } from 'pixi.js';
2
+ import { animate } from 'popmotion';
3
+ import { Directive, registerDirective } from '../engine/directive';
4
+ import { Element } from '../engine/reactive';
5
+
6
+
7
+ export class Transition extends Directive {
8
+
9
+
10
+ onInit(element: Element<Container>) {
11
+ }
12
+
13
+ onMount(element: Element<Container>) {
14
+ const { image } = element.props.transition
15
+ const displacementSprite = new Sprite(Texture.from(image))
16
+ displacementSprite.texture.baseTexture.wrapMode = WRAP_MODES.REPEAT
17
+ const displacementFilter = new DisplacementFilter(displacementSprite)
18
+ const instance = element.componentInstance
19
+ instance.filters = [displacementFilter]
20
+
21
+ instance.addChild(displacementSprite)
22
+
23
+ setTimeout(() => {
24
+ animate({
25
+ from: 0,
26
+ to: 1,
27
+ duration: 500,
28
+ onUpdate: (progress) => {
29
+ displacementFilter.scale.x = progress
30
+ displacementFilter.scale.y = progress
31
+ }
32
+ })
33
+ }, 5000)
34
+ }
35
+
36
+ onUpdate(props: any) {
37
+
38
+ }
39
+
40
+ onDestroy() {
41
+
42
+ }
43
+ }
44
+
45
+ registerDirective('transition', Transition)
@@ -0,0 +1,40 @@
1
+ import { effect } from '@signe/reactive';
2
+ import { Simple } from "pixi-cull";
3
+ import { Container } from 'pixi.js';
4
+ import { Directive, registerDirective } from '../engine/directive';
5
+ import { Element } from '../engine/reactive';
6
+ import { error } from '../engine/utils';
7
+
8
+ export class ViewportCull extends Directive {
9
+ private cull: Simple
10
+
11
+ onInit(element) {
12
+ this.cull = new Simple({
13
+ dirtyTest: false,
14
+ })
15
+ }
16
+ onMount(element: Element<Container>) {
17
+ const tick = element.props.context.tick
18
+ const { viewportCull } = element.props
19
+ const { viewport } = element.props.context
20
+ if (!viewport) {
21
+ throw error('ViewportCull directive requires a Viewport component to be mounted in the same context')
22
+ }
23
+
24
+ element.props.children[0].subscribe((val) => {
25
+ this.cull.lists[0] = val.fullElements.map((el: any) => el.componentInstance)
26
+ })
27
+
28
+ effect(() => {
29
+ tick()
30
+ if (viewport.dirty) {
31
+ this.cull.cull(viewport.getVisibleBounds())
32
+ viewport.dirty = false
33
+ }
34
+ })
35
+ }
36
+ onUpdate(props: any) { }
37
+ onDestroy() { }
38
+ }
39
+
40
+ registerDirective('viewportCull', ViewportCull)
@@ -0,0 +1,26 @@
1
+ import { ComponentInstance } from '../components/DisplayObject';
2
+ import { Directive, registerDirective } from '../engine/directive';
3
+ import { Element } from '../engine/reactive';
4
+ import { error } from '../engine/utils';
5
+
6
+ export class ViewportFollow extends Directive {
7
+ onInit(element: Element<ComponentInstance>) {
8
+
9
+ }
10
+ onMount(element: Element) {
11
+ const { viewportFollow } = element.props
12
+ const { viewport } = element.props.context
13
+ if (!viewport) {
14
+ throw error('ViewportFollow directive requires a Viewport component to be mounted in the same context')
15
+ }
16
+ viewport.follow(element.componentInstance)
17
+ }
18
+ onUpdate(props: any) {
19
+
20
+ }
21
+ onDestroy() {
22
+
23
+ }
24
+ }
25
+
26
+ registerDirective('viewportFollow', ViewportFollow)
@@ -0,0 +1,7 @@
1
+ import './KeyboardControls'
2
+ import './Scheduler'
3
+ import './ViewportFollow'
4
+ //import './ViewportCull'
5
+ import './Sound'
6
+ import './Drag'
7
+ import './Transition'
@@ -0,0 +1,113 @@
1
+ import { effect, signal, type WritableSignal } from "@signe/reactive";
2
+ import { animate as animatePopmotion } from "popmotion";
3
+
4
+ interface AnimateOptions<T> {
5
+ duration?: number;
6
+ ease?: (t: number) => number;
7
+ onUpdate?: (value: T) => void;
8
+ }
9
+
10
+ export interface AnimatedState<T> {
11
+ current: T;
12
+ start: T;
13
+ end: T;
14
+ }
15
+
16
+ export interface AnimatedSignal<T> extends Omit<WritableSignal<T>, 'set'> {
17
+ (): T;
18
+ set: (newValue: T) => void;
19
+ animatedState: WritableSignal<AnimatedState<T>>;
20
+ update: (updater: (value: T) => T) => void;
21
+ }
22
+
23
+ export function isAnimatedSignal(signal: WritableSignal<any>): boolean {
24
+ return (signal as unknown as AnimatedSignal<any>).animatedState !== undefined;
25
+ }
26
+
27
+ /**
28
+ * Creates an animated signal with the given initial value and animation options.
29
+ * It's a writable signal that can be animated using popmotion. Properties of the animated signal are:
30
+ * - current: the current value of the signal.
31
+ * - start: the start value of the animation.
32
+ * - end: the end value of the animation.
33
+ *
34
+ * @param initialValue The initial value of the signal.
35
+ * @param options The animation options.
36
+ * @returns The animated signal.
37
+ * @example
38
+ * const animatedValue = animatedSignal(0, { duration: 1000 });
39
+ * animatedValue.set(10);
40
+ * animatedValue.update((value) => value + 1);
41
+ * console.log(animatedValue()); // 11
42
+ *
43
+ * animatedValue.animatedState() // { current: 10, start: 10, end: 11 }
44
+ */
45
+ export function animatedSignal<T>(initialValue: T, options: AnimateOptions<T> = {}): AnimatedSignal<T> {
46
+ const state: AnimatedState<T> = {
47
+ current: initialValue,
48
+ start: initialValue,
49
+ end: initialValue,
50
+ };
51
+ let animation
52
+
53
+ const publicSignal = signal(initialValue);
54
+ const privateSignal = signal(state);
55
+
56
+ effect(() => {
57
+ const currentState = privateSignal();
58
+ publicSignal.set(currentState.current);
59
+ });
60
+
61
+ function animatedSignal(): AnimatedState<T>;
62
+ function animatedSignal(newValue: T): void;
63
+ function animatedSignal(newValue?: T): AnimatedState<T> | void {
64
+ if (newValue === undefined) {
65
+ return privateSignal();
66
+ }
67
+
68
+ const prevState = privateSignal();
69
+ const newState: AnimatedState<T> = {
70
+ current: prevState.current,
71
+ start: prevState.current,
72
+ end: newValue,
73
+ };
74
+
75
+ privateSignal.set(newState);
76
+
77
+ if (animation) {
78
+ animation.stop();
79
+ }
80
+
81
+ animation = animatePopmotion({
82
+ // TODO
83
+ duration: 20,
84
+ ...options,
85
+ from: prevState.current,
86
+ to: newValue,
87
+ onUpdate: (value) => {
88
+ privateSignal.update(s => ({ ...s, current: value as T }));
89
+ if (options.onUpdate) {
90
+ options.onUpdate(value as T);
91
+ }
92
+ },
93
+ });
94
+ }
95
+
96
+ const fn = function() {
97
+ return privateSignal().current
98
+ }
99
+
100
+ for (const key in publicSignal) {
101
+ fn[key] = publicSignal[key]
102
+ }
103
+
104
+ fn.animatedState = privateSignal
105
+ fn.update = (updater: (value: T) => any) => {
106
+ animatedSignal(updater(privateSignal().current));
107
+ }
108
+ fn.set = (newValue: T) => {
109
+ animatedSignal(newValue);
110
+ }
111
+
112
+ return fn as any
113
+ }
@@ -0,0 +1,19 @@
1
+ import { ComponentFunction, h } from "./signal";
2
+
3
+ /**
4
+ * Bootstraps a canvas element and renders it to the DOM.
5
+ *
6
+ * @param rootElement - The HTML element where the canvas will be rendered. Can be null.
7
+ * @param canvas - A Promise that resolves to an Element representing the canvas component.
8
+ * @returns A Promise that resolves to the rendered canvas element.
9
+ * @throws {Error} If the provided element is not a Canvas component.
10
+ */
11
+ export const bootstrapCanvas = async (rootElement: HTMLElement | null, canvas: ComponentFunction<any>) => {
12
+ const canvasElement = await h(canvas);
13
+ if (canvasElement.tag != 'Canvas') {
14
+ throw new Error('Canvas is required');
15
+ }
16
+ (canvasElement as any).render(rootElement);
17
+
18
+ return canvasElement;
19
+ };
@@ -0,0 +1,23 @@
1
+ import { Element } from "./reactive"
2
+
3
+ export const directives: { [key: string]: any } = {}
4
+
5
+ export abstract class Directive {
6
+ abstract onDestroy();
7
+ abstract onInit(element: Element<any>);
8
+ abstract onMount(element: Element<any>);
9
+ abstract onUpdate(props: any);
10
+ }
11
+
12
+ export function registerDirective(name: string, directive: any) {
13
+ directives[name] = directive
14
+ }
15
+
16
+ export function applyDirective(element: Element, directiveName: string) {
17
+ if (!directives[directiveName]) {
18
+ return null
19
+ }
20
+ const directive = new directives[directiveName]()
21
+ directive.onInit?.(element)
22
+ return directive
23
+ }