mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
overlay 4
This commit is contained in:
parent
9d1109c9dc
commit
3b5d74e0e8
@ -15,7 +15,7 @@
|
||||
}
|
||||
.spine-div {
|
||||
border: 1px solid black;
|
||||
padding: 20px;
|
||||
/* padding: 20px; */
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.spacer {
|
||||
@ -24,39 +24,64 @@
|
||||
#canvas {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.resize-handle {
|
||||
width: 20%;
|
||||
height: 20%;
|
||||
background-color: #007bff;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
cursor: se-resize;
|
||||
}
|
||||
</style>
|
||||
<script src="../dist/iife/spine-webgl.js"></script>
|
||||
<!-- <script src="../dist/iife/spine-webgl.js"></script> -->
|
||||
<!-- <script src="./spine-webgl.min.js"></script> -->
|
||||
<script src="./spine-webgl.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1>OverlayCanvas Example</h1>
|
||||
<h1>Spine Canvas Overlay Example</h1>
|
||||
|
||||
<p>Scroll down to div.</p>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="spine-div" div-spine>
|
||||
<h2>Spine Box 1</h2>
|
||||
<div id="spineboy1" class="spine-div" style="width: 200px; height: 300px; margin-left: 200px; touch-action:none; position:relative;" div-spine>
|
||||
<div id="resizeHandle" class="resize-handle"></div>
|
||||
<h2>Drag and resize me</h2>
|
||||
<h3>Mode: inside</h3>
|
||||
<h4>Spineboy will be resize to remain into the div.</h4>
|
||||
<h4>Skeleton cannot be reused (side effect on skeleton scale).</h4>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div id="spineboy2" class="spine-div" style="width: 50%; margin-left: 50%; touch-action:none" div-spine>
|
||||
<h2>Spine Box 2 (drag me)</h2>
|
||||
<div id="spineboy2" class="spine-div" style="width: 50%; margin-left: 50%; touch-action:none" div-spine2>
|
||||
<h2>Drag me</h2>
|
||||
<h3>Mode: origin</h3>
|
||||
<h4>You can easily change the position using offset or percentage of html element axis (origin is top-left)</h4>
|
||||
<h4>Skeleton can be reused.</h4>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div id="raptor" class="spine-div" style="width: 50%; margin-left: 50%; transition: transform 1s linear;" div-raptor>
|
||||
<h2>Raptor Box</h2>
|
||||
<div id="spineboy3" class="spine-div" style="width: 50%; margin-left: 50%; touch-action:none" div-spine2>
|
||||
<h3>Skeleton of previous box is being reused here</h3>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="spine-div" style="width: 50%; margin-left: 20%;" div-celeste>
|
||||
<h2>Celeste Box</h2>
|
||||
<div class="spine-div" div-spine3>
|
||||
<h3>Initializer with NodeList</h3>
|
||||
</div>
|
||||
<div class="spine-div" div-spine3>
|
||||
<h3>Initializer with NodeList</h3>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="spine-div" style="width: 50%; height: 200px;" div-spine4>
|
||||
<h3>Initializer with HTMLElement</h3>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<p>End of content.</p>
|
||||
</div>
|
||||
@ -66,18 +91,161 @@
|
||||
const divs = document.querySelectorAll(`[div-spine]`);
|
||||
const overlay = new spine.SpineCanvasOverlay();
|
||||
|
||||
const p = overlay.addSkeleton({
|
||||
atlasPath: "assets/spineboy-pma.atlas",
|
||||
skeletonPath: "assets/spineboy-pro.skel",
|
||||
const p = overlay.addSkeleton(
|
||||
{
|
||||
atlasPath: "assets/spineboy-pma.atlas",
|
||||
skeletonPath: "assets/spineboy-pro.skel",
|
||||
scale: .5,
|
||||
animation: 'walk',
|
||||
},
|
||||
[
|
||||
{
|
||||
element: divs[0],
|
||||
mode: 'inside',
|
||||
showBounds: true,
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
setTimeout(async () => {
|
||||
const { skeleton, state } = await p;
|
||||
state.setAnimation(0, "run", true);
|
||||
overlay.recalculateBounds(skeleton, state);
|
||||
}, 1000)
|
||||
|
||||
const divs2 = document.querySelectorAll(`[div-spine2]`);
|
||||
const p2 = overlay.addSkeleton({
|
||||
atlasPath: "assets/celestial-circus-pma.atlas",
|
||||
skeletonPath: "assets/celestial-circus-pro.skel",
|
||||
animation: 'swing',
|
||||
scale: .5,
|
||||
}, divs);
|
||||
},
|
||||
[
|
||||
{
|
||||
element: divs2[0],
|
||||
mode: 'origin',
|
||||
showBounds: true,
|
||||
xAxis: .5,
|
||||
yAxis: 1,
|
||||
// offsetX: 100
|
||||
},
|
||||
{
|
||||
element: divs2[1],
|
||||
mode: 'origin',
|
||||
showBounds: true,
|
||||
offsetX: 100,
|
||||
offsetY: -50
|
||||
},
|
||||
],);
|
||||
|
||||
p2.then(({ state }) => state.setAnimation(1, "eyeblink", true));
|
||||
|
||||
const divs3 = document.querySelectorAll(`[div-spine3]`);
|
||||
const p3 = overlay.addSkeleton({
|
||||
atlasPath: "assets/raptor-pma.atlas",
|
||||
skeletonPath: "assets/raptor-pro.skel",
|
||||
animation: 'walk',
|
||||
scale: .5,
|
||||
}, divs3);
|
||||
|
||||
const divs4 = document.querySelectorAll(`[div-spine4]`);
|
||||
const p4 = overlay.addSkeleton({
|
||||
atlasPath: "assets/tank-pma.atlas",
|
||||
skeletonPath: "assets/tank-pro.skel",
|
||||
animation: 'shoot',
|
||||
scale: .5,
|
||||
}, divs4[0]);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
// Drag utility
|
||||
|
||||
|
||||
p.then(({ skeleton, state }) => {
|
||||
state.setAnimation(0, "walk", true);
|
||||
})
|
||||
function makeDraggable(element) {
|
||||
let isDragging = false;
|
||||
let startX, startY;
|
||||
let originalX, originalY;
|
||||
|
||||
element.addEventListener('pointerdown', startDragging);
|
||||
document.addEventListener('pointermove', drag);
|
||||
document.addEventListener('pointerup', stopDragging);
|
||||
|
||||
function startDragging(e) {
|
||||
e.preventDefault();
|
||||
if (e.target === document.getElementById('resizeHandle')) return;
|
||||
|
||||
isDragging = true;
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
|
||||
const translate = element.style.transform;
|
||||
if (translate !== '') {
|
||||
const translateValues = translate.match(/translate\(([^)]+)\)/)[1].split(', ');
|
||||
originalX = parseFloat(translateValues[0]);
|
||||
originalY = parseFloat(translateValues[1]);
|
||||
} else {
|
||||
originalX = 0;
|
||||
originalY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function drag(e) {
|
||||
if (!isDragging) return;
|
||||
|
||||
const deltaX = e.clientX - startX;
|
||||
const deltaY = e.clientY - startY;
|
||||
|
||||
element.style.transform = `translate(${originalX + deltaX}px, ${originalY + deltaY}px)`;
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function stopDragging(e) {
|
||||
isDragging = false;
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
makeDraggable(document.getElementById('spineboy1'));
|
||||
makeDraggable(document.getElementById('spineboy2'));
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
// Resize utility
|
||||
|
||||
const resizableDiv = document.getElementById('spineboy1');
|
||||
const resizeHandle = document.getElementById('resizeHandle');
|
||||
let isResizing = false;
|
||||
let startX, startY, startWidth, startHeight;
|
||||
|
||||
resizeHandle.addEventListener('pointerdown', initResize);
|
||||
|
||||
function initResize(e) {
|
||||
isResizing = true;
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
startWidth = resizableDiv.offsetWidth;
|
||||
startHeight = resizableDiv.offsetHeight;
|
||||
document.addEventListener('pointermove', resize);
|
||||
document.addEventListener('pointerup', stopResize);
|
||||
}
|
||||
|
||||
function resize(e) {
|
||||
if (!isResizing) return;
|
||||
const width = startWidth + (e.clientX - startX);
|
||||
const height = startHeight + (e.clientY - startY);
|
||||
resizableDiv.style.width = width + 'px';
|
||||
resizableDiv.style.height = height + 'px';
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
isResizing = false;
|
||||
document.removeEventListener('pointermove', resize);
|
||||
document.removeEventListener('pointerup', stopResize);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@ -27,21 +27,50 @@
|
||||
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { SpineCanvas, SpineCanvasApp, AtlasAttachmentLoader, SkeletonBinary, SkeletonJson, Skeleton, AnimationState, AnimationStateData, Physics, Vector3, ResizeMode, Color } from "./index.js";
|
||||
import { SpineCanvas, SpineCanvasApp, AtlasAttachmentLoader, SkeletonBinary, SkeletonJson, Skeleton, Animation, AnimationState, AnimationStateData, Physics, Vector2, Vector3, ResizeMode, Color, MixBlend, MixDirection, SceneRenderer, SkeletonData } from "./index.js";
|
||||
|
||||
/** Manages the life-cycle and WebGL context of a {@link SpineCanvasApp}. The app loads
|
||||
* assets and initializes itself, then updates and renders its state at the screen refresh rate. */
|
||||
interface Rectangle {
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
}
|
||||
|
||||
interface OverlaySkeletonOptions {
|
||||
atlasPath: string,
|
||||
skeletonPath: string,
|
||||
scale: number,
|
||||
animation?: string,
|
||||
skeletonData?: SkeletonData,
|
||||
}
|
||||
|
||||
interface OverlayHTMLOptions {
|
||||
element: HTMLElement,
|
||||
mode?: OverlayElementMode,
|
||||
showBounds?: boolean,
|
||||
offsetX?: number,
|
||||
offsetY?: number,
|
||||
xAxis?: number,
|
||||
yAxis?: number,
|
||||
}
|
||||
|
||||
type OverlayElementMode = 'inside' | 'origin';
|
||||
|
||||
/** Manages the life-cycle and WebGL context of a {@link SpineCanvasOverlay}. */
|
||||
export class SpineCanvasOverlay {
|
||||
|
||||
private spineCanvas:SpineCanvas;
|
||||
private canvas:HTMLCanvasElement;
|
||||
|
||||
private skeletonList = new Array<{ skeleton: Skeleton, state: AnimationState, htmlElements: Array<HTMLElement>}>();
|
||||
private skeletonList = new Array<{
|
||||
skeleton: Skeleton,
|
||||
state: AnimationState,
|
||||
bounds: Rectangle,
|
||||
htmlOptionsList: Array<OverlayHTMLOptions>,
|
||||
}>();
|
||||
|
||||
private disposed = false;
|
||||
|
||||
|
||||
|
||||
/** Constructs a new spine canvas, rendering to the provided HTML canvas. */
|
||||
constructor () {
|
||||
this.canvas = document.createElement('canvas');
|
||||
@ -60,11 +89,12 @@ export class SpineCanvasOverlay {
|
||||
resizeObserver.observe(document.body);
|
||||
|
||||
const red = new Color(1, 0, 0, 1);
|
||||
const blue = new Color(0, 0, 1, 1);
|
||||
const spineCanvasApp: SpineCanvasApp = {
|
||||
|
||||
update: (canvas: SpineCanvas, delta: number) => {
|
||||
this.skeletonList.forEach(({ skeleton, state, htmlElements }) => {
|
||||
if (htmlElements.length === 0) return;
|
||||
this.skeletonList.forEach(({ skeleton, state, htmlOptionsList }) => {
|
||||
if (htmlOptionsList.length === 0) return;
|
||||
state.update(delta);
|
||||
state.apply(skeleton);
|
||||
skeleton.update(delta);
|
||||
@ -81,29 +111,73 @@ export class SpineCanvasOverlay {
|
||||
renderer.camera.worldToScreen(vec3, canvas.htmlCanvas.clientWidth, canvas.htmlCanvas.clientHeight);
|
||||
|
||||
const devicePixelRatio = window.devicePixelRatio;
|
||||
this.skeletonList.forEach(({ skeleton, htmlElements }) => {
|
||||
if (htmlElements.length === 0) return;
|
||||
const tempVector = new Vector3();
|
||||
this.skeletonList.forEach(({ skeleton, htmlOptionsList, bounds }) => {
|
||||
if (htmlOptionsList.length === 0) return;
|
||||
|
||||
htmlElements.forEach((div) => {
|
||||
let { x: ax, y: ay, width: aw, height: ah } = bounds;
|
||||
|
||||
const bounds = div.getBoundingClientRect();
|
||||
const x = (bounds.x + window.scrollX - vec3.x) * devicePixelRatio;
|
||||
const y = (bounds.y + window.scrollY - vec3.y) * devicePixelRatio;
|
||||
htmlOptionsList.forEach(({ element, mode, showBounds, offsetX = 0, offsetY = 0, xAxis = 0, yAxis = 0 }) => {
|
||||
|
||||
const divBounds = element.getBoundingClientRect();
|
||||
let x = 0, y = 0;
|
||||
if (mode === 'inside') {
|
||||
// scale ratio
|
||||
const scaleWidth = divBounds.width * devicePixelRatio / aw;
|
||||
const scaleHeight = divBounds.height * devicePixelRatio / ah;
|
||||
|
||||
// attempt to use width ratio
|
||||
let ratio = scaleWidth;
|
||||
let scaledW = aw * ratio;
|
||||
let scaledH = ah * ratio;
|
||||
|
||||
// if scaled height is bigger than div height, use height ratio instead
|
||||
if (scaledH > divBounds.height * devicePixelRatio) ratio = scaleHeight;
|
||||
|
||||
const scaledX = (ax + aw / 2) * ratio;
|
||||
const scaledY = (ay + ah / 2) * ratio;
|
||||
|
||||
const divX = divBounds.x + divBounds.width / 2 + window.scrollX;
|
||||
const divY = divBounds.y - 1 + divBounds.height / 2 + window.scrollY;
|
||||
|
||||
tempVector.set(divX, divY, 0);
|
||||
renderer.camera.screenToWorld(tempVector, canvas.htmlCanvas.clientWidth, canvas.htmlCanvas.clientHeight);
|
||||
|
||||
x = tempVector.x - scaledX;
|
||||
y = tempVector.y - scaledY;
|
||||
|
||||
skeleton.scaleX = ratio;
|
||||
skeleton.scaleY = ratio;
|
||||
|
||||
if (showBounds) {
|
||||
renderer.circle(true, tempVector.x, tempVector.y, 10, blue);
|
||||
renderer.rect(false, ax * ratio + x + offsetX, ay * ratio + y + offsetY, aw * ratio, ah * ratio, blue);
|
||||
}
|
||||
|
||||
} else {
|
||||
const divX = divBounds.x + divBounds.width * xAxis + window.scrollX;
|
||||
const divY = divBounds.y + divBounds.height * yAxis + window.scrollY;
|
||||
|
||||
tempVector.set(divX, divY, 0);
|
||||
renderer.camera.screenToWorld(tempVector, canvas.htmlCanvas.clientWidth, canvas.htmlCanvas.clientHeight);
|
||||
|
||||
x = tempVector.x;
|
||||
y = tempVector.y;
|
||||
|
||||
if (showBounds) {
|
||||
// show skeleton root
|
||||
const root = skeleton.getRootBone()!;
|
||||
renderer.circle(true, x + root.x + offsetX, y + root.y + offsetY, 10, red);
|
||||
}
|
||||
}
|
||||
|
||||
renderer.drawSkeleton(skeleton, true, -1, -1, (vertices, size, vertexSize) => {
|
||||
for (let i = 0; i < size; i+=vertexSize) {
|
||||
vertices[i] = vertices[i] + x;
|
||||
vertices[i+1] = vertices[i+1] - y;
|
||||
vertices[i] = vertices[i] + x + offsetX;
|
||||
vertices[i+1] = vertices[i+1] + y + offsetY;
|
||||
}
|
||||
});
|
||||
|
||||
// show skeleton center (root)
|
||||
const root = skeleton.getRootBone()!;
|
||||
const vec3Root = new Vector3(root.x, root.y);
|
||||
renderer.camera.worldToScreen(vec3Root, canvas.htmlCanvas.clientWidth, canvas.htmlCanvas.clientHeight);
|
||||
const rootX = (vec3Root.x - vec3.x) * devicePixelRatio;
|
||||
const rootY = (vec3Root.y - vec3.y) * devicePixelRatio;
|
||||
renderer.circle(true, x + rootX, -y + rootY, 20, red);
|
||||
});
|
||||
|
||||
});
|
||||
@ -118,6 +192,7 @@ export class SpineCanvasOverlay {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Reject error
|
||||
public async loadBinary(path: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.spineCanvas.assetManager.loadBinary(path, () => resolve(null));
|
||||
@ -137,10 +212,10 @@ export class SpineCanvasOverlay {
|
||||
}
|
||||
|
||||
public async addSkeleton(
|
||||
skeletonOptions: { atlasPath: string, skeletonPath: string, scale: number },
|
||||
elements: Array<HTMLElement> = [],
|
||||
skeletonOptions: OverlaySkeletonOptions,
|
||||
htmlOptionsList: Array<OverlayHTMLOptions> | Array<HTMLElement> | HTMLElement | NodeList = [],
|
||||
) {
|
||||
const { atlasPath, skeletonPath, scale } = skeletonOptions;
|
||||
const { atlasPath, skeletonPath, scale = 1, animation, skeletonData: skeletonDataInput } = skeletonOptions;
|
||||
const isBinary = skeletonPath.endsWith(".skel");
|
||||
await Promise.all([
|
||||
isBinary ? this.loadBinary(skeletonPath) : this.loadJson(skeletonPath),
|
||||
@ -154,17 +229,104 @@ export class SpineCanvasOverlay {
|
||||
skeletonLoader.scale = scale;
|
||||
|
||||
const skeletonFile = this.spineCanvas.assetManager.require(skeletonPath);
|
||||
const skeletonData = skeletonLoader.readSkeletonData(skeletonFile);
|
||||
const skeletonData = skeletonDataInput ?? skeletonLoader.readSkeletonData(skeletonFile);
|
||||
|
||||
const skeleton = new Skeleton(skeletonData);
|
||||
const animationStateData = new AnimationStateData(skeletonData);
|
||||
const state = new AnimationState(animationStateData);
|
||||
|
||||
this.skeletonList.push({ skeleton, state, htmlElements: [...elements] });
|
||||
let animationData;
|
||||
if (animation) {
|
||||
state.setAnimation(0, animation, true);
|
||||
animationData = animation ? skeleton.data.findAnimation(animation)! : undefined;
|
||||
}
|
||||
const bounds = this.calculateAnimationViewport(skeleton, animationData);
|
||||
|
||||
let list: Array<OverlayHTMLOptions>;
|
||||
if (htmlOptionsList instanceof HTMLElement) htmlOptionsList = [htmlOptionsList] as Array<HTMLElement>;
|
||||
if (htmlOptionsList instanceof NodeList) htmlOptionsList = Array.from(htmlOptionsList) as Array<HTMLElement>;
|
||||
|
||||
if (htmlOptionsList.length > 0 && htmlOptionsList[0] instanceof HTMLElement) {
|
||||
list = htmlOptionsList.map(element => ({ element: element } as OverlayHTMLOptions));
|
||||
} else {
|
||||
list = htmlOptionsList as Array<OverlayHTMLOptions>;
|
||||
}
|
||||
|
||||
const mapList = list.map(({ element, mode: givenMode, showBounds = false, offsetX = 0, offsetY = 0, xAxis = 0, yAxis = 0 }, i) => {
|
||||
const mode = givenMode ?? 'inside';
|
||||
if (mode == 'inside' && i > 0) {
|
||||
console.warn("inside option works with multiple html elements only if the elements have the same dimension"
|
||||
+ "This is because the skeleton is scaled to stay into the div."
|
||||
+ "You can call addSkeleton several time (skeleton data can be reuse, if given).");
|
||||
}
|
||||
return {
|
||||
element,
|
||||
mode,
|
||||
showBounds,
|
||||
offsetX,
|
||||
offsetY,
|
||||
xAxis,
|
||||
yAxis,
|
||||
}
|
||||
});
|
||||
this.skeletonList.push({ skeleton, state, bounds, htmlOptionsList: mapList });
|
||||
|
||||
return { skeleton, state }
|
||||
}
|
||||
|
||||
public recalculateBounds(skeleton: Skeleton, state: AnimationState) {
|
||||
const track = state.getCurrent(0);
|
||||
const animation = track?.animation as (Animation | undefined);
|
||||
const bounds = this.calculateAnimationViewport(skeleton, animation);
|
||||
bounds.x /= skeleton.scaleX;
|
||||
bounds.y /= skeleton.scaleY;
|
||||
bounds.width /= skeleton.scaleX;
|
||||
bounds.height /= skeleton.scaleY;
|
||||
const element = this.skeletonList.find(element => element.skeleton === skeleton);
|
||||
if (element) {
|
||||
element.bounds = bounds;
|
||||
}
|
||||
}
|
||||
|
||||
private calculateAnimationViewport (skeleton: Skeleton, animation?: Animation): Rectangle {
|
||||
skeleton.setToSetupPose();
|
||||
|
||||
let offset = new Vector2(), size = new Vector2();
|
||||
const tempArray = new Array<number>(2);
|
||||
if (!animation) {
|
||||
skeleton.updateWorldTransform(Physics.update);
|
||||
skeleton.getBounds(offset, size, tempArray, this.spineCanvas.renderer.skeletonRenderer.getSkeletonClipping());
|
||||
return {
|
||||
x: offset.x,
|
||||
y: offset.y,
|
||||
width: size.x,
|
||||
height: size.y,
|
||||
}
|
||||
}
|
||||
|
||||
let steps = 100, stepTime = animation.duration ? animation.duration / steps : 0, time = 0;
|
||||
let minX = 100000000, maxX = -100000000, minY = 100000000, maxY = -100000000;
|
||||
for (let i = 0; i < steps; i++, time += stepTime) {
|
||||
animation.apply(skeleton, time, time, false, [], 1, MixBlend.setup, MixDirection.mixIn);
|
||||
skeleton.updateWorldTransform(Physics.update);
|
||||
skeleton.getBounds(offset, size, tempArray, this.spineCanvas.renderer.skeletonRenderer.getSkeletonClipping());
|
||||
|
||||
if (!isNaN(offset.x) && !isNaN(offset.y) && !isNaN(size.x) && !isNaN(size.y)) {
|
||||
minX = Math.min(offset.x, minX);
|
||||
maxX = Math.max(offset.x + size.x, maxX);
|
||||
minY = Math.min(offset.y, minY);
|
||||
maxY = Math.max(offset.y + size.y, maxY);
|
||||
} else
|
||||
console.error("Animation bounds are invalid: " + animation.name);
|
||||
}
|
||||
|
||||
return {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
}
|
||||
}
|
||||
|
||||
private updateCanvasSize() {
|
||||
const pageSize = this.getPageSize();
|
||||
@ -192,7 +354,7 @@ export class SpineCanvasOverlay {
|
||||
return { width, height };
|
||||
}
|
||||
|
||||
/** Disposes the app, so the update() and render() functions are no longer called. Calls the dispose() callback.*/
|
||||
// TODO
|
||||
dispose () {
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user