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.
- package/.cursorrules +0 -0
- package/.github/workflows/ci.yml +29 -0
- package/README.md +79 -0
- package/dist/compiler/vite.js +119 -0
- package/dist/compiler/vite.js.map +1 -0
- package/dist/index.d.ts +846 -0
- package/dist/index.js +3340 -0
- package/dist/index.js.map +1 -0
- package/index.d.ts +6 -0
- package/logo.png +0 -0
- package/package.json +84 -18
- package/src/compiler/grammar.pegjs +180 -0
- package/src/compiler/vite.ts +166 -0
- package/src/components/Canvas.ts +134 -0
- package/src/components/Container.ts +46 -0
- package/src/components/DisplayObject.ts +458 -0
- package/src/components/DrawMap/index.ts +65 -0
- package/src/components/Graphic.ts +147 -0
- package/src/components/NineSliceSprite.ts +46 -0
- package/src/components/ParticleEmitter.ts +39 -0
- package/src/components/Scene.ts +6 -0
- package/src/components/Sprite.ts +493 -0
- package/src/components/Text.ts +145 -0
- package/src/components/Tilemap/Tile.ts +79 -0
- package/src/components/Tilemap/TileGroup.ts +207 -0
- package/src/components/Tilemap/TileLayer.ts +163 -0
- package/src/components/Tilemap/TileSet.ts +41 -0
- package/src/components/Tilemap/index.ts +80 -0
- package/src/components/TilingSprite.ts +39 -0
- package/src/components/Viewport.ts +159 -0
- package/src/components/index.ts +12 -0
- package/src/components/types/DisplayObject.ts +68 -0
- package/src/components/types/MouseEvent.ts +3 -0
- package/src/components/types/Spritesheet.ts +389 -0
- package/src/components/types/index.ts +4 -0
- package/src/directives/Drag.ts +84 -0
- package/src/directives/KeyboardControls.ts +922 -0
- package/src/directives/Scheduler.ts +112 -0
- package/src/directives/Sound.ts +91 -0
- package/src/directives/Transition.ts +45 -0
- package/src/directives/ViewportCull.ts +40 -0
- package/src/directives/ViewportFollow.ts +26 -0
- package/src/directives/index.ts +7 -0
- package/src/engine/animation.ts +113 -0
- package/src/engine/bootstrap.ts +19 -0
- package/src/engine/directive.ts +23 -0
- package/src/engine/reactive.ts +379 -0
- package/src/engine/signal.ts +138 -0
- package/src/engine/trigger.ts +40 -0
- package/src/engine/utils.ts +135 -0
- package/src/hooks/addContext.ts +6 -0
- package/src/hooks/useProps.ts +155 -0
- package/src/hooks/useRef.ts +21 -0
- package/src/index.ts +14 -0
- package/src/presets/Bar.ts +89 -0
- package/src/presets/Button.ts +0 -0
- package/src/presets/Joystick.ts +286 -0
- package/src/presets/NightAmbiant.ts +122 -0
- package/src/presets/Particle.ts +53 -0
- package/src/utils/Ease.ts +33 -0
- package/src/utils/RadialGradient.ts +86 -0
- package/starter/assets/logo.png +0 -0
- package/starter/components/app.ce +18 -0
- package/starter/components/hello.ce +35 -0
- package/starter/index.html +21 -0
- package/starter/main.ts +6 -0
- package/starter/package.json +20 -0
- package/starter/tsconfig.json +32 -0
- package/starter/vite.config.ts +12 -0
- package/tsconfig.json +32 -0
- package/tsconfig.node.json +10 -0
- package/tsup.config.ts +28 -0
- package/vitest.config.ts +12 -0
- package/.gitattributes +0 -22
- package/.npmignore +0 -163
- package/canvasengine-1.3.0.all.min.js +0 -21
- package/canvasengine.js +0 -5802
- package/core/DB.js +0 -24
- package/core/ModelServer.js +0 -348
- package/core/Users.js +0 -190
- package/core/engine-common.js +0 -952
- package/doc/cocoonjs.md +0 -36
- package/doc/doc-lang.yml +0 -43
- package/doc/doc-router.yml +0 -14
- package/doc/doc-tuto.yml +0 -9
- package/doc/doc.yml +0 -39
- package/doc/element.md +0 -37
- package/doc/entity.md +0 -90
- package/doc/extend.md +0 -47
- package/doc/get_started.md +0 -19
- package/doc/images/entity.png +0 -0
- package/doc/multitouch.md +0 -58
- package/doc/nodejs.md +0 -142
- package/doc/scene.md +0 -44
- package/doc/text.md +0 -156
- package/examples/server/client.html +0 -31
- package/examples/server/server.js +0 -16
- package/examples/tiled_server/client.html +0 -52
- package/examples/tiled_server/images/tiles_spritesheet.png +0 -0
- package/examples/tiled_server/server/map.json +0 -50
- package/examples/tiled_server/server/map.tmx +0 -16
- package/examples/tiled_server/server/server.js +0 -16
- package/extends/Animation.js +0 -910
- package/extends/Effect.js +0 -252
- package/extends/Gleed2d.js +0 -252
- package/extends/Hit.js +0 -1509
- package/extends/Input.js +0 -699
- package/extends/Marshal.js +0 -716
- package/extends/Scrolling.js +0 -388
- package/extends/Soundmanager2.js +0 -5466
- package/extends/Spritesheet.js +0 -196
- package/extends/Text.js +0 -366
- package/extends/Tiled.js +0 -403
- package/extends/Window.js +0 -575
- package/extends/gamepad.js +0 -397
- package/extends/socket.io.min.js +0 -2
- package/extends/swf/soundmanager2.swf +0 -0
- package/extends/swf/soundmanager2_debug.swf +0 -0
- package/extends/swf/soundmanager2_flash9.swf +0 -0
- package/extends/swf/soundmanager2_flash9_debug.swf +0 -0
- package/extends/swf/soundmanager2_flash_xdomain.zip +0 -0
- package/extends/workers/transition.js +0 -43
- package/index.js +0 -46
- package/license.txt +0 -19
- 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,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
|
+
}
|