diff --git a/spine-ts/spine-webgl/example/webcomponent-tutorial.html b/spine-ts/spine-webgl/example/webcomponent-tutorial.html index a7fc75fb2..538bcaffd 100644 --- a/spine-ts/spine-webgl/example/webcomponent-tutorial.html +++ b/spine-ts/spine-webgl/example/webcomponent-tutorial.html @@ -1431,7 +1431,7 @@ skins.forEach((skin, i) => { By default, the callback does two things:
@@ -1449,7 +1449,7 @@ skins.forEach((skin, i) => { skeleton="assets/coin-pro.skel" animation="animation" manual-start - on-viewport-manual-start + on-screen-manual-start >
@@ -1981,7 +1981,7 @@ stretchyman.update = (canvas, delta, skeleton, state) => { 2) For scrollable containers, the widget will overflow the container bounds until the widget html element container is visible
3) For fixed containers, the widget will scroll in a jerky way

- In order to fix this behaviour, it is necessary to insert a dedicated spine-overlay webcomponent as a direct child of the container. + In order to fix this behaviour, it is necessary to insert a dedicated spine-overlay webcomponent as a direct child of the container. Moreover, it is necessary to perform the following actions:

1) The scrollable container must have a transform css attribute. If it hasn't this attribute the spine-overlay will add it for you. diff --git a/spine-ts/spine-webgl/example/webcomponent-tutorial2.html b/spine-ts/spine-webgl/example/webcomponent-tutorial2.html deleted file mode 100644 index 4d5235678..000000000 --- a/spine-ts/spine-webgl/example/webcomponent-tutorial2.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - - - - Webcomponent Tutorial - - - - - - - - - - - - - -
- -
-
- -
- -
- -
-
- -
- - - - - - - - - - - - - - -/// -/// -/// -/// -/// -/// -/// - -
- -
- - - - - - - - - - - - - - \ No newline at end of file diff --git a/spine-ts/spine-webgl/src/SpineWebComponentWidget.ts b/spine-ts/spine-webgl/src/SpineWebComponentWidget.ts index d083e070b..fac052b24 100644 --- a/spine-ts/spine-webgl/src/SpineWebComponentWidget.ts +++ b/spine-ts/spine-webgl/src/SpineWebComponentWidget.ts @@ -126,7 +126,7 @@ interface WidgetAttributes { debug: boolean identifier: string manualStart: boolean - onViewportManualStart: boolean + onScreenManualStart: boolean pages?: Array clip: boolean offScreenUpdateBehaviour: OffScreenUpdateBehaviourType @@ -167,13 +167,6 @@ interface WidgetInternalProperties { export class SpineWebComponentWidget extends HTMLElement implements Disposable, WidgetAttributes, WidgetOverridableMethods, WidgetInternalProperties, Partial { - public worldX = 0; - public worldY = 0; - public cursorWorldX = 1; - public cursorWorldY = 1; - public jsonSkeletonKey?: string; - public onViewportManualStart = false; - // this promise in necessary only for manual start. Before calling manual start is necessary that the overlay has been assigned to the widget. // overlay assignment is asynchronous due to webcomponent promotion and dom load termination. // When manual start is false, loadSkeleton is invoked after the overlay is assigned. loadSkeleton needs the assetManager that is owned by the overlay. @@ -206,6 +199,12 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable, */ public skeletonPath?: string; + /** + * The name of the skeleton when the skeleton file is a JSON and contains multiple skeletons. + * Connected to `json-skeleton-key` attribute. + */ + public jsonSkeletonKey?: string; + /** * The scale passed to the Skeleton Loader. SkeletonData values will be scaled accordingly. * Default: 1 @@ -410,6 +409,30 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable, */ public isDraggable = false; + /** + * The x of the root relative to the canvas/webgl context center in spine world coordinates. + * This is an experimental property and might be removed in the future. + */ + public worldX = 0; + + /** + * The y of the root relative to the canvas/webgl context center in spine world coordinates. + * This is an experimental property and might be removed in the future. + */ + public worldY = 0; + + /** + * The x coordinate of the cursor relative to the cursor relative to the skeleton root in spine world coordinates. + * This is an experimental property and might be removed in the future. + */ + public cursorWorldX = 1; + + /** + * The x coordinate of the cursor relative to the cursor relative to the skeleton root in spine world coordinates. + * This is an experimental property and might be removed in the future. + */ + public cursorWorldY = 1; + /** * If true, some convenience elements are drawn to show the skeleton world origin (green), * the root (red), and the bounds rectangle (blue) @@ -430,6 +453,14 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable, */ public manualStart = false; + /** + * If true and manualStart is true, allows the default {@link onScreenFunction} to invoke the {@link start} method. + * This is useful when you want to load the assets only when the widget is revealed. + * By default, is false implying the start method to be invoked manually. + * Connected to `on-screen-manual-start` attribute. + */ + public onScreenManualStart = false; + /** * An array of indexes indicating the atlas pages indexes to be loaded. * If undefined, all pages are loaded. If empty (default), no page is loaded; @@ -488,7 +519,7 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable, if (widget.loading && !widget.onScreenAtLeastOnce) { widget.onScreenAtLeastOnce = true; - if (widget.manualStart && widget.onViewportManualStart) { + if (widget.manualStart && widget.onScreenManualStart) { widget.start(); } } @@ -631,7 +662,7 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable, identifier: { propertyName: "identifier", type: "string" }, debug: { propertyName: "debug", type: "boolean" }, "manual-start": { propertyName: "manualStart", type: "boolean" }, - "on-viewport-manual-start": { propertyName: "onViewportManualStart", type: "boolean" }, + "on-screen-manual-start": { propertyName: "onScreenManualStart", type: "boolean" }, spinner: { propertyName: "loadingSpinner", type: "boolean" }, clip: { propertyName: "clip", type: "boolean" }, pages: { propertyName: "pages", type: "string-number" }, @@ -703,6 +734,9 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable, this.debugDragDiv?.remove(); } + /** + * Remove the widget from the overlay and the DOM. + */ dispose () { this.remove(); this.loadingScreen?.dispose(); @@ -938,6 +972,7 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable, interface OverlayAttributes { overlayId?: string scrollable: boolean + scrollableTweakOff: boolean overflowTop: number overflowBottom: number overflowLeft: number @@ -946,8 +981,12 @@ interface OverlayAttributes { class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, Disposable { - public static OVERLAY_ID = "spine-overlay-default-identifier"; - public static OVERLAY_LIST = new Map(); + private static OVERLAY_ID = "spine-overlay-default-identifier"; + private static OVERLAY_LIST = new Map(); + + /** + * @internal + */ static getOrCreateOverlay(overlayId: string | null): SpineWebComponentOverlay { let overlay = SpineWebComponentOverlay.OVERLAY_LIST.get(overlayId || SpineWebComponentOverlay.OVERLAY_ID); if (!overlay) { @@ -958,14 +997,85 @@ class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, return overlay; } + /** + * A list holding the widgets added to this overlay. + */ public skeletonList = new Array(); + + /** + * A reference to the {@link SceneRenderer} used by this overlay. + */ public renderer: SceneRenderer; + + /** + * A reference to the {@link AssetManager} used by this overlay. + */ public assetManager: AssetManager; - private root: ShadowRoot; + /** + * The identifier of this overlay. This is necessary when multiply overlay are created. + * Connected to `overlay-id` attribute. + */ public overlayId?: string; + + /** + * If true, the overlay will have the size of the element container in contrast to the default behaviour where the + * overlay has always the size of the screen. + * This is necessary when the overlay is inserted into a container that scroll in a different way with respect to the page. + * Otherwise the following problems might occur: + * 1) For scrollable containers, the widget will be slightly slower to scroll than the html behind. The effect is more evident for lower refresh rate display. + * 2) For scrollable containers, the widget will overflow the container bounds until the widget html element container is visible + * 3) For fixed containers, the widget will scroll in a jerky way + * + * In order to fix this behaviour, it is necessary to insert a dedicated `spine-overlay` webcomponent as a direct child of the container. + * Moreover, it is necessary to perform the following actions: + * 1) The scrollable container must have a `transform`css attribute. If it hasn't this attribute the `spine-overlay` will add it for you. + * If your scrollable container has already this css attribute, or if you prefer to add it by yourself (example: `transform: translateZ(0);`), set the `scrollable-tweak-off` to the `spine-overlay`. + * 2) The `spine-overlay` must have the `scrollable`attribute + * 3) The `spine-overlay` must have an `overlay-id` attribute. Choose the value you prefer. + * 4) Each `spine-widget` must have an `overlay-id` attribute. The same as the hosting `spine-overlay`. + * Connected to `scrollable` attribute. + */ public scrollable = false; + /** + * If `false` (default value), the overlay container style will be affected adding `transform: translateZ(0);` to it. + * The `transform` is not affected if it already exists on the container. + * This is necessary to make the scrolling works with containers that scroll in a different way with respect to the page, as explained in {@link scrollable}. + * Connected to `scrollable-tweak-off` attribute. + */ + public scrollableTweakOff = false; + + /** + * How many pixels to add to the top of the canvas to prevent "edge cutting" on fast scrolling, in canvas height units. + * By default, the canvas is big as the screen resolution. Making it too big might reduce performance. + * Connected to `overflow-top` attribute. + */ + public overflowTop = .2; + + /** + * How many pixels to add to the bottom of the canvas to prevent "edge cutting" on fast scrolling, in canvas height units. + * By default, the canvas is big as the screen resolution. Making it too big might reduce performance. + * Connected to `overflow-bottom` attribute. + */ + public overflowBottom = .0; + + /** + * How many pixels to add to the left of the canvas to prevent "edge cutting" on fast scrolling, in canvas width units. + * By default, the canvas is big as the screen resolution. Making it too big might reduce performance. + * Connected to `overflow-left` attribute. + */ + public overflowLeft = .0; + + /** + * How many pixels to add to the right of the canvas to prevent "edge cutting" on fast scrolling, in canvas width units. + * By default, the canvas is big as the screen resolution. Making it too big might reduce performance. + * Connected to `overflow-right` attribute. + */ + public overflowRight = .0; + + private root: ShadowRoot; + private div: HTMLDivElement; private canvas: HTMLCanvasElement; private fps: HTMLSpanElement; @@ -975,13 +1085,6 @@ class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, private resizeObserver?:ResizeObserver; private input?: Input; - // how many pixels to add to the edges to prevent "edge cutting" on fast scrolling - // be aware that the canvas is already big as the display size - // making it bigger might reduce performance significantly - public overflowTop = .2; - public overflowBottom = .2; - public overflowLeft = .0; - public overflowRight = .0; private overflowLeftSize = 0; private overflowTopSize = 0; @@ -1068,7 +1171,7 @@ class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, this.resizeObserver = new ResizeObserver(this.resizeCallback); if (this.scrollable) { const style = getComputedStyle(this.parentElement!); - if (style.transform === "none") { + if (style.transform === "none" && !this.scrollableTweakOff) { this.parentElement!.style.transform = `translateZ(0)`; } this.resizeObserver.observe(this.parentElement!); @@ -1100,6 +1203,7 @@ class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, static attributesDescription: Record = { "overlay-id": { propertyName: "overlayId", type: "string" }, "scrollable": { propertyName: "scrollable", type: "boolean" }, + "scrollable-tweak-off": { propertyName: "scrollableTweakOff", type: "boolean" }, "overflow-top": { propertyName: "overflowTop", type: "number" }, "overflow-bottom": { propertyName: "overflowBottom", type: "number" }, "overflow-left": { propertyName: "overflowLeft", type: "number" }, @@ -1144,8 +1248,12 @@ class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, this.loaded = true; } + /** + * Remove the overlay from the DOM, dispose all the contained widgets, and dispose the renderer. + */ dispose (): void { this.remove(); + this.skeletonList.forEach(widget => widget.dispose()); this.skeletonList.length = 0; this.renderer.dispose(); this.disposed = true; @@ -1154,7 +1262,7 @@ class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, addWidget (widget: SpineWebComponentWidget) { this.skeletonList.push(widget); this.intersectionObserver?.observe(widget.getHTMLElementReference()); - if (this.loaded) { + if (this.loaded && (this.compareDocumentPosition(widget) & Node.DOCUMENT_POSITION_FOLLOWING)) { this.parentElement!.appendChild(this); } }