canvasengine 2.0.0-beta.4 → 2.0.0-beta.6

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/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import { Subscription, Subject, Observable } from 'rxjs';
5
5
  export { isObservable } from 'rxjs';
6
6
  import { Node } from 'yoga-layout';
7
7
  import * as PIXI from 'pixi.js';
8
- import { ObservablePoint, Graphics as Graphics$1, TextStyle, Texture, Matrix } from 'pixi.js';
8
+ import { ObservablePoint, Graphics as Graphics$1, Texture, TextStyle, Matrix } from 'pixi.js';
9
9
  export { Howler } from 'howler';
10
10
  import * as popmotion from 'popmotion';
11
11
 
@@ -54,6 +54,7 @@ type AlignContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'sp
54
54
  type Size = number | `${number}%`;
55
55
  type EdgeSize = SignalOrPrimitive<Size | [Size, Size] | [Size, Size, Size, Size]>;
56
56
  interface DisplayObjectProps {
57
+ attach?: any;
57
58
  ref?: string;
58
59
  x?: SignalOrPrimitive<number>;
59
60
  y?: SignalOrPrimitive<number>;
@@ -251,7 +252,14 @@ declare function createComponent(tag: string, props?: Props): Element;
251
252
  * @returns {Observable} An observable that emits the list of created child elements.
252
253
  */
253
254
  declare function loop<T = any>(itemsSubject: WritableArraySignal<T>, createElementFn: (item: any, index: number) => Element | Promise<Element>): FlowObservable;
254
- declare function cond(condition: Signal, createElementFn: () => Element | Promise<Element>): FlowObservable;
255
+ /**
256
+ * Conditionally creates and destroys elements based on a condition signal.
257
+ *
258
+ * @param {Signal<boolean> | boolean} condition - A signal or boolean that determines whether to create an element.
259
+ * @param {Function} createElementFn - A function that returns an element or a promise that resolves to an element.
260
+ * @returns {Observable} An observable that emits the created or destroyed element.
261
+ */
262
+ declare function cond(condition: Signal<boolean> | boolean, createElementFn: () => Element | Promise<Element>): FlowObservable;
255
263
 
256
264
  declare abstract class Directive {
257
265
  abstract onDestroy(): any;
@@ -749,10 +757,26 @@ interface SpritePropsWithSheet extends Omit<SpriteProps, "image" | "rectangle">
749
757
  params?: any;
750
758
  onFinish?: () => void;
751
759
  };
760
+ loader?: {
761
+ onProgress?: (progress: number) => void;
762
+ onComplete?: (texture: Texture) => void;
763
+ };
752
764
  }
753
765
  type SpritePropTypes = SpritePropsWithImage | SpritePropsWithSheet;
754
766
  declare const Sprite: ComponentFunction<SpritePropTypes>;
755
767
 
