canvasengine 1.3.0 → 2.0.0-beta.2

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 (96) hide show
  1. package/package.json +51 -17
  2. package/src/components/Canvas.ts +134 -0
  3. package/src/components/Container.ts +46 -0
  4. package/src/components/DisplayObject.ts +458 -0
  5. package/src/components/DrawMap/index.ts +65 -0
  6. package/src/components/Graphic.ts +147 -0
  7. package/src/components/NineSliceSprite.ts +46 -0
  8. package/src/components/ParticleEmitter.ts +39 -0
  9. package/src/components/Scene.ts +6 -0
  10. package/src/components/Sprite.ts +493 -0
  11. package/src/components/Text.ts +145 -0
  12. package/src/components/Tilemap/Tile.ts +79 -0
  13. package/src/components/Tilemap/TileGroup.ts +207 -0
  14. package/src/components/Tilemap/TileLayer.ts +163 -0
  15. package/src/components/Tilemap/TileSet.ts +41 -0
  16. package/src/components/Tilemap/index.ts +80 -0
  17. package/src/components/TilingSprite.ts +39 -0
  18. package/src/components/Viewport.ts +159 -0
  19. package/src/components/index.ts +13 -0
  20. package/src/components/types/DisplayObject.ts +69 -0
  21. package/src/components/types/MouseEvent.ts +3 -0
  22. package/src/components/types/Spritesheet.ts +389 -0
  23. package/src/components/types/index.ts +4 -0
  24. package/src/directives/Drag.ts +84 -0
  25. package/src/directives/KeyboardControls.ts +922 -0
  26. package/src/directives/Scheduler.ts +101 -0
  27. package/src/directives/Sound.ts +91 -0
  28. package/src/directives/Transition.ts +45 -0
  29. package/src/directives/ViewportCull.ts +40 -0
  30. package/src/directives/ViewportFollow.ts +26 -0
  31. package/src/directives/index.ts +7 -0
  32. package/src/engine/animation.ts +113 -0
  33. package/src/engine/bootstrap.ts +19 -0
  34. package/src/engine/directive.ts +23 -0
  35. package/src/engine/reactive.ts +379 -0
  36. package/src/engine/signal.ts +138 -0
  37. package/src/engine/trigger.ts +40 -0
  38. package/src/engine/utils.ts +135 -0
  39. package/src/hooks/addContext.ts +6 -0
  40. package/src/hooks/useProps.ts +155 -0
  41. package/src/hooks/useRef.ts +21 -0
  42. package/src/index.ts +13 -0
  43. package/src/utils/Ease.ts +33 -0
  44. package/src/utils/RadialGradient.ts +86 -0
  45. package/.gitattributes +0 -22
  46. package/.npmignore +0 -163
  47. package/canvasengine-1.3.0.all.min.js +0 -21
  48. package/canvasengine.js +0 -5802
  49. package/core/DB.js +0 -24
  50. package/core/ModelServer.js +0 -348
  51. package/core/Users.js +0 -190
  52. package/core/engine-common.js +0 -952
  53. package/doc/cocoonjs.md +0 -36
  54. package/doc/doc-lang.yml +0 -43
  55. package/doc/doc-router.yml +0 -14
  56. package/doc/doc-tuto.yml +0 -9
  57. package/doc/doc.yml +0 -39
  58. package/doc/element.md +0 -37
  59. package/doc/entity.md +0 -90
  60. package/doc/extend.md +0 -47
  61. package/doc/get_started.md +0 -19
  62. package/doc/images/entity.png +0 -0
  63. package/doc/multitouch.md +0 -58
  64. package/doc/nodejs.md +0 -142
  65. package/doc/scene.md +0 -44
  66. package/doc/text.md +0 -156
  67. package/examples/server/client.html +0 -31
  68. package/examples/server/server.js +0 -16
  69. package/examples/tiled_server/client.html +0 -52
  70. package/examples/tiled_server/images/tiles_spritesheet.png +0 -0
  71. package/examples/tiled_server/server/map.json +0 -50
  72. package/examples/tiled_server/server/map.tmx +0 -16
  73. package/examples/tiled_server/server/server.js +0 -16
  74. package/extends/Animation.js +0 -910
  75. package/extends/Effect.js +0 -252
  76. package/extends/Gleed2d.js +0 -252
  77. package/extends/Hit.js +0 -1509
  78. package/extends/Input.js +0 -699
  79. package/extends/Marshal.js +0 -716
  80. package/extends/Scrolling.js +0 -388
  81. package/extends/Soundmanager2.js +0 -5466
  82. package/extends/Spritesheet.js +0 -196
  83. package/extends/Text.js +0 -366
  84. package/extends/Tiled.js +0 -403
  85. package/extends/Window.js +0 -575
  86. package/extends/gamepad.js +0 -397
  87. package/extends/socket.io.min.js +0 -2
  88. package/extends/swf/soundmanager2.swf +0 -0
  89. package/extends/swf/soundmanager2_debug.swf +0 -0
  90. package/extends/swf/soundmanager2_flash9.swf +0 -0
  91. package/extends/swf/soundmanager2_flash9_debug.swf +0 -0
  92. package/extends/swf/soundmanager2_flash_xdomain.zip +0 -0
  93. package/extends/workers/transition.js +0 -43
  94. package/index.js +0 -46
  95. package/license.txt +0 -19
  96. package/readme.md +0 -483
