Made some changes to make it work on old browsers.

This commit is contained in:
Davide Tantillo 2024-09-27 12:30:09 +02:00
parent 27edd8a284
commit e26034426a
4 changed files with 66 additions and 84 deletions

View File

@ -17,7 +17,7 @@
last = new spine.Vector3(); last = new spine.Vector3();
new spine.SpinePlayer("player", { new spine.SpinePlayer("player", {
skeleton: "assets/celestial-circus-pro.skel", skeleton: "assets/celestial-circus-pro.skel",
url: "assets/celestial-circus-pma.atlas", atlas: "assets/celestial-circus-pma.atlas",
showControls: true, showControls: true,
animation: "swing", animation: "swing",
success: (player) => { success: (player) => {

View File

@ -14,6 +14,7 @@
} }
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
font-size: 16px;
} }
.section { .section {
display: flex; display: flex;
@ -1712,7 +1713,7 @@ stretchyman.update = (canvas, delta, skeleton, state) => {
atlas="assets/celestial-circus-pma.atlas" atlas="assets/celestial-circus-pma.atlas"
skeleton="assets/celestial-circus-pro.skel" skeleton="assets/celestial-circus-pro.skel"
clip clip
draggable isdraggable
></spine-widget> ></spine-widget>
<script> <script>
@ -1779,7 +1780,7 @@ stretchyman.update = (canvas, delta, skeleton, state) => {
skeleton="assets/celestial-circus-pro.skel" skeleton="assets/celestial-circus-pro.skel"
animation="wings-and-feet" animation="wings-and-feet"
clip clip
draggable isdraggable
></spine-widget> ></spine-widget>
</div> </div>
@ -1814,14 +1815,14 @@ stretchyman.update = (canvas, delta, skeleton, state) => {
<div class="split-top split"> <div class="split-top split">
<div class="split-left"> <div class="split-left">
As a bonus item, you can move you skeleton around just by setting the <code>draggable</code> property to <code>true</code>. As a bonus item, you can move you skeleton around just by setting the <code>isdraggable</code> property to <code>true</code>.
</div> </div>
<div class="split-right"> <div class="split-right">
<spine-widget <spine-widget
atlas="assets/celestial-circus-pma.atlas" atlas="assets/celestial-circus-pma.atlas"
skeleton="assets/celestial-circus-pro.skel" skeleton="assets/celestial-circus-pro.skel"
animation="wings-and-feet" animation="wings-and-feet"
draggable isdraggable
></spine-widget> ></spine-widget>
</div> </div>
</div> </div>
@ -1833,7 +1834,7 @@ stretchyman.update = (canvas, delta, skeleton, state) => {
atlas="assets/celestial-circus-pma.atlas" atlas="assets/celestial-circus-pma.atlas"
skeleton="assets/celestial-circus-pro.skel" skeleton="assets/celestial-circus-pro.skel"
animation="wings-and-feet" animation="wings-and-feet"
draggable="true" isdraggable="true"
></spine-widget>` ></spine-widget>`
);</script> );</script>
</code></pre> </code></pre>

View File

@ -36,11 +36,11 @@ export class Input {
touch1: Touch | null = null; touch1: Touch | null = null;
initialPinchDistance = 0; initialPinchDistance = 0;
private listeners = new Array<InputListener>(); private listeners = new Array<InputListener>();
private preventDefault: boolean; private autoPreventDefault: boolean;
constructor (element: HTMLElement, preventDefault = true) { constructor (element: HTMLElement, autoPreventDefault = true) {
this.element = element; this.element = element;
this.preventDefault = preventDefault; this.autoPreventDefault = autoPreventDefault;
this.setupCallbacks(element); this.setupCallbacks(element);
} }
@ -48,7 +48,7 @@ export class Input {
let mouseDown = (ev: UIEvent) => { let mouseDown = (ev: UIEvent) => {
if (ev instanceof MouseEvent) { if (ev instanceof MouseEvent) {
let rect = element.getBoundingClientRect(); let rect = element.getBoundingClientRect();
this.mouseX = ev.clientX - rect.left;; this.mouseX = ev.clientX - rect.left;
this.mouseY = ev.clientY - rect.top; this.mouseY = ev.clientY - rect.top;
this.buttonDown = true; this.buttonDown = true;
this.listeners.map((listener) => { if (listener.down) listener.down(this.mouseX, this.mouseY, ev); }); this.listeners.map((listener) => { if (listener.down) listener.down(this.mouseX, this.mouseY, ev); });
@ -88,7 +88,7 @@ export class Input {
} }
let mouseWheel = (ev: WheelEvent) => { let mouseWheel = (ev: WheelEvent) => {
if (this.preventDefault) ev.preventDefault(); if (this.autoPreventDefault) ev.preventDefault();
let deltaY = ev.deltaY; let deltaY = ev.deltaY;
if (ev.deltaMode == WheelEvent.DOM_DELTA_LINE) deltaY *= 8; if (ev.deltaMode == WheelEvent.DOM_DELTA_LINE) deltaY *= 8;
if (ev.deltaMode == WheelEvent.DOM_DELTA_PAGE) deltaY *= 24; if (ev.deltaMode == WheelEvent.DOM_DELTA_PAGE) deltaY *= 24;
@ -125,8 +125,8 @@ export class Input {
this.listeners.map((listener) => { if (listener.zoom) listener.zoom(this.initialPinchDistance, this.initialPinchDistance, ev) }); this.listeners.map((listener) => { if (listener.zoom) listener.zoom(this.initialPinchDistance, this.initialPinchDistance, ev) });
} }
} }
if (this.preventDefault) ev.preventDefault(); if (this.autoPreventDefault) ev.preventDefault();
}, { passive: this.preventDefault }); }, { passive: false, capture: false });
element.addEventListener("touchmove", (ev: TouchEvent) => { element.addEventListener("touchmove", (ev: TouchEvent) => {
if (this.touch0) { if (this.touch0) {
@ -154,8 +154,8 @@ export class Input {
this.listeners.map((listener) => { if (listener.zoom) listener.zoom(this.initialPinchDistance, distance, ev) }); this.listeners.map((listener) => { if (listener.zoom) listener.zoom(this.initialPinchDistance, distance, ev) });
} }
} }
if (this.preventDefault) ev.preventDefault(); if (this.autoPreventDefault) ev.preventDefault();
}, { passive: this.preventDefault }); }, { passive: false, capture: false });
let touchEnd = (ev: TouchEvent) => { let touchEnd = (ev: TouchEvent) => {
if (this.touch0) { if (this.touch0) {
@ -191,9 +191,9 @@ export class Input {
} }
} }
} }
if (this.preventDefault) ev.preventDefault(); if (this.autoPreventDefault) ev.preventDefault();
}; };
element.addEventListener("touchend", touchEnd, false); element.addEventListener("touchend", touchEnd, { passive: false, capture: false });
element.addEventListener("touchcancel", touchEnd); element.addEventListener("touchcancel", touchEnd);
} }

