General refactor. spine-widget to spine-skeleton.

This commit is contained in:
Davide Tantillo 2025-05-08 16:25:34 +02:00
parent 70cc7e45ca
commit 0abcaff7fe
9 changed files with 1590 additions and 1538 deletions

View File

@ -201,7 +201,7 @@
<!-- SECTION 1 -->
<div class="screen">
<div class="top-section">
<spine-widget
<spine-skeleton
identifier="list"
overlay-id="phone"
@ -212,7 +212,7 @@
isinteractive
></spine-widget>
></spine-skeleton>
</div>
<div class="bottom-section">
@ -268,7 +268,7 @@
<div class="screen">
<div class="top-section">
<spine-widget
<spine-skeleton
identifier="pan"
overlay-id="phone"
@ -277,7 +277,7 @@
skeleton="assets/food/pan-cooking-pro.json"
animation="animation"
></spine-widget>
></spine-skeleton>
</div>
@ -333,7 +333,7 @@
<div class="screen">
<div class="top-section">
<spine-widget
<spine-skeleton
identifier="delivery"
overlay-id="phone"
@ -342,7 +342,7 @@
skeleton="assets/food/meal-delivery-pro.json"
animation="animation"
></spine-widget>
></spine-skeleton>
</div>
@ -372,7 +372,7 @@
<div class="screen">
<div class="top-section">
<spine-widget
<spine-skeleton
identifier="ready"
overlay-id="phone"
@ -382,7 +382,7 @@
animation="base"
isinteractive
></spine-widget>
></spine-skeleton>
</div>

View File

@ -37,20 +37,20 @@
</div>
<div class="bottom" style="flex: 80%; background-color: rgb(24, 149, 89); display: flex; align-items: center; justify-content: center;">
<spine-widget
<spine-skeleton
identifier="windmill-game"
atlas="assets/windmill-ess.atlas"
skeleton="assets/windmill-ess.json"
animation="animation"
isinteractive
></spine-widget>
<spine-widget
></spine-skeleton>
<spine-skeleton
identifier="spineboy-game"
atlas="assets/spineboy-pma.atlas"
skeleton="assets/spineboy-pro.json"
animation="hoverboard"
fit="none"
></spine-widget>
></spine-skeleton>
</div>
</div>

View File

@ -41,13 +41,13 @@
<div class="container">
<div class="left-column">
<spine-widget
<spine-skeleton
identifier="boi"
atlas="assets/spineboy-pma.atlas"
skeleton="assets/spineboy-pro.skel"
auto-calculate-bounds
debug
></spine-widget>
></spine-skeleton>
</div>
<div class="right-column">
<div id="lil"></div>

View File

@ -21,7 +21,7 @@
<div style="background-color: white; width: 250px; padding: 30px; text-align: center; border-radius: 10px; border: 3px solid black; box-shadow: 5px 5px rgb(0, 0, 0);">
<div style="display: flex; justify-content: center;">
<div style="width: 150px; height:150px; border-radius: 5%; border: 1px solid rgb(113, 113, 113); background-color: rgb(211, 211, 211); margin-bottom: 30px;">
<spine-widget
<spine-skeleton
identifier="spineboy-login"
atlas="assets/pwd/chibi-stickers-pro-pwd-test.atlas"
skeleton="assets/pwd/chibi-stickers.json"
@ -33,7 +33,7 @@
animation="interactive/head/idle"
clip
isinteractive
></spine-widget>
></spine-skeleton>
</div>
</div>
@ -51,14 +51,14 @@
<div style="height: 75px; cursor: pointer;">
<div id="button-text" style="font-size: xx-large; cursor: pointer; user-select: none; display: none;"> LOGIN </div>
<spine-widget
<spine-skeleton
identifier="button-login"
atlas="assets/pwd/button.atlas"
skeleton="assets/pwd/button.json"
animation="idle"
isinteractive
fit="fill"
></spine-widget>
></spine-skeleton>
</div>
</form>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
export * from './SpineWebComponentWidget.js';
export * from './SpineWebComponentSkeleton.js';
export * from './SpineWebComponentOverlay.js';
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";
export * from "@esotericsoftware/spine-webgl";

