From 7f5b934a649f8b1ee6ea39517baeb8f8e3c0ec6e Mon Sep 17 00:00:00 2001 From: Davide Tantillo Date: Fri, 16 Aug 2024 17:32:21 +0200 Subject: [PATCH] overlay fix scroll cut --- spine-ts/spine-webgl/example/canvas5.html | 367 +++++++++++------- spine-ts/spine-webgl/src/Input.ts | 2 +- .../spine-webgl/src/SpineCanvasOverlay.ts | 95 +++-- 3 files changed, 292 insertions(+), 172 deletions(-) diff --git a/spine-ts/spine-webgl/example/canvas5.html b/spine-ts/spine-webgl/example/canvas5.html index a0cb548ef..63aa8a148 100644 --- a/spine-ts/spine-webgl/example/canvas5.html +++ b/spine-ts/spine-webgl/example/canvas5.html @@ -4,6 +4,7 @@ + JS Library Showcase - + FPS + + + +
+ +
+
+ As a bonus item, you can move you skeleton around just by setting the draggable property to true. +
+
+
+
+ +

 overlay.addSkeleton(
@@ -359,7 +412,7 @@ overlay.addSkeleton(
         animation: 'wings-and-feet',
     },
     {
-        element: document.getElementById(`section7-element`),
+        element: document.getElementById(`section8-element`),
         draggable: true,
     }
 );