@@ -0,0 +1,101 @@
1
+ import { WritableSignal } from '@signe/reactive';
2
+ import { Directive, registerDirective } from '../engine/directive';
3
+ import { Element } from '../engine/reactive';
4
+ import * as Utils from '../engine/utils';
5
+
6
+ export interface Tick {
7
+ timestamp: number
8
+ deltaTime: number
9
+ frame: number
10
+ deltaRatio: number
11
+ }
12
+
13
+ export class Scheduler extends Directive {
14
+ private maxFps?: number
15
+ private fps: number = 60
16
+ private deltaTime: number = 0
17
+ public frame: number = 0
18
+ private timestamp: number = 0
19
+ private requestedDelay: number = 0
20
+ private lastTimestamp: number = 0
21
+ private _stop: boolean = false
22
+ private tick: WritableSignal<Tick | null>
23
+
24
+ onInit(element: Element) {
25
+ this.tick = element.propObservables?.tick as any
26
+ }
27
+
28
+ onDestroy() { }
29
+ onMount(element: Element) { }
30
+ onUpdate(props: any) { }
31
+
32
+ nextTick(timestamp: number) {
33
+ this.lastTimestamp = this.lastTimestamp || this.timestamp // first
34
+ this.deltaTime = Utils.preciseNow() - this.timestamp
35
+ this.timestamp = timestamp
36
+ this.tick.set({
37
+ timestamp: this.timestamp,
38
+ deltaTime: this.deltaTime,
39
+ frame: this.frame,
40
+ deltaRatio: ~~this.deltaTime / ~~Utils.fps2ms(this.fps)
41
+ })
42
+ this.lastTimestamp = this.timestamp
43
+ this.frame++
44
+ }
45
+ /**
46
+ * start the schedule
47
+ * @return {Scheduler} returns this scheduler instance
48
+ */
49
+ start(options: {
50
+ maxFps?: number
51
+ fps?: number,
52
+ delay?: number
53
+ } = {}) {
54
+ if (options.maxFps) this.maxFps = options.maxFps
55
+ if (options.fps) this.fps = options.fps
56
+ if (options.delay) this.requestedDelay = options.delay
57
+ const requestAnimationFrame = (fn: (timestamp: number) => void) => {
58
+ if (Utils.isBrowser()) {
59
+ window.requestAnimationFrame(fn.bind(this))
60
+ }
61
+ else {
62
+ setTimeout(() => {
63
+ this.requestedDelay = 0
64
+ fn(Utils.preciseNow())
65
+ }, Utils.fps2ms(this.fps) + this.requestedDelay)
66
+ }
67
+ }
68
+
69
+ if (!this.maxFps) {
70
+ const loop = (timestamp: number) => {
71
+ requestAnimationFrame(loop)
72
+ this.nextTick(timestamp)
73
+ }
74
+ requestAnimationFrame(loop)
75
+ }
76
+ else {
77
+ const msInterval = Utils.fps2ms(this.maxFps)
78
+ let now = Utils.preciseNow()
79
+ let then = Utils.preciseNow()
80
+ const loop = (timestamp: number) => {
81
+ if (this._stop) return
82
+ requestAnimationFrame(loop)
83
+ now = Utils.preciseNow()
84
+ const elapsed = now - then
85
+ if (elapsed > msInterval) {
86
+ then = now - (elapsed % msInterval)
87
+ this.nextTick(timestamp)
88
+ }
89
+ }
90
+ requestAnimationFrame(loop)
91
+ }
92
+
93
+ return this;
94
+ }
95
+
96
+ stop() {
97
+ this._stop = true
98
+ }
99
+ }
100
+
101
+ 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
+ }