View File

@ -111,7 +111,7 @@ interface WidgetAttributes {
offsetY: number offsetY: number
width: number width: number
height: number height: number
draggable: boolean isDraggable: boolean
debug: boolean debug: boolean
identifier: string identifier: string
manualStart: boolean manualStart: boolean
@ -280,9 +280,9 @@ export class SpineWebComponentWidget extends HTMLElement implements WidgetAttrib
/** /**
* If true, the widget is draggable * If true, the widget is draggable
* Connected to `draggable` attribute. * Connected to `isdraggable` attribute.
*/ */
public draggable = false; public isDraggable = false;
/** /**
* If true, some convenience elements are drawn to show the skeleton world origin (green), * If true, some convenience elements are drawn to show the skeleton world origin (green),
@ -488,7 +488,7 @@ export class SpineWebComponentWidget extends HTMLElement implements WidgetAttrib
skin: { propertyName: "skin", type: "string" }, skin: { propertyName: "skin", type: "string" },
width: { propertyName: "width", type: "number", defaultValue: -1 }, width: { propertyName: "width", type: "number", defaultValue: -1 },
height: { propertyName: "height", type: "number", defaultValue: -1 }, height: { propertyName: "height", type: "number", defaultValue: -1 },
draggable: { propertyName: "draggable", type: "boolean" }, isdraggable: { propertyName: "isDraggable", type: "boolean" },
"x-axis": { propertyName: "xAxis", type: "number" }, "x-axis": { propertyName: "xAxis", type: "number" },
"y-axis": { propertyName: "yAxis", type: "number" }, "y-axis": { propertyName: "yAxis", type: "number" },
"offset-x": { propertyName: "offsetX", type: "number" }, "offset-x": { propertyName: "offsetX", type: "number" },
@ -505,29 +505,7 @@ export class SpineWebComponentWidget extends HTMLElement implements WidgetAttrib
} }
static get observedAttributes(): string[] { static get observedAttributes(): string[] {
return [ return Object.keys(SpineWebComponentWidget.attributesDescription);
"atlas", // atlasPath
"skeleton", // skeletonPath
"scale", // scale
"animation", // animation
"skin", // skin
"fit", // fit
"width", // width
"height", // height
"draggable", // draggable
"mode", // mode
"x-axis", // xAxis
"y-axis", // yAxis
"offset-x", // offsetX
"offset-y", // offsetY
"identifier", // identifier
"debug", // debug
"manual-start", // manualStart
"spinner", // loadingSpinner
"pages", // pages
"offscreen", // offScreenUpdateBehaviour
"clip", // clip
];
} }
constructor() { constructor() {
@ -843,7 +821,6 @@ class SpineWebComponentOverlay extends HTMLElement {
public skeletonList = new Array<SpineWebComponentWidget>(); public skeletonList = new Array<SpineWebComponentWidget>();
private intersectionObserver? : IntersectionObserver; private intersectionObserver? : IntersectionObserver;
private resizeObserver:ResizeObserver;
private input: Input; 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 cuttin" on fast scrolling
@ -896,40 +873,61 @@ class SpineWebComponentOverlay extends HTMLElement {
const context = new ManagedWebGLRenderingContext(this.canvas, { alpha: true }); const context = new ManagedWebGLRenderingContext(this.canvas, { alpha: true });
this.renderer = new SceneRenderer(this.canvas, context); this.renderer = new SceneRenderer(this.canvas, context);
this.assetManager = new AssetManager(context); this.assetManager = new AssetManager(context);
this.input = new Input(this.canvas); this.input = new Input(this.canvas, false);
this.setupRenderingElements(); this.setupRenderingElements();
this.updateCanvasSize();
this.zoomHandler();
// translateCanvas starts a requestAnimationFrame loop
this.translateCanvas();
this.overflowLeftSize = this.overflowLeft * document.documentElement.clientWidth; this.overflowLeftSize = this.overflowLeft * document.documentElement.clientWidth;
this.overflowTopSize = this.overflowTop * document.documentElement.clientHeight; this.overflowTopSize = this.overflowTop * document.documentElement.clientHeight;
// resize and zoom window.addEventListener('resize', () => {
// TODO: should I use the resize event?
this.resizeObserver = new ResizeObserver(() => {
this.updateCanvasSize(); this.updateCanvasSize();
this.zoomHandler(); this.zoomHandler();
}); });
this.resizeObserver.observe(document.body);
const screen = window.screen; window.screen.orientation.onchange = () => {
screen.orientation.onchange = () => {
this.updateCanvasSize(); this.updateCanvasSize();
// after an orientation change the scrolling changes, but the scroll event does not fire // after an orientation change the scrolling changes, but the scroll event does not fire
this.scrollHandler(); this.scrollHandler();
} }
window.addEventListener("scroll", this.scrollHandler); window.addEventListener("scroll", this.scrollHandler);
this.scrollHandler();
window.onload = () => {
this.updateCanvasSize();
this.zoomHandler();
// translateCanvas starts a requestAnimationFrame loop
this.translateCanvas();
this.scrollHandler();
};
this.input = new Input(document.body, false); this.input = new Input(document.body, false);
this.setupDragUtility(); this.setupDragUtility();
} }
connectedCallback(): void {
this.intersectionObserver = new IntersectionObserver((widgets) => {
widgets.forEach(({ isIntersecting, target, intersectionRatio }) => {
const widget = this.skeletonList.find(w => w.getHTMLElementReference() == target);
if (!widget) return;
// old browsers do not have isIntersecting
if (isIntersecting === undefined) {
isIntersecting = intersectionRatio > 0;
}
widget.onScreen = isIntersecting;
if (isIntersecting) {
widget.onScreenFunction(widget);
}
})
}, { rootMargin: "30px 20px 30px 20px" });
}
disconnectedCallback(): void {
}
addWidget(widget: SpineWebComponentWidget) { addWidget(widget: SpineWebComponentWidget) {
this.skeletonList.push(widget); this.skeletonList.push(widget);
this.intersectionObserver!.observe(widget.getHTMLElementReference()); this.intersectionObserver!.observe(widget.getHTMLElementReference());
@ -1028,12 +1026,13 @@ class SpineWebComponentOverlay extends HTMLElement {
const devicePixelRatio = window.devicePixelRatio; const devicePixelRatio = window.devicePixelRatio;
const tempVector = new Vector3(); const tempVector = new Vector3();
this.skeletonList.forEach((widget) => { this.skeletonList.forEach((widget) => {
const { skeleton, bounds, mode, debug, offsetX, offsetY, xAxis, yAxis, dragX, dragY, fit, loadingSpinner, onScreen, loading, clip, draggable } = widget; const { skeleton, bounds, mode, debug, offsetX, offsetY, xAxis, yAxis, dragX, dragY, fit, loadingSpinner, onScreen, loading, clip, isDraggable } = widget;
if ((!onScreen && dragX === 0 && dragY === 0)) return; if ((!onScreen && dragX === 0 && dragY === 0)) return;
const divBounds = widget.getHTMLElementReference().getBoundingClientRect(); const divBounds = widget.getHTMLElementReference().getBoundingClientRect();
divBounds.x += this.overflowLeftSize; // need to use left and top, because x and y are not available on older browser
divBounds.y += this.overflowTopSize; divBounds.x = divBounds.left + this.overflowLeftSize;
divBounds.y = divBounds.top + this.overflowTopSize;
let divOriginX = 0; let divOriginX = 0;
let divOriginY = 0; let divOriginY = 0;
@ -1137,8 +1136,8 @@ class SpineWebComponentOverlay extends HTMLElement {
} }
}); });
// store the draggable surface to make darg logic easier // store the draggable surface to make drag logic easier
if (draggable) { if (isDraggable) {
let { x: ax, y: ay, width: aw, height: ah } = bounds!; let { x: ax, y: ay, width: aw, height: ah } = bounds!;
this.worldToScreen(tempVector, ax * skeleton.scaleX + worldOffsetX, ay * skeleton.scaleY + worldOffsetY); this.worldToScreen(tempVector, ax * skeleton.scaleX + worldOffsetX, ay * skeleton.scaleY + worldOffsetY);
widget.dragBoundsRectangle.x = tempVector.x + window.scrollX; widget.dragBoundsRectangle.x = tempVector.x + window.scrollX;
@ -1211,24 +1210,6 @@ class SpineWebComponentOverlay extends HTMLElement {
const transparentWhite = new Color(1, 1, 1, .3); const transparentWhite = new Color(1, 1, 1, .3);
} }
connectedCallback(): void {
this.intersectionObserver = new IntersectionObserver((widgets) => {
widgets.forEach(({ isIntersecting, target }) => {
const widget = this.skeletonList.find(w => w.getHTMLElementReference() == target);
if (!widget) return;
widget.onScreen = isIntersecting;
if (isIntersecting) {
widget.onScreenFunction(widget);
}
})
}, { rootMargin: "30px 20px 30px 20px" });
}
disconnectedCallback(): void {
}
// TODO: drag is bugged when zoom on browser (just zoom and activare debug to see the drag surface has some offset)
private setupDragUtility() { private setupDragUtility() {
// TODO: we should use document - body might have some margin that offset the click events - Meanwhile I take event pageX/Y // TODO: we should use document - body might have some margin that offset the click events - Meanwhile I take event pageX/Y
const point: Point = { x: 0, y: 0 }; const point: Point = { x: 0, y: 0 };
@ -1246,7 +1227,7 @@ class SpineWebComponentOverlay extends HTMLElement {
down: (x, y, ev) => { down: (x, y, ev) => {
const input = getInput(ev); const input = getInput(ev);
this.skeletonList.forEach(widget => { this.skeletonList.forEach(widget => {
if (!widget.draggable || (!widget.onScreen && widget.dragX === 0 && widget.dragY === 0)) return; if (!widget.isDraggable || (!widget.onScreen && widget.dragX === 0 && widget.dragY === 0)) return;
if (inside(input, widget.dragBoundsRectangle)) { if (inside(input, widget.dragBoundsRectangle)) {
widget.dragging = true; widget.dragging = true;
ev?.preventDefault(); ev?.preventDefault();