mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
Initial support of spine-widget into scrollable containers, and overlay-id for multiple spine-overlay.
working
This commit is contained in:
parent
e9a07bd6b9
commit
de7494036c
@ -113,6 +113,22 @@
|
||||
width: 150px;
|
||||
aspect-ratio: 3 / 3;
|
||||
}
|
||||
|
||||
.overflow-grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 100px);
|
||||
grid-template-rows: repeat(4, 100px);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.overflow-grid-item {
|
||||
background-color: lightblue;;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@ -1987,6 +2003,115 @@ stretchyman.update = (canvas, delta, skeleton, state) => {
|
||||
/////////////////////
|
||||
-->
|
||||
|
||||
<!--
|
||||
/////////////////////
|
||||
// start section //
|
||||
/////////////////////
|
||||
-->
|
||||
|
||||
<div class="section vertical-split">
|
||||
|
||||
<div class="split-left">
|
||||
If you use a spine widget in a scrollable element, the effect might not be the desired one. There will be two problems:
|
||||
<br>
|
||||
<br>
|
||||
1) The widget will be slightly slower to scroll than the html behind <br>
|
||||
2) The widget will overflow the scrollable container until the html element container is visible <br>
|
||||
<br>
|
||||
In order to fix this behaviour, it is necessary to insert a dedicated <code>spine-overlay</code> webcomponent as a direct child of the scrollable container.
|
||||
Moreover, it is necessary to perform the following actions: <br>
|
||||
<br>
|
||||
1) The scrollable container must have a <code>transform</code> css attribute. If it hasn't this attribute the <code>spine-overlay</code> will add it for you.
|
||||
If your scrollable container has already this css attribute, or if you prefer to add it by yourself (example: <code>transform: translateZ(0);</code>), set the <code>scrollable-tweak-off</code> to the <code>spine-overlay</code>.
|
||||
<br>
|
||||
2) The <code>spine-overlay</code> must have the <code>scrollable</code> attribute
|
||||
<br>
|
||||
3) The <code>spine-overlay</code> must have an <code>overlay-id</code> attribute. Choose the value you prefer.
|
||||
<br>
|
||||
4) Each <code>spine-widget</code> must have an <code>overlay-id</code> attribute. The same as the hosting <code>spine-overlay</code>.
|
||||
<br>
|
||||
<br>
|
||||
Additionally, you can set <code>overflow-top</code>, <code>overflow-bottom</code>, <code>overflow-left</code>, <code>overflow-right</code> attributes to the <code>spine-overlay</code> in order to make the canvas bigger and prevent scrolling artifacts.
|
||||
</div>
|
||||
|
||||
<div class="split-top split" style="justify-content: space-between">
|
||||
<div class="split-left" style="overflow-y: auto; width: 100px; height: 200px;">
|
||||
<div class="overflow-grid-container">
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split-left" style="overflow-y: auto; width: 100px; height: 200px;">
|
||||
<spine-overlay
|
||||
overlay-id="scroll"
|
||||
scrollable
|
||||
scrollable-tweak-off
|
||||
overflow-top=".2"
|
||||
overflow-bottom=".2"
|
||||
overflow-left=".2"
|
||||
overflow-right=".2"
|
||||
></spine-overlay>
|
||||
<div class="overflow-grid-container">
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="split-bottom">
|
||||
<pre><code id="code-display">
|
||||
<script>escapeHTMLandInject(`
|
||||
<spine-widget
|
||||
atlas="assets/celestial-circus-pma.atlas"
|
||||
skeleton="assets/celestial-circus-pro.skel"
|
||||
animation="wings-and-feet"
|
||||
isdraggable="true"
|
||||
></spine-widget>`
|
||||
);</script>
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!--
|
||||
/////////////////////
|
||||
// end section //
|
||||
/////////////////////
|
||||
-->
|
||||
|
||||
|
||||
<script>
|
||||
spine.SpineWebComponentWidget.SHOW_FPS = true;
|
||||
</script>
|
||||
|
||||
238
spine-ts/spine-webgl/example/webcomponent-tutorial2.html
Normal file
238
spine-ts/spine-webgl/example/webcomponent-tutorial2.html
Normal file
@ -0,0 +1,238 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="../dist/iife/spine-webgl.js"></script>
|
||||
<!-- <script src="./spine-webgl.min.js"></script> -->
|
||||
<title>Webcomponent Tutorial</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
.section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
background-color: #3498db;
|
||||
}
|
||||
.split {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
.split-left, .split-right {
|
||||
width: 50%;
|
||||
min-height: 50%;
|
||||
padding: 1rem;
|
||||
margin: 1rem;
|
||||
border: 1px solid salmon;
|
||||
}
|
||||
.split-nosize {
|
||||
border: 1px solid salmon;
|
||||
}
|
||||
.split-size {
|
||||
padding: 1rem;
|
||||
margin: 1rem;
|
||||
}
|
||||
.navigation {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 20px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.nav-btn {
|
||||
display: block;
|
||||
margin: 0px 5px;
|
||||
padding: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vertical-split {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.high-page {
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.split-top {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.split-bottom {
|
||||
width: 100%;
|
||||
/* height: 600px; */
|
||||
}
|
||||
|
||||
.split-bottom {
|
||||
background-color: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.split-bottom pre {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.split-bottom code {
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.skin-grid {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
justify-content: space-evenly;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.skin-grid-element {
|
||||
border: 1px solid #ccc;
|
||||
width: 150px;
|
||||
aspect-ratio: 3 / 3;
|
||||
}
|
||||
|
||||
.overflow-grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 100px);
|
||||
grid-template-rows: repeat(4, 100px);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.overflow-grid-item {
|
||||
background-color: lightblue;;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function escapeHTMLandInject(text) {
|
||||
const escaped = text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
document.currentScript.parentElement.innerHTML = escaped;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<!--
|
||||
/////////////////////
|
||||
// start section //
|
||||
/////////////////////
|
||||
-->
|
||||
|
||||
<div class="section vertical-split">
|
||||
|
||||
<div class="split-top split" style="justify-content: space-between">
|
||||
<!-- <div class="split-left" style="overflow-y: auto; width: 200px; height: 200px;">
|
||||
<div class="overflow-grid-container">
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel"></spine-widget></div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="split-left" style="overflow-y: auto; width: 200px; height: 200px;">
|
||||
<spine-overlay
|
||||
overlay-id="scroll"
|
||||
scrollable
|
||||
overflow-top=".2"
|
||||
overflow-bottom=".2"
|
||||
overflow-left=".2"
|
||||
overflow-right=".2"
|
||||
></spine-overlay>
|
||||
<div class="overflow-grid-container">
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
<div class="overflow-grid-item"><spine-widget atlas="assets/spineboy-pma.atlas" skeleton="assets/spineboy-pro.skel" overlay-id="scroll"></spine-widget></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="split-bottom">
|
||||
<pre><code id="code-display">
|
||||
<script>escapeHTMLandInject(`
|
||||
<spine-widget
|
||||
atlas="assets/celestial-circus-pma.atlas"
|
||||
skeleton="assets/celestial-circus-pro.skel"
|
||||
animation="wings-and-feet"
|
||||
isdraggable="true"
|
||||
></spine-widget>`
|
||||
);</script>
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="height: 1000px;"> SPACE </div>
|
||||
|
||||
<!--
|
||||
/////////////////////
|
||||
// end section //
|
||||
/////////////////////
|
||||
-->
|
||||
|
||||
<script>
|
||||
spine.SpineWebComponentWidget.SHOW_FPS = true;
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -573,7 +573,7 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
|
||||
private root: ShadowRoot;
|
||||
|
||||
// Reference to the overlay webcomponent
|
||||
private overlay: SpineWebComponentOverlay;
|
||||
private overlay!: SpineWebComponentOverlay;
|
||||
|
||||
static attributesDescription: Record<string, { propertyName: keyof WidgetAttributes, type: AttributeTypes, defaultValue?: any }> = {
|
||||
atlas: { propertyName: "atlasPath", type: "string" },
|
||||
@ -615,7 +615,6 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
|
||||
constructor () {
|
||||
super();
|
||||
this.root = this.attachShadow({ mode: "closed" });
|
||||
this.overlay = this.initializeOverlay();
|
||||
|
||||
this.debugDragDiv = document.createElement("div");
|
||||
this.debugDragDiv.style.position = "absolute";
|
||||
@ -628,7 +627,10 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
|
||||
throw new Error("You cannot attach a disposed widget");
|
||||
};
|
||||
|
||||
customElements.whenDefined("spine-overlay").then(() => this.overlay.addWidget(this));
|
||||
customElements.whenDefined("spine-overlay").then(() => {
|
||||
if (!this.overlay) this.overlay = this.initializeOverlay(this.getAttribute("overlay-id"));
|
||||
this.overlay.addWidget(this);
|
||||
});
|
||||
if (!this.manualStart && !this.started) {
|
||||
this.start();
|
||||
}
|
||||
@ -636,9 +638,9 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
|
||||
}
|
||||
|
||||
disconnectedCallback (): void {
|
||||
const index = this.overlay.skeletonList.indexOf(this);
|
||||
const index = this.overlay!.skeletonList.indexOf(this);
|
||||
if (index !== -1) {
|
||||
this.overlay.skeletonList.splice(index, 1);
|
||||
this.overlay!.skeletonList.splice(index, 1);
|
||||
}
|
||||
this.debugDragDiv?.remove();
|
||||
}
|
||||
@ -654,7 +656,7 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
|
||||
|
||||
attributeChangedCallback (name: string, oldValue: string | null, newValue: string | null): void {
|
||||
const { type, propertyName, defaultValue } = SpineWebComponentWidget.attributesDescription[name];
|
||||
const val = SpineWebComponentWidget.castValue(type, newValue, defaultValue);
|
||||
const val = castValue(type, newValue, defaultValue);
|
||||
(this as any)[propertyName] = val;
|
||||
return;
|
||||
}
|
||||
@ -824,8 +826,9 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
|
||||
// Create a new overlay webcomponent, if no one exists yet.
|
||||
// TODO: allow the possibility to instantiate multiple overlay (eg: background, foreground),
|
||||
// to give them an identifier, and to specify which overlay is assigned to a widget
|
||||
private initializeOverlay (): SpineWebComponentOverlay {
|
||||
let overlay = this.overlay || document.querySelector("spine-overlay") as SpineWebComponentOverlay;
|
||||
private initializeOverlay (overlayId: string | null): SpineWebComponentOverlay {
|
||||
const queryString = overlayId === null ? "spine-overlay:not([overlay-id])" : `spine-overlay[overlay-id=${overlayId}]`;
|
||||
let overlay = this.overlay || document.querySelector(queryString) as SpineWebComponentOverlay;
|
||||
if (!overlay) {
|
||||
overlay = document.createElement("spine-overlay") as SpineWebComponentOverlay;
|
||||
document.body.appendChild(overlay);
|
||||
@ -882,60 +885,26 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
|
||||
}
|
||||
}
|
||||
|
||||
private static castBoolean (value: string | null, defaultValue = "") {
|
||||
return value === "true" || value === "" ? true : false;
|
||||
}
|
||||
|
||||
private static castString (value: string | null, defaultValue = "") {
|
||||
return value === null ? defaultValue : value;
|
||||
}
|
||||
|
||||
private static castNumber (value: string | null, defaultValue = 0) {
|
||||
if (value === null) return defaultValue;
|
||||
|
||||
const parsed = parseFloat(value);
|
||||
if (Number.isNaN(parsed)) return defaultValue;
|
||||
return parsed;
|
||||
}
|
||||
|
||||
private static castArrayNumber (value: string | null, defaultValue = undefined) {
|
||||
if (value === null) return defaultValue;
|
||||
return value.split(",").reduce((acc, pageIndex) => {
|
||||
const index = parseInt(pageIndex);
|
||||
if (!isNaN(index)) acc.push(index);
|
||||
return acc;
|
||||
}, [] as Array<number>);
|
||||
}
|
||||
|
||||
private static castValue (type: AttributeTypes, value: string | null, defaultValue?: any) {
|
||||
switch (type) {
|
||||
case "string":
|
||||
return SpineWebComponentWidget.castString(value, defaultValue);
|
||||
case "number":
|
||||
return SpineWebComponentWidget.castNumber(value, defaultValue);
|
||||
case "boolean":
|
||||
return SpineWebComponentWidget.castBoolean(value, defaultValue);
|
||||
case "string-number":
|
||||
return SpineWebComponentWidget.castArrayNumber(value, defaultValue);
|
||||
case "fitType":
|
||||
return isFitType(value) ? value : defaultValue;
|
||||
case "modeType":
|
||||
return isModeType(value) ? value : defaultValue;
|
||||
case "offScreenUpdateBehaviourType":
|
||||
return isOffScreenUpdateBehaviourType(value) ? value : defaultValue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
interface OverlayAttributes {
|
||||
overlayId?: string
|
||||
scrollable: boolean
|
||||
overflowTop: number
|
||||
overflowBottom: number
|
||||
overflowLeft: number
|
||||
overflowRight: number
|
||||
}
|
||||
|
||||
class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, Disposable {
|
||||
|
||||
public skeletonList = new Array<SpineWebComponentWidget>();
|
||||
public renderer: SceneRenderer;
|
||||
public assetManager: AssetManager;
|
||||
|
||||
private root: ShadowRoot;
|
||||
public overlayId?: string;
|
||||
public scrollable = false;
|
||||
|
||||
private div: HTMLDivElement;
|
||||
private canvas: HTMLCanvasElement;
|
||||
@ -946,15 +915,15 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
private resizeObserver?:ResizeObserver;
|
||||
private input?: Input;
|
||||
|
||||
// how many pixels to add to the edges to prevent "edge cuttin" on fast scrolling
|
||||
// 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
|
||||
private overflowTop = .2;
|
||||
private overflowBottom = .0;
|
||||
private overflowLeft = .0;
|
||||
private overflowRight = .0;
|
||||
private overflowLeftSize: number
|
||||
private overflowTopSize: number;
|
||||
public overflowTop = .2;
|
||||
public overflowBottom = .0;
|
||||
public overflowLeft = .0;
|
||||
public overflowRight = .0;
|
||||
private overflowLeftSize = 0;
|
||||
private overflowTopSize = 0;
|
||||
|
||||
private currentCanvasBaseWidth = 0;
|
||||
private currentCanvasBaseHeight = 0;
|
||||
@ -965,7 +934,7 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
|
||||
constructor () {
|
||||
super();
|
||||
this.root = this.attachShadow({ mode: "closed" });
|
||||
this.root = this.attachShadow({ mode: "open" });
|
||||
|
||||
this.div = document.createElement("div");
|
||||
this.div.style.position = "absolute";
|
||||
@ -973,11 +942,12 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
this.div.style.left = "0";
|
||||
this.div.style.setProperty("pointer-events", "none");
|
||||
this.div.style.overflow = "hidden"
|
||||
// this.div.style.backgroundColor = "rgba(0, 255, 0, 0.3)";
|
||||
// this.div.style.backgroundColor = "rgba(0, 255, 0, 0.1)";
|
||||
|
||||
this.root.appendChild(this.div);
|
||||
|
||||
this.canvas = document.createElement("canvas");
|
||||
|
||||
this.div.appendChild(this.canvas);
|
||||
this.canvas.style.position = "absolute";
|
||||
this.canvas.style.top = "0";
|
||||
@ -994,10 +964,28 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
|
||||
const context = new ManagedWebGLRenderingContext(this.canvas, { alpha: true });
|
||||
this.renderer = new SceneRenderer(this.canvas, context);
|
||||
this.assetManager = new AssetManager(context);
|
||||
|
||||
this.overflowLeftSize = this.overflowLeft * document.documentElement.clientWidth;
|
||||
this.overflowTopSize = this.overflowTop * document.documentElement.clientHeight;
|
||||
this.assetManager = new AssetManager(context);
|
||||
}
|
||||
|
||||
static attributesDescription: Record<string, { propertyName: keyof OverlayAttributes, type: AttributeTypes, defaultValue?: any }> = {
|
||||
"overlay-id": { propertyName: "overlayId", type: "string" },
|
||||
"scrollable": { propertyName: "scrollable", type: "boolean" },
|
||||
"overflow-top": { propertyName: "overflowTop", type: "number" },
|
||||
"overflow-bottom": { propertyName: "overflowBottom", type: "number" },
|
||||
"overflow-left": { propertyName: "overflowLeft", type: "number" },
|
||||
"overflow-right": { propertyName: "overflowRight", type: "number" },
|
||||
}
|
||||
|
||||
static get observedAttributes (): string[] {
|
||||
return Object.keys(SpineWebComponentOverlay.attributesDescription);
|
||||
}
|
||||
|
||||
attributeChangedCallback (name: string, oldValue: string | null, newValue: string | null): void {
|
||||
const { type, propertyName, defaultValue } = SpineWebComponentOverlay.attributesDescription[name];
|
||||
const val = castValue(type, newValue, defaultValue);
|
||||
(this as any)[propertyName] = val;
|
||||
return;
|
||||
}
|
||||
|
||||
private resizeCallback = () => {
|
||||
@ -1025,7 +1013,7 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
}
|
||||
|
||||
connectedCallback (): void {
|
||||
window.addEventListener("scroll", this.scrollHandler);
|
||||
// window.addEventListener("scroll", this.scrollHandler);
|
||||
window.addEventListener("load", this.onLoadCallback);
|
||||
if (this.loaded) this.onLoadCallback();
|
||||
window.screen.orientation.addEventListener('change', this.orientationChangeCallback);
|
||||
@ -1052,7 +1040,15 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
// Alternatively, we can store the body size, check the current body size in the loop (like the translateCanvas), and
|
||||
// if they differs call the resizeCallback. I already tested it, and it works. ResizeObserver should be more efficient.
|
||||
this.resizeObserver = new ResizeObserver(this.resizeCallback);
|
||||
this.resizeObserver.observe(document.body);
|
||||
if (this.scrollable) {
|
||||
const style = getComputedStyle(this.parentElement!);
|
||||
if (style.transform === "none") {
|
||||
this.parentElement!.style.transform = `translateZ(0)`;
|
||||
}
|
||||
this.resizeObserver.observe(this.parentElement!);
|
||||
} else {
|
||||
this.resizeObserver.observe(document.body);
|
||||
}
|
||||
|
||||
this.skeletonList.forEach((widget) => {
|
||||
this.intersectionObserver?.observe(widget.getHTMLElementReference());
|
||||
@ -1063,7 +1059,7 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
}
|
||||
|
||||
disconnectedCallback (): void {
|
||||
window.removeEventListener("scroll", this.scrollHandler);
|
||||
// window.removeEventListener("scroll", this.scrollHandler);
|
||||
window.removeEventListener("load", this.onLoadCallback);
|
||||
window.screen.orientation.removeEventListener('change', this.orientationChangeCallback);
|
||||
this.intersectionObserver?.disconnect();
|
||||
@ -1173,6 +1169,7 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
let renderer = this.renderer;
|
||||
renderer.begin();
|
||||
|
||||
const ref = this.parentElement!.getBoundingClientRect();
|
||||
const tempVector = new Vector3();
|
||||
this.skeletonList.forEach((widget) => {
|
||||
const { skeleton, bounds, mode, debug, offsetX, offsetY, xAxis, yAxis, dragX, dragY, fit, loadingSpinner, onScreen, loading, clip, isDraggable } = widget;
|
||||
@ -1184,6 +1181,10 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
divBounds.x = divBounds.left + this.overflowLeftSize;
|
||||
divBounds.y = divBounds.top + this.overflowTopSize;
|
||||
|
||||
if (this.scrollable) {
|
||||
divBounds.x -= ref.left;
|
||||
divBounds.y -= ref.top;
|
||||
}
|
||||
|
||||
const { padLeft, padRight, padTop, padBottom } = widget
|
||||
const paddingShiftHorizontal = (padLeft - padRight) / 2;
|
||||
@ -1441,16 +1442,36 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
// temporarely remove the div to get the page size without considering the div
|
||||
// this is necessary otherwise if the bigger element in the page is remove and the div
|
||||
// was the second bigger element, now it would be the div to determine the page size
|
||||
this.div.remove();
|
||||
const { width, height } = this.getPageSize();
|
||||
this.root.appendChild(this.div);
|
||||
|
||||
this.div.style.width = width + "px";
|
||||
this.div.style.height = height + "px";
|
||||
|
||||
if (!this.scrollable) {
|
||||
this.div?.remove();
|
||||
const { width, height } = this.getPageSize();
|
||||
this.div!.style.width = width + "px";
|
||||
this.div!.style.height = height + "px";
|
||||
this.root.appendChild(this.div!);
|
||||
} else {
|
||||
this.div?.remove();
|
||||
this.div!.style.width = this.parentElement!.scrollWidth + "px";
|
||||
this.div!.style.height = this.parentElement!.scrollHeight + "px";
|
||||
// this.canvas.style.transform = `translate(${-this.overflowLeftSize}px,${-this.overflowTopSize}px)`;
|
||||
this.root.appendChild(this.div!);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private resizeCanvas () {
|
||||
let { width, height } = this.getScreenSize();
|
||||
let width, height;
|
||||
if (!this.overlayId) {
|
||||
const screenSize = this.getScreenSize();
|
||||
width = screenSize.width;
|
||||
height = screenSize.height;
|
||||
} else {
|
||||
width = this.parentElement!.clientWidth;
|
||||
height = this.parentElement!.clientHeight;
|
||||
}
|
||||
|
||||
|
||||
// this is needed because screen size is wrong when zoom levels occurs
|
||||
// zooming out will make the canvas smaller and its known that zoom level
|
||||
@ -1458,8 +1479,8 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
// ideally, window.innerWidth/innerHeight would be preferrable. However
|
||||
// on mobile browsers the dynamic search bar makes the innerHeight smaller
|
||||
// at the beginning (changing the canvas size at each scroll is not ideal)
|
||||
width = Math.max(width, window.innerWidth);
|
||||
height = Math.max(height, window.innerHeight);
|
||||
// width = Math.max(width, window.innerWidth);
|
||||
// height = Math.max(height, window.innerHeight);
|
||||
|
||||
if (this.currentCanvasBaseWidth !== width || this.currentCanvasBaseHeight !== height) {
|
||||
this.currentCanvasBaseWidth = width;
|
||||
@ -1477,8 +1498,17 @@ class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
}
|
||||
|
||||
private translateCanvas () {
|
||||
const scrollPositionX = window.scrollX - this.overflowLeftSize;
|
||||
const scrollPositionY = window.scrollY - this.overflowTopSize;
|
||||
let scrollPositionX = -this.overflowLeftSize;
|
||||
let scrollPositionY = -this.overflowTopSize;
|
||||
|
||||
if (!this.scrollable) {
|
||||
scrollPositionX += window.scrollX;
|
||||
scrollPositionY += window.scrollY;
|
||||
} else {
|
||||
scrollPositionX += this.parentElement!.scrollLeft;
|
||||
scrollPositionY += this.parentElement!.scrollTop;
|
||||
}
|
||||
|
||||
this.canvas.style.transform = `translate(${scrollPositionX}px,${scrollPositionY}px)`;
|
||||
}
|
||||
|
||||
@ -1578,4 +1608,50 @@ export function createSpineWidget (parameters: WidgetAttributes): SpineWebCompon
|
||||
}
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
function castBoolean (value: string | null, defaultValue = "") {
|
||||
return value === "true" || value === "" ? true : false;
|
||||
}
|
||||
|
||||
function castString (value: string | null, defaultValue = "") {
|
||||
return value === null ? defaultValue : value;
|
||||
}
|
||||
|
||||
function castNumber (value: string | null, defaultValue = 0) {
|
||||
if (value === null) return defaultValue;
|
||||
|
||||
const parsed = parseFloat(value);
|
||||
if (Number.isNaN(parsed)) return defaultValue;
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function castArrayNumber (value: string | null, defaultValue = undefined) {
|
||||
if (value === null) return defaultValue;
|
||||
return value.split(",").reduce((acc, pageIndex) => {
|
||||
const index = parseInt(pageIndex);
|
||||
if (!isNaN(index)) acc.push(index);
|
||||
return acc;
|
||||
}, [] as Array<number>);
|
||||
}
|
||||
|
||||
function castValue (type: AttributeTypes, value: string | null, defaultValue?: any) {
|
||||
switch (type) {
|
||||
case "string":
|
||||
return castString(value, defaultValue);
|
||||
case "number":
|
||||
return castNumber(value, defaultValue);
|
||||
case "boolean":
|
||||
return castBoolean(value, defaultValue);
|
||||
case "string-number":
|
||||
return castArrayNumber(value, defaultValue);
|
||||
case "fitType":
|
||||
return isFitType(value) ? value : defaultValue;
|
||||
case "modeType":
|
||||
return isModeType(value) ? value : defaultValue;
|
||||
case "offScreenUpdateBehaviourType":
|
||||
return isOffScreenUpdateBehaviourType(value) ? value : defaultValue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user