768
+ interface VideoProps {
769
+ src: string;
770
+ paused?: boolean;
771
+ loop?: boolean;
772
+ muted?: boolean;
773
+ loader?: {
774
+ onComplete?: (texture: Texture) => void;
775
+ onProgress?: (progress: number) => void;
776
+ };
777
+ }
778
+ declare function Video(props: VideoProps): Element<ComponentInstance> | Promise<Element<ComponentInstance>>;
779
+
756
780
  interface TextProps extends DisplayObjectProps {
757
781
  text?: string;
758
782
  style?: Partial<TextStyle>;
@@ -1082,4 +1106,4 @@ declare namespace utils {
1082
1106
  export { utils_arrayEquals as arrayEquals, utils_calculateDistance as calculateDistance, utils_error as error, utils_fps2ms as fps2ms, utils_get as get, utils_isBrowser as isBrowser, utils_isFunction as isFunction, utils_isObject as isObject, utils_isPromise as isPromise, utils_log as log, utils_preciseNow as preciseNow, utils_set as set, utils_setObservablePoint as setObservablePoint };
1083
1107
  }
1084
1108
 
1085
- export { type AnimatedSignal, type AnimatedState, type ArrayChange, Canvas, Circle, type ComponentFunction, type ComponentInstance, Container, DisplayObject, EVENTS, Easing, type Element, Ellipse, Graphics, NineSliceSprite, ParticlesEmitter, type Props, RadialGradient, Rect, Scene, Sprite, Text, TilingSprite, Triangle, utils as Utils, Viewport, animatedSignal, bootstrapCanvas, cond, createComponent, currentSubscriptionsTracker, h, isAnimatedSignal, isElement, isPrimitive, isTrigger, loop, mount, mountTracker, on, registerComponent, Svg as svg, tick, trigger, useDefineProps, useProps };
1109
+ export { type AnimatedSignal, type AnimatedState, type ArrayChange, Canvas, Circle, type ComponentFunction, type ComponentInstance, Container, DisplayObject, EVENTS, Easing, type Element, Ellipse, Graphics, NineSliceSprite, ParticlesEmitter, type Props, RadialGradient, Rect, Scene, Sprite, Text, TilingSprite, Triangle, utils as Utils, Video, Viewport, animatedSignal, bootstrapCanvas, cond, createComponent, currentSubscriptionsTracker, h, isAnimatedSignal, isElement, isPrimitive, isTrigger, loop, mount, mountTracker, on, registerComponent, Svg as svg, tick, trigger, useDefineProps, useProps };
package/dist/index.js CHANGED
@@ -1115,8 +1115,7 @@ import {
1115
1115
  defer,
1116
1116
  from,
1117
1117
  map,
1118
- of,
1119
- switchMap
1118
+ of
1120
1119
  } from "rxjs";
1121
1120
  var components = {};
1122
1121
  var isElement = (value) => {
@@ -1209,8 +1208,20 @@ function createComponent(tag, props) {
1209
1208
  recursiveProps(props);
1210
1209
  }
1211
1210
  instance.onInit?.(element.props);
1212
- instance.onUpdate?.(element.props);
1213
- const onMount = (parent, element2, index) => {
1211
+ const elementsListen = new Subject();
1212
+ if (props?.isRoot) {
1213
+ element.allElements = elementsListen;
1214
+ element.props.context.rootElement = element;
1215
+ element.componentInstance.onMount?.(element);
1216
+ propagateContext(element);
1217
+ }
1218
+ if (props) {
1219
+ for (let key in props) {
1220
+ const directive = applyDirective(element, key);
1221
+ if (directive) element.directives[key] = directive;
1222
+ }
1223
+ }
1224
+ function onMount(parent, element2, index) {
1214
1225
  element2.props.context = parent.props.context;
1215
1226
  element2.parent = parent;
1216
1227
  element2.componentInstance.onMount?.(element2, index);
@@ -1220,78 +1231,76 @@ function createComponent(tag, props) {
1220
1231
  element2.effectMounts.forEach((fn) => {
1221
1232
  element2.effectUnmounts.push(fn(element2));
1222
1233
  });
1223
- };
1224
- const elementsListen = new Subject();
1225
- if (props?.isRoot) {
1226
- const propagateContext = async (element2) => {
1227
- if (element2.props.attach) {
1228
- const isReactiveAttach = isSignal2(element2.propObservables?.attach);
1229
- if (!isReactiveAttach) {
1230
- element2.props.children.push(element2.props.attach);
1231
- } else {
1234
+ }
1235
+ ;
1236
+ async function propagateContext(element2) {
1237
+ if (element2.props.attach) {
1238
+ const isReactiveAttach = isSignal2(element2.propObservables?.attach);
1239
+ if (!isReactiveAttach) {
1240
+ element2.props.children.push(element2.props.attach);
1241
+ } else {
1242
+ await new Promise((resolve) => {
1232
1243
  let lastElement = null;
1233
- element2.propObservables.attach.observable.subscribe(({ value, type }) => {
1234
- if (type != "init") {
1244
+ element2.propSubscriptions.push(element2.propObservables.attach.observable.subscribe(async (args) => {
1245
+ const value = args?.value ?? args;
1246
+ if (!value) {
1247
+ throw new Error(`attach in ${element2.tag} is undefined or null, add a component`);
1248
+ }
1249
+ if (lastElement) {
1235
1250
  destroyElement(lastElement);
1236
1251
  }
1237
1252
  lastElement = value;
1238
- onMount(element2, value);
1239
- propagateContext(value);
1240
- });
1241
- }
1242
- }
1243
- if (!element2.props.children) {
1244
- return;
1253
+ await createElement(element2, value);
1254
+ resolve(void 0);
1255
+ }));
1256
+ });
1245
1257
  }
1246
- for (let child of element2.props.children) {
1247
- if (!child) continue;
1248
- if (isPromise(child)) {
1249
- child = await child;
1250
- }
1251
- if (child instanceof Observable) {
1252
- child.subscribe(
1253
- ({
1254
- elements: comp,
1255
- prev
1256
- }) => {
1257
- const components2 = comp.filter((c) => c !== null);
1258
- if (prev) {
1259
- components2.forEach((c) => {
1260
- const index = element2.props.children.indexOf(prev.props.key);
1261
- onMount(element2, c, index + 1);
1262
- propagateContext(c);
1263
- });
1264
- return;
1265
- }
1266
- components2.forEach((component) => {
1267
- if (!Array.isArray(component)) {
1268
- onMount(element2, component);
1269
- propagateContext(component);
1270
- } else {
1271
- component.forEach((comp2) => {
1272
- onMount(element2, comp2);
1273
- propagateContext(comp2);
1274
- });
1275
- }
1258
+ }
1259
+ if (!element2.props.children) {
1260
+ return;
1261
+ }
1262
+ for (let child of element2.props.children) {
1263
+ if (!child) continue;
1264
+ await createElement(element2, child);
1265
+ }
1266
+ }
1267
+ ;
1268
+ async function createElement(parent, child) {
1269
+ if (isPromise(child)) {
1270
+ child = await child;
1271
+ }
1272
+ if (child instanceof Observable) {
1273
+ child.subscribe(
1274
+ ({
1275
+ elements: comp,
1276
+ prev
1277
+ }) => {
1278
+ const components2 = comp.filter((c) => c !== null);
1279
+ if (prev) {
1280
+ components2.forEach((c) => {
1281
+ const index = parent.props.children.indexOf(prev.props.key);
1282
+ onMount(parent, c, index + 1);
1283
+ propagateContext(c);
1284
+ });
1285
+ return;
1286
+ }
1287
+ components2.forEach((component) => {
1288
+ if (!Array.isArray(component)) {
1289
+ onMount(parent, component);
1290
+ propagateContext(component);
1291
+ } else {
1292
+ component.forEach((comp2) => {
1293
+ onMount(parent, comp2);
1294
+ propagateContext(comp2);
1276
1295
  });
1277
- elementsListen.next(void 0);
1278
1296
  }
1279
- );
1280
- } else {
1281
- onMount(element2, child);
1282
- await propagateContext(child);
1297
+ });
1298
+ elementsListen.next(void 0);
1283
1299
  }
1284
- }
1285
- };
1286
- element.allElements = elementsListen;
1287
- element.props.context.rootElement = element;
1288
- element.componentInstance.onMount?.(element);
1289
- propagateContext(element);
1290
- }
1291
- if (props) {
1292
- for (let key in props) {
1293
- const directive = applyDirective(element, key);
1294
- if (directive) element.directives[key] = directive;
1300
+ );
1301
+ } else {
1302
+ onMount(parent, child);
1303
+ await propagateContext(child);
1295
1304
  }
1296
1305
  }
1297
1306
  return element;
@@ -1363,34 +1372,59 @@ function loop(itemsSubject, createElementFn) {
1363
1372
  }
1364
1373
  function cond(condition, createElementFn) {
1365
1374
  let element = null;
1366
- return condition.observable.pipe(
1367
- switchMap((bool) => {
1368
- if (bool) {
1369
- let _el = createElementFn();
1370
- if (isPromise(_el)) {
1371
- return from(_el).pipe(
1372
- map((el) => {
1373
- element = _el;
1374
- return {
1375
+ if (isSignal2(condition)) {
1376
+ const signalCondition = condition;
1377
+ return new Observable((subscriber) => {
1378
+ return signalCondition.observable.subscribe((bool) => {
1379
+ if (bool) {
1380
+ let _el = createElementFn();
1381
+ if (isPromise(_el)) {
1382
+ from(_el).subscribe((el) => {
1383
+ element = el;
1384
+ subscriber.next({
1375
1385
  type: "init",
1376
1386
  elements: [el]
1377
- };
1378
- })
1379
- );
1387
+ });
1388
+ });
1389
+ } else {
1390
+ element = _el;
1391
+ subscriber.next({
1392
+ type: "init",
1393
+ elements: [element]
1394
+ });
1395
+ }
1396
+ } else if (element) {
1397
+ destroyElement(element);
1398
+ subscriber.next({
1399
+ elements: []
1400
+ });
1401
+ } else {
1402
+ subscriber.next({
1403
+ elements: []
1404
+ });
1380
1405
  }
1381
- element = _el;
1382
- return of({
1383
- type: "init",
1384
- elements: [element]
1385
- });
1386
- } else if (element) {
1387
- destroyElement(element);
1406
+ });
1407
+ });
1408
+ } else {
1409
+ if (condition) {
1410
+ let _el = createElementFn();
1411
+ if (isPromise(_el)) {
1412
+ return from(_el).pipe(
1413
+ map((el) => ({
1414
+ type: "init",
1415
+ elements: [el]
1416
+ }))
1417
+ );
1388
1418
  }
1389
1419
  return of({
1390
- elements: []
1420
+ type: "init",
1421
+ elements: [_el]
1391
1422
  });
1392
- })
1393
- );
1423
+ }
1424
+ return of({
1425
+ elements: []
1426
+ });
1427
+ }
1394
1428
  }
1395
1429
 
1396
1430
  // src/hooks/useProps.ts
@@ -2168,8 +2202,8 @@ import {
2168
2202
  // src/engine/animation.ts
2169
2203
  import { effect as effect6, signal as signal4 } from "@signe/reactive";
2170
2204
  import { animate as animatePopmotion } from "popmotion";
2171
- function isAnimatedSignal(signal6) {
2172
- return signal6.animatedState !== void 0;
2205
+ function isAnimatedSignal(signal7) {
2206
+ return signal7.animatedState !== void 0;
2173
2207
  }
2174
2208
  function animatedSignal(initialValue, options = {}) {
2175
2209
  const state = {
@@ -2374,6 +2408,21 @@ var CanvasSprite = class extends DisplayObject(PixiSprite) {
2374
2408
  }
2375
2409
  async onUpdate(props) {
2376
2410
  super.onUpdate(props);
2411
+ const setTexture = async (image) => {
2412
+ const onProgress = this.fullProps.loader?.onProgress;
2413
+ const texture = await Assets.load(image, (progress) => {
2414
+ if (onProgress) onProgress(progress);
2415
+ if (progress == 1) {
2416
+ const onComplete = this.fullProps.loader?.onComplete;
2417
+ if (onComplete) {
2418
+ setTimeout(() => {
2419
+ onComplete(texture);
2420
+ });
2421
+ }
2422
+ }
2423
+ });
2424
+ return texture;
2425
+ };
2377
2426
  const sheet = props.sheet;
2378
2427
  if (sheet?.params) this.sheetParams = sheet?.params;
2379
2428
  if (sheet?.playing && this.isMounted) {
@@ -2383,13 +2432,13 @@ var CanvasSprite = class extends DisplayObject(PixiSprite) {
2383
2432
  if (props.hitbox) this.hitbox = props.hitbox;
2384
2433
  if (props.scaleMode) this.baseTexture.scaleMode = props.scaleMode;
2385
2434
  else if (props.image && this.fullProps.rectangle === void 0) {
2386
- this.texture = await Assets.load(this.fullProps.image);
2435
+ this.texture = await setTexture(this.fullProps.image);
2387
2436
  } else if (props.texture) {
2388
2437
  this.texture = props.texture;
2389
2438
  }
2390
2439
  if (props.rectangle !== void 0) {
2391
2440
  const { x, y, width, height } = props.rectangle?.value ?? props.rectangle;
2392
- const texture = await Assets.load(this.fullProps.image);
2441
+ const texture = await setTexture(this.fullProps.image);
2393
2442
  this.texture = new Texture2({
2394
2443
  source: texture.source,
2395
2444
  frame: new Rectangle2(x, y, width, height)
@@ -2530,16 +2579,108 @@ var Sprite2 = (props) => {
2530
2579
  return createComponent("Sprite", props);
2531
2580
  };
2532
2581
 
2582
+ // src/components/Video.ts
2583
+ import { effect as effect8, signal as signal5 } from "@signe/reactive";
2584
+ function Video(props) {
2585
+ const eventsMap = {
2586
+ audioprocess: null,
2587
+ canplay: null,
2588
+ canplaythrough: null,
2589
+ complete: null,
2590
+ durationchange: null,
2591
+ emptied: null,
2592
+ ended: null,
2593
+ loadeddata: null,
2594
+ loadedmetadata: null,
2595
+ pause: null,
2596
+ play: null,
2597
+ playing: null,
2598
+ progress: null,
2599
+ ratechange: null,
2600
+ seeked: null,
2601
+ seeking: null,
2602
+ stalled: null,
2603
+ suspend: null,
2604
+ timeupdate: null,
2605
+ volumechange: null,
2606
+ waiting: null
2607
+ };
2608
+ const video = signal5(null);
2609
+ const defineProps = useDefineProps(props);
2610
+ const { play, loop: loop2, muted } = defineProps({
2611
+ play: {
2612
+ type: Boolean,
2613
+ default: true
2614
+ },
2615
+ loop: {
2616
+ type: Boolean,
2617
+ default: false
2618
+ },
2619
+ muted: {
2620
+ type: Boolean,
2621
+ default: false
2622
+ }
2623
+ });
2624
+ effect8(() => {
2625
+ const _video = video();
2626
+ const state = play();
2627
+ if (_video && state !== void 0) {
2628
+ if (state) {
2629
+ _video.play();
2630
+ } else {
2631
+ _video.pause();
2632
+ }
2633
+ }
2634
+ if (_video && loop2()) {
2635
+ _video.loop = loop2();
2636
+ }
2637
+ if (_video && muted()) {
2638
+ _video.muted = muted();
2639
+ }
2640
+ });
2641
+ mount(() => {
2642
+ return () => {
2643
+ for (let event in eventsMap) {
2644
+ if (eventsMap[event]) {
2645
+ video().removeEventListener(event, eventsMap[event]);
2646
+ }
2647
+ }
2648
+ };
2649
+ });
2650
+ return h(Sprite2, {
2651
+ ...props,
2652
+ image: props.src,
2653
+ loader: {
2654
+ onComplete: (texture) => {
2655
+ const source = texture.source.resource;
2656
+ video.set(source);
2657
+ if (props?.loader?.onComplete) {
2658
+ props.loader.onComplete(texture);
2659
+ }
2660
+ for (let event in eventsMap) {
2661
+ if (props[event]) {
2662
+ const cb = (ev) => {
2663
+ props[event](ev);
2664
+ };
2665
+ eventsMap[event] = cb;
2666
+ source.addEventListener(event, cb);
2667
+ }
2668
+ }
2669
+ }
2670
+ }
2671
+ });
2672
+ }
2673
+
2533
2674
  // src/components/Text.ts
2534
2675
  import { Text as PixiText } from "pixi.js";
2535
2676
 
2536
2677
  // src/engine/trigger.ts
2537
- import { effect as effect8, signal as signal5 } from "@signe/reactive";
2678
+ import { effect as effect9, signal as signal6 } from "@signe/reactive";
2538
2679
  function isTrigger(arg) {
2539
2680
  return arg?.start && arg?.listen;
2540
2681
  }
2541
2682
  function trigger(globalConfig) {
2542
- const _signal = signal5({
2683
+ const _signal = signal6({
2543
2684
  config: globalConfig,
2544
2685
  value: 0,
2545
2686
  resolve: (value) => void 0
@@ -2569,7 +2710,7 @@ function on(triggerSignal, callback) {
2569
2710
  if (!isTrigger(triggerSignal)) {
2570
2711
  throw new Error("In 'on(arg)' must have a trigger signal type");
2571
2712
  }
2572
- effect8(() => {
2713
+ effect9(() => {
2573
2714
  const result = triggerSignal.listen();
2574
2715
  if (result?.seed.value) {
2575
2716
  const ret = callback(result?.seed.config);
@@ -2716,7 +2857,7 @@ function TilingSprite(props) {
2716
2857
 
2717
2858
  // src/components/Viewport.ts
2718
2859
  import { Viewport as PixiViewport } from "pixi-viewport";
2719
- import { effect as effect9 } from "@signe/reactive";
2860
+ import { effect as effect10 } from "@signe/reactive";
2720
2861
  var EVENTS3 = [
2721
2862
  "bounce-x-end",
2722
2863
  "bounce-x-start",
@@ -2767,7 +2908,7 @@ var CanvasViewport = class extends DisplayObject(PixiViewport) {
2767
2908
  super.onMount(element);
2768
2909
  const { tick: tick2, renderer, canvasSize } = element.props.context;
2769
2910
  let isDragging = false;
2770
- effect9(() => {
2911
+ effect10(() => {
2771
2912
  this.screenWidth = canvasSize().width;
2772
2913
  this.screenHeight = canvasSize().height;
2773
2914
  });
@@ -3016,6 +3157,7 @@ export {
3016
3157
  TilingSprite,
3017
3158
  Triangle,
3018
3159
  utils_exports as Utils,
3160
+ Video,
3019
3161
  Viewport,
3020
3162
  animatedSignal,
3021
3163
  bootstrapCanvas,