View File

@ -0,0 +1,189 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated July 28, 2023. Replaces all prior versions.
*
* Copyright (c) 2013-2023, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software or
* otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { AnimationsInfo, FitType, ModeType, OffScreenUpdateBehaviourType } from "./SpineWebComponentSkeleton";
const animatonTypeRegExp = /\[([^\]]+)\]/g;
export type AttributeTypes = "string" | "number" | "boolean" | "array-number" | "array-string" | "object" | "fitType" | "modeType" | "offScreenUpdateBehaviourType" | "animationsInfo";
export 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 "array-number":
return castArrayNumber(value, defaultValue);
case "array-string":
return castArrayString(value, defaultValue);
case "object":
return castObject(value, defaultValue);
case "fitType":
return isFitType(value) ? value : defaultValue;
case "modeType":
return isModeType(value) ? value : defaultValue;
case "offScreenUpdateBehaviourType":
return isOffScreenUpdateBehaviourType(value) ? value : defaultValue;
case "animationsInfo":
return castToAnimationsInfo(value) || defaultValue;
default:
break;
}
}
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 castArrayString (value: string | null, defaultValue = undefined) {
if (value === null) return defaultValue;
return value.split(",");
}
function castObject (value: string | null, defaultValue = undefined) {
if (value === null) return null;
return JSON.parse(value);
}
function castToAnimationsInfo (value: string | null): AnimationsInfo | undefined {
if (value === null) {
return undefined;
}
const matches = value.match(animatonTypeRegExp);
if (!matches) return undefined;
return matches.reduce((obj, group) => {
const [trackIndexStringOrLoopDefinition, animationNameOrTrackIndexStringCycle, loop, delayString, mixDurationString] = group.slice(1, -1).split(',').map(v => v.trim());
if (trackIndexStringOrLoopDefinition === "loop") {
if (!Number.isInteger(Number(animationNameOrTrackIndexStringCycle))) {
throw new Error(`Track index of cycle in ${group} must be a positive integer number, instead it is ${animationNameOrTrackIndexStringCycle}. Original value: ${value}`);
}
const animationInfoObject = obj[animationNameOrTrackIndexStringCycle] ||= { animations: [] };
animationInfoObject.cycle = true;
return obj;
}
const trackIndex = Number(trackIndexStringOrLoopDefinition);
if (!Number.isInteger(trackIndex)) {
throw new Error(`Track index in ${group} must be a positive integer number, instead it is ${trackIndexStringOrLoopDefinition}. Original value: ${value}`);
}
let delay;
if (delayString !== undefined) {
delay = parseFloat(delayString);
if (isNaN(delay)) {
throw new Error(`Delay in ${group} must be a positive number, instead it is ${delayString}. Original value: ${value}`);
}
}
let mixDuration;
if (mixDurationString !== undefined) {
mixDuration = parseFloat(mixDurationString);
if (isNaN(mixDuration)) {
throw new Error(`mixDuration in ${group} must be a positive number, instead it is ${mixDurationString}. Original value: ${value}`);
}
}
const animationInfoObject = obj[trackIndexStringOrLoopDefinition] ||= { animations: [] };
animationInfoObject.animations.push({
animationName: animationNameOrTrackIndexStringCycle,
loop: loop.trim().toLowerCase() === "true",
delay,
mixDuration,
});
return obj;
}, {} as AnimationsInfo);
}
function isFitType (value: string | null): value is FitType {
return (
value === "fill" ||
value === "width" ||
value === "height" ||
value === "contain" ||
value === "cover" ||
value === "none" ||
value === "scaleDown"
);
}
function isOffScreenUpdateBehaviourType (value: string | null): value is OffScreenUpdateBehaviourType {
return (
value === "pause" ||
value === "update" ||
value === "pose"
);
}
function isModeType (value: string | null): value is ModeType {
return (
value === "inside" ||
value === "origin"
);
}
const base64RegExp = /^(([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==))$/;
export function isBase64 (str: string) {
return base64RegExp.test(str);
}
export interface Point {
x: number,
y: number,
}
export interface Rectangle extends Point {
width: number,
height: number,
}