@@ -369,7 +422,7 @@ overlay.addSkeleton(
 
     
 
@@ -382,6 +435,7 @@ overlay.addSkeleton(
         
         
         
+        
     
\ No newline at end of file diff --git a/spine-ts/spine-webgl/src/Input.ts b/spine-ts/spine-webgl/src/Input.ts index 36fb4d53a..f542caaf4 100644 --- a/spine-ts/spine-webgl/src/Input.ts +++ b/spine-ts/spine-webgl/src/Input.ts @@ -92,7 +92,7 @@ export class Input { let deltaY = ev.deltaY; if (ev.deltaMode == WheelEvent.DOM_DELTA_LINE) deltaY *= 8; if (ev.deltaMode == WheelEvent.DOM_DELTA_PAGE) deltaY *= 24; - this.listeners.map((listener) => { if (listener.wheel) listener.wheel(e.deltaY); }); + this.listeners.map((listener) => { if (listener.wheel) listener.wheel(ev.deltaY); }); }; element.addEventListener("mousedown", mouseDown, true); diff --git a/spine-ts/spine-webgl/src/SpineCanvasOverlay.ts b/spine-ts/spine-webgl/src/SpineCanvasOverlay.ts index d68d39805..acb36bc40 100644 --- a/spine-ts/spine-webgl/src/SpineCanvasOverlay.ts +++ b/spine-ts/spine-webgl/src/SpineCanvasOverlay.ts @@ -50,7 +50,7 @@ type UpdateSpineFunction = (canvas: SpineCanvas, delta: number, skeleton: Skelet interface OverlayHTMLOptions { element: HTMLElement, mode?: OverlayElementMode, - showBounds?: boolean, + debug?: boolean, offsetX?: number, offsetY?: number, xAxis?: number, @@ -80,6 +80,15 @@ export class SpineCanvasOverlay { private resizeObserver:ResizeObserver; private disposed = false; + // how may pixels to add to the bottom (to avoid cut on edge during scrolling) + private readonly additionalPixelsBottom = 300; + + // how much the canvas is translated above (to avoid cut on edge during scrolling) + private readonly offsetHeight = 100; + + // the actual base translation + private offsetHeightDraw: number; + /** Constructs a new spine canvas, rendering to the provided HTML canvas. */ constructor () { this.canvas = document.createElement('canvas'); @@ -87,11 +96,14 @@ export class SpineCanvasOverlay { this.canvas.style.position = "absolute"; this.canvas.style.top = "0"; this.canvas.style.left = "0"; - this.canvas.style.display = "inline"; this.canvas.style.setProperty("pointer-events", "none"); - // this.canvas.style.width = "100%"; - // this.canvas.style.height = "100%"; + this.offsetHeightDraw = this.offsetHeight; + this.canvas.style.transform =`translate(0px,0px)`; + // this.canvas.style.display = "inline"; + // this.canvas.style.overflow = "hidden"; // useless + // this.canvas.style.setProperty("will-change", "transform"); // performance seems to be even worse with this uncommented this.updateCanvasSize(); + this.scrollHandler(); this.resizeObserver = new ResizeObserver(() => { this.updateCanvasSize(); @@ -99,6 +111,9 @@ export class SpineCanvasOverlay { }); this.resizeObserver.observe(document.body); + window.addEventListener('scroll', this.scrollHandler); + + this.spineCanvas = new SpineCanvas(this.canvas, { app: this.setupSpineCanvasApp() }); this.input = new Input(document.body, false); @@ -148,7 +163,7 @@ export class SpineCanvasOverlay { list = htmlOptionsList as Array; } - const mapList = list.map(({ element, mode: givenMode, showBounds = false, offsetX = 0, offsetY = 0, xAxis = 0, yAxis = 0, draggable = false, }, i) => { + const mapList = list.map(({ element, mode: givenMode, debug = false, offsetX = 0, offsetY = 0, xAxis = 0, yAxis = 0, draggable = false, }, 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" @@ -158,7 +173,7 @@ export class SpineCanvasOverlay { return { element: element as HTMLElement, mode, - showBounds, + debug, offsetX, offsetY, xAxis, @@ -251,15 +266,14 @@ export class SpineCanvasOverlay { skeleton.updateWorldTransform(Physics.update); } }); - // (document.body.querySelector("#fps")! as HTMLElement).innerText = canvas.time.framesPerSecond.toFixed(2) + " fps"; + (document.body.querySelector("#fps")! as HTMLElement).innerText = canvas.time.framesPerSecond.toFixed(2) + " fps"; }, render: (canvas: SpineCanvas) => { + // canvas.clear(1, 0, 0, .1); let renderer = canvas.renderer; renderer.begin(); - // console.log(canvas.gl.getParameter(canvas.gl.MAX_RENDERBUFFER_SIZE)); - const devicePixelRatio = window.devicePixelRatio; const tempVector = new Vector3(); this.skeletonList.forEach(({ skeleton, htmlOptionsList, bounds }) => { @@ -268,10 +282,9 @@ export class SpineCanvasOverlay { let { x: ax, y: ay, width: aw, height: ah } = bounds; htmlOptionsList.forEach((list) => { - const { element, mode, showBounds, offsetX, offsetY, xAxis, yAxis, dragX, dragY } = list; + const { element, mode, debug, offsetX, offsetY, xAxis, yAxis, dragX, dragY } = list; const divBounds = element.getBoundingClientRect(); - - // console.log(divBounds.x, divBounds.y, divBounds.width, divBounds.height) + divBounds.y += this.offsetHeightDraw; let x = 0, y = 0; if (mode === 'inside') { @@ -292,8 +305,10 @@ export class SpineCanvasOverlay { const boundsY = (ay + ah / 2) * ratio; // get the center of the div in world coordinate - const divX = divBounds.x + divBounds.width / 2 + window.scrollX; - const divY = divBounds.y - 1 + divBounds.height / 2 + window.scrollY; + // const divX = divBounds.x + divBounds.width / 2 + window.scrollX; + // const divY = divBounds.y - 1 + divBounds.height / 2 + window.scrollY; + const divX = divBounds.x + divBounds.width / 2; + const divY = divBounds.y - 1 + divBounds.height / 2; this.screenToWorld(tempVector, divX, divY); // get vertices offset: calculate the distance between div center and bounds center @@ -308,8 +323,10 @@ export class SpineCanvasOverlay { // TODO: window.devicePixelRatio to manage browser zoom // get the center of the div in world coordinate - const divX = divBounds.x + divBounds.width * xAxis + window.scrollX; - const divY = divBounds.y + divBounds.height * yAxis + window.scrollY; + // const divX = divBounds.x + divBounds.width * xAxis + window.scrollX; + // const divY = divBounds.y + divBounds.height * yAxis + window.scrollY; + const divX = divBounds.x + divBounds.width * xAxis; + const divY = divBounds.y + divBounds.height * yAxis; this.screenToWorld(tempVector, divX, divY); // console.log(tempVector.x, tempVector.y) // console.log(window.devicePixelRatio) @@ -323,9 +340,6 @@ export class SpineCanvasOverlay { list.worldOffsetX = x + offsetX + dragX; list.worldOffsetY = y + offsetY + dragY; - console.log(list.worldOffsetY) - // console.log("----") - renderer.drawSkeleton(skeleton, true, -1, -1, (vertices, size, vertexSize) => { for (let i = 0; i < size; i+=vertexSize) { vertices[i] = vertices[i] + list.worldOffsetX; @@ -334,7 +348,8 @@ export class SpineCanvasOverlay { }); // drawing debug stuff - if (showBounds) { + if (debug) { + // if (true) { // show bounds and its center renderer.rect(false, ax * skeleton.scaleX + list.worldOffsetX, @@ -378,7 +393,7 @@ export class SpineCanvasOverlay { this.input.addListener({ down: (x, y, ev) => { const originalEvent = ev instanceof MouseEvent ? ev : ev!.changedTouches[0]; - tempVectorInput.set(originalEvent.pageX, originalEvent.pageY, 0); + tempVectorInput.set(originalEvent.pageX - window.scrollX, originalEvent.pageY - window.scrollY + this.offsetHeightDraw, 0); this.spineCanvas.renderer.camera.screenToWorld(tempVectorInput, this.canvas.clientWidth, this.canvas.clientHeight); this.skeletonList.forEach(({ htmlOptionsList, bounds, skeleton }) => { htmlOptionsList.forEach((element) => { @@ -404,7 +419,7 @@ export class SpineCanvasOverlay { }, dragged: (x, y, ev) => { const originalEvent = ev instanceof MouseEvent ? ev : ev!.changedTouches[0]; - tempVectorInput.set(originalEvent.pageX, originalEvent.pageY, 0); + tempVectorInput.set(originalEvent.pageX - window.scrollX, originalEvent.pageY - window.scrollY + this.offsetHeightDraw, 0); this.spineCanvas.renderer.camera.screenToWorld(tempVectorInput, this.canvas.clientWidth, this.canvas.clientHeight); let dragX = tempVectorInput.x - prevX; let dragY = tempVectorInput.y - prevY; @@ -436,13 +451,40 @@ export class SpineCanvasOverlay { } /* - * Resize utilities + * Resize/scroll utilities */ private updateCanvasSize() { - const pageSize = this.getPageSize(); - this.canvas.style.width = pageSize.width + "px"; - this.canvas.style.height = pageSize.height + "px"; + const displayWidth = document.documentElement.clientWidth; + const displayHeight = document.documentElement.clientHeight; + this.canvas.style.width = displayWidth + "px"; + this.canvas.style.height = displayHeight + this.additionalPixelsBottom + "px"; + } + + private scrollHandler = () => { + const { width, height } = this.getPageSize(); + + const scrollPositionX = window.scrollX; + const floatingDivWidth = this.canvas.offsetWidth; + const maxTranslationX = width - floatingDivWidth; + const translationX = Math.min(scrollPositionX, maxTranslationX); + + const scrollPositionY = window.scrollY; + const floatingDivHeight = this.canvas.offsetHeight; + const maxTranslation = height - floatingDivHeight + this.offsetHeight; + const translationY = Math.min(scrollPositionY, maxTranslation) - this.offsetHeight; + + const delta = scrollPositionY - maxTranslation + this.offsetHeightDraw = this.offsetHeight; + if (delta > 0) { + this.offsetHeightDraw += delta + 1; + } + + // translate should be faster + this.canvas.style.transform =`translate(${translationX}px,${translationY}px)`; + // this.canvas.style.top = `${this.currentTranslateY}px`; + // this.canvas.style.left = `${this.currentTranslateX}px`; + } private getPageSize() { @@ -512,7 +554,6 @@ export class SpineCanvasOverlay { private screenToWorld(vec: Vector3, x: number, y: number) { vec.set(x, y, 0); this.spineCanvas.renderer.camera.screenToWorld(vec, this.canvas.clientWidth, this.canvas.clientHeight); - // console.log(this.canvas.clientWidth, this.canvas.clientHeight); } private inside(point: { x: number; y: number }, rectangle: Rectangle): boolean {