Scroll should be resolved

This commit is contained in:
Davide Tantillo 2024-08-20 15:06:06 +02:00
parent 394b97b105
commit d92046f325
2 changed files with 177 additions and 200 deletions

View File

@ -169,8 +169,8 @@ overlay.addSkeleton(
{ {
element: document.getElementById(`section2-element`), element: document.getElementById(`section2-element`),
mode: 'origin', mode: 'origin',
xAxis: .25, xAxis: .5,
yAxis: .75, yAxis: 1,
}, },
); );
</code></pre> </code></pre>
@ -474,20 +474,20 @@ overlay.addSkeleton(
// ); // );
// ///////////////////// /////////////////////
// // start section 1 // // start section 1 //
// ///////////////////// /////////////////////
// overlay.addSkeleton( overlay.addSkeleton(
// { {
// atlasPath: "assets/spineboy-pma.atlas", atlasPath: "assets/spineboy-pma.atlas",
// skeletonPath: "assets/spineboy-pro.skel", skeletonPath: "assets/spineboy-pro.skel",
// animation: 'walk', animation: 'walk',
// }, },
// document.querySelectorAll(`#section1-element`), document.querySelectorAll(`#section1-element`),
// ); );
// ///////////////////// /////////////////////
// // end section 1 // // end section 1 //
// ///////////////////// /////////////////////
@ -502,11 +502,10 @@ overlay.addSkeleton(
scale: .25, scale: .25,
}, },
{ {
element: document.getElementById(`section1-element-x`), element: document.getElementById(`section2-element`),
mode: 'origin', mode: 'origin',
xAxis: 0, xAxis: .5,
yAxis: 0, yAxis: 1,
debug: true,
}, },
); );
///////////////////// /////////////////////
@ -514,114 +513,114 @@ overlay.addSkeleton(
///////////////////// /////////////////////
// ///////////////////// /////////////////////
// // start section 3 // // start section 3 //
// ///////////////////// /////////////////////
// overlay.addSkeleton( overlay.addSkeleton(
// { {
// atlasPath: "assets/spineboy-pma.atlas", atlasPath: "assets/spineboy-pma.atlas",
// skeletonPath: "assets/spineboy-pro.skel", skeletonPath: "assets/spineboy-pro.skel",
// animation: 'jump', animation: 'jump',
// }, },
// { {
// element: document.getElementById(`section3-element`), element: document.getElementById(`section3-element`),
// mode: 'inside', // default mode: 'inside', // default
// offsetX: 100, offsetX: 100,
// offsetY: 50, offsetY: 50,
// }, },
// ); );
// ///////////////////// /////////////////////
// // end section 3 // // end section 3 //
// ///////////////////// /////////////////////
// ///////////////////// /////////////////////
// // start section 4 // // start section 4 //
// ///////////////////// /////////////////////
// const { skeleton, state } = await overlay.addSkeleton( const { skeleton, state } = await overlay.addSkeleton(
// { {
// atlasPath: "assets/raptor-pma.atlas", atlasPath: "assets/raptor-pma.atlas",
// skeletonPath: "assets/raptor-pro.skel", skeletonPath: "assets/raptor-pro.skel",
// animation: 'walk', animation: 'walk',
// }, },
// document.getElementById(`section4-element`) document.getElementById(`section4-element`)
// ); );
// let isRoaring = false; let isRoaring = false;
// setInterval(() => { setInterval(() => {
// const newAnimation = isRoaring ? "walk" : "roar"; const newAnimation = isRoaring ? "walk" : "roar";
// state.setAnimation(0, newAnimation, true); state.setAnimation(0, newAnimation, true);
// overlay.recalculateBounds(skeleton); overlay.recalculateBounds(skeleton);
// isRoaring = !isRoaring; isRoaring = !isRoaring;
// }, 4000); }, 4000);
// ///////////////////// /////////////////////
// // end section 4 // // end section 4 //
// ///////////////////// /////////////////////
// ///////////////////// /////////////////////
// // start section 5 // // start section 5 //
// ///////////////////// /////////////////////
// ///////////////////// /////////////////////
// // end section 5 // // end section 5 //
// ///////////////////// /////////////////////
// ///////////////////// /////////////////////
// // start section 6 // // start section 6 //
// ///////////////////// /////////////////////
// overlay.addSkeleton( overlay.addSkeleton(
// { {
// atlasPath: "assets/cloud-pot-pma.atlas", atlasPath: "assets/cloud-pot-pma.atlas",
// skeletonPath: "assets/cloud-pot.skel", skeletonPath: "assets/cloud-pot.skel",
// animation: 'playing-in-the-rain', animation: 'playing-in-the-rain',
// }, },
// document.getElementById(`section6-element`) document.getElementById(`section6-element`)
// ); );
// ///////////////////// /////////////////////
// // end section 6 // // end section 6 //
// ///////////////////// /////////////////////
// ///////////////////// /////////////////////
// // start section 7 // // start section 7 //
// ///////////////////// /////////////////////
// overlay.addSkeleton( overlay.addSkeleton(
// { {
// atlasPath: "assets/owl-pma.atlas", atlasPath: "assets/owl-pma.atlas",
// skeletonPath: "assets/owl-pro.skel", skeletonPath: "assets/owl-pro.skel",
// animation: 'idle', animation: 'idle',
// }, },
// { {
// element: document.getElementById(`section7-element`), element: document.getElementById(`section7-element`),
// debug: true, debug: true,
// } }
// ); );
// ////////////////////// //////////////////////
// // end section 7 // // end section 7 //
// ////////////////////// //////////////////////
// ///////////////////// /////////////////////
// // start section 8 // // start section 8 //
// ///////////////////// /////////////////////
// overlay.addSkeleton( overlay.addSkeleton(
// { {
// atlasPath: "assets/celestial-circus-pma.atlas", atlasPath: "assets/celestial-circus-pma.atlas",
// skeletonPath: "assets/celestial-circus-pro.skel", skeletonPath: "assets/celestial-circus-pro.skel",
// animation: 'wings-and-feet', animation: 'wings-and-feet',
// }, },
// { {
// element: document.getElementById(`section8-element`), element: document.getElementById(`section8-element`),
// draggable: true, draggable: true,
// debug: true, debug: true,
// } }
// ); );
// ////////////////////// //////////////////////
// // end section 8 // // end section 8 //
// ////////////////////// //////////////////////
})(); })();

View File

@ -80,27 +80,35 @@ export class SpineCanvasOverlay {
private resizeObserver:ResizeObserver; private resizeObserver:ResizeObserver;
private disposed = false; private disposed = false;
// how may pixels to add to the bottom (to avoid cut on edge during scrolling) // how many pixels to add to the edges as parcentages (to avoid cut on edge during scrolling)
private readonly additionalPixelsBottom = 300; private overflowTop = .1;
private overflowBottom = .2;
private overflowLeft = .1;
private overflowRight = .1;
private overflowLeftSize: number;
private overflowTopSize: number;
// how much the canvas is translated above (to avoid cut on edge during scrolling) private div: HTMLDivElement;
private readonly offsetHeight = 100;
// the actual base translation
private offsetHeightDraw: number;
/** Constructs a new spine canvas, rendering to the provided HTML canvas. */ /** Constructs a new spine canvas, rendering to the provided HTML canvas. */
constructor () { constructor () {
this.div = document.createElement('div');
this.div.style.position = "absolute";
this.div.style.top = "0";
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.canvas = document.createElement('canvas'); this.canvas = document.createElement('canvas');
document.body.appendChild(this.canvas); this.div.appendChild(this.canvas);
document.body.appendChild(this.div);
this.canvas.style.position = "absolute"; this.canvas.style.position = "absolute";
this.canvas.style.top = "0"; this.canvas.style.top = "0";
this.canvas.style.left = "0"; this.canvas.style.left = "0";
this.canvas.style.setProperty("pointer-events", "none"); this.canvas.style.setProperty("pointer-events", "none");
this.offsetHeightDraw = this.offsetHeight;
this.canvas.style.transform =`translate(0px,0px)`; this.canvas.style.transform =`translate(0px,0px)`;
// this.canvas.style.display = "inline"; // 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.canvas.style.setProperty("will-change", "transform"); // performance seems to be even worse with this uncommented
// resize and zoom // resize and zoom
@ -111,7 +119,11 @@ export class SpineCanvasOverlay {
this.spineCanvas.renderer.resize(ResizeMode.Expand); this.spineCanvas.renderer.resize(ResizeMode.Expand);
}); });
this.resizeObserver.observe(document.body); this.resizeObserver.observe(document.body);
this.updateCanvasSize(); this.updateCanvasSize();
this.overflowLeftSize = this.overflowLeft * document.documentElement.clientWidth;
this.overflowTopSize = this.overflowTop * document.documentElement.clientHeight;
this.zoomHandler(); this.zoomHandler();
// scroll // scroll
@ -297,7 +309,8 @@ export class SpineCanvasOverlay {
htmlOptionsList.forEach((list) => { htmlOptionsList.forEach((list) => {
const { element, mode, debug, offsetX, offsetY, xAxis, yAxis, dragX, dragY } = list; const { element, mode, debug, offsetX, offsetY, xAxis, yAxis, dragX, dragY } = list;
const divBounds = element.getBoundingClientRect(); const divBounds = element.getBoundingClientRect();
divBounds.y += this.offsetHeightDraw; divBounds.x += this.overflowLeftSize;
divBounds.y += this.overflowTopSize;
let x = 0, y = 0; let x = 0, y = 0;
if (mode === 'inside') { if (mode === 'inside') {
@ -318,8 +331,6 @@ export class SpineCanvasOverlay {
const boundsY = (ay + ah / 2) * ratio; const boundsY = (ay + ah / 2) * ratio;
// get the center of the div in world coordinate // 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; const divX = divBounds.x + divBounds.width / 2;
const divY = divBounds.y - 1 + divBounds.height / 2; const divY = divBounds.y - 1 + divBounds.height / 2;
this.screenToWorld(tempVector, divX, divY); this.screenToWorld(tempVector, divX, divY);
@ -332,19 +343,10 @@ export class SpineCanvasOverlay {
skeleton.scaleX = ratio; skeleton.scaleX = ratio;
skeleton.scaleY = ratio; skeleton.scaleY = ratio;
} else { } else {
// TODO: window.devicePixelRatio to manage browser zoom
// get the center of the div in world coordinate // get the center of the div in world coordinate
const divX = divBounds.x + divBounds.width * xAxis; const divX = divBounds.x + divBounds.width * xAxis;
const divY = divBounds.y + divBounds.height * yAxis; const divY = divBounds.y + divBounds.height * yAxis;
this.screenToWorld(tempVector, divX, divY); this.screenToWorld(tempVector, divX, divY);
// console.log(tempVector.x, tempVector.y)
// console.log(element.getBoundingClientRect().y, this.canvas.clientWidth)
// skeleton.scaleX /= window.devicePixelRatio;
// skeleton.scaleY /= window.devicePixelRatio;
// get vertices offset // get vertices offset
x = tempVector.x; x = tempVector.x;
@ -408,7 +410,7 @@ export class SpineCanvasOverlay {
this.input.addListener({ this.input.addListener({
down: (x, y, ev) => { down: (x, y, ev) => {
const originalEvent = ev instanceof MouseEvent ? ev : ev!.changedTouches[0]; const originalEvent = ev instanceof MouseEvent ? ev : ev!.changedTouches[0];
tempVectorInput.set(originalEvent.pageX - window.scrollX, originalEvent.pageY - window.scrollY + this.offsetHeightDraw, 0); tempVectorInput.set(originalEvent.pageX - window.scrollX + this.overflowLeftSize, originalEvent.pageY - window.scrollY + this.overflowTopSize, 0);
this.spineCanvas.renderer.camera.screenToWorld(tempVectorInput, this.canvas.clientWidth, this.canvas.clientHeight); this.spineCanvas.renderer.camera.screenToWorld(tempVectorInput, this.canvas.clientWidth, this.canvas.clientHeight);
this.skeletonList.forEach(({ htmlOptionsList, bounds, skeleton }) => { this.skeletonList.forEach(({ htmlOptionsList, bounds, skeleton }) => {
htmlOptionsList.forEach((element) => { htmlOptionsList.forEach((element) => {
@ -434,7 +436,7 @@ export class SpineCanvasOverlay {
}, },
dragged: (x, y, ev) => { dragged: (x, y, ev) => {
const originalEvent = ev instanceof MouseEvent ? ev : ev!.changedTouches[0]; const originalEvent = ev instanceof MouseEvent ? ev : ev!.changedTouches[0];
tempVectorInput.set(originalEvent.pageX - window.scrollX, originalEvent.pageY - window.scrollY + this.offsetHeightDraw, 0); tempVectorInput.set(originalEvent.pageX - window.scrollX + this.overflowLeftSize, originalEvent.pageY - window.scrollY + this.overflowTopSize, 0);
this.spineCanvas.renderer.camera.screenToWorld(tempVectorInput, this.canvas.clientWidth, this.canvas.clientHeight); this.spineCanvas.renderer.camera.screenToWorld(tempVectorInput, this.canvas.clientWidth, this.canvas.clientHeight);
let dragX = tempVectorInput.x - prevX; let dragX = tempVectorInput.x - prevX;
let dragY = tempVectorInput.y - prevY; let dragY = tempVectorInput.y - prevY;
@ -470,57 +472,46 @@ export class SpineCanvasOverlay {
*/ */
private updateCanvasSize() { private updateCanvasSize() {
const displayWidth = document.documentElement.clientWidth; // resize canvas
const displayHeight = document.documentElement.clientHeight; this.resizeCanvas();
// this.canvas.style.left = displayWidth * .1 + "px";
// this.canvas.style.width = displayWidth * .8 + "px"; // recalculate overflow left and size since canvas size changed
// this.canvas.style.left = displayWidth + "px"; // we could keep the initial values, avoid this and the translation below - even though we don't have a great gain
console.log(displayWidth) this.translateCanvas();
this.canvas.style.width = displayWidth + "px";
this.canvas.style.height = displayHeight + this.additionalPixelsBottom + "px"; // 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 dtermine the page size
this.div.remove();
const { width, height } = this.getPageSize();
document.body.appendChild(this.div);
this.div.style.width = width + "px";
this.div.style.height = height + "px";
} }
private scrollHandler = () => { private scrollHandler = () => {
const { width, height } = this.getPageSize(); this.translateCanvas();
}
const scrollPositionX = window.scrollX; private resizeCanvas() {
const canvasWidth = this.canvas.offsetWidth; const displayWidth = document.documentElement.clientWidth;
const maxTranslationX = width - canvasWidth; const displayHeight = document.documentElement.clientHeight;
// const translationX = Math.min(scrollPositionX, maxTranslationX); this.canvas.style.width = displayWidth * (1 + (this.overflowLeft + this.overflowRight)) + "px";
const translationX = scrollPositionX; this.canvas.style.height = displayHeight * (1 + (this.overflowTop + this.overflowBottom)) + "px";
console.log(width, canvasWidth, maxTranslationX, translationX) if (this.spineCanvas) this.spineCanvas.renderer.resize(ResizeMode.Expand);
// const translationX = Math.max(0, Math.min(scrollPositionX, maxTranslationX)); }
private translateCanvas() {
const displayWidth = document.documentElement.clientWidth;
const displayHeight = document.documentElement.clientHeight;
// when the phone zooms in, it happen that it is possible to scroll horizontally this.overflowLeftSize = this.overflowLeft * displayWidth;
// this means that scrollPositionX will have a value > 0 this.overflowTopSize = this.overflowTop * displayHeight;
// however, at the beginning the webpage is fit horizontally. It means that | maxTranslationX = width - canvasWidth | will be always 0
// it means that | translationX = Math.min(scrollPositionX, maxTranslationX) | will be always 0
// however, if I simply set translationX = scrollPositionX, the canvas is translated on the right increasing the page size
// the solution is probably to shrink the canvas on scroll
// CHECK THEN WHAT HAPPENS WHEN HORIZONTAL SCROLLBAR IS AVAILABLE ON BROWSER
const scrollPositionX = window.scrollX - this.overflowLeftSize;
const scrollPositionY = window.scrollY - this.overflowTopSize;
const scrollPositionY = window.scrollY; this.canvas.style.transform =`translate(${scrollPositionX}px,${scrollPositionY}px)`;
const canvasHeight = this.canvas.offsetHeight;
const maxTranslation = height - canvasHeight + 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)`;
// console.log(translationX, translationY)
// TODO: some browser plugins prevent transform translate - we can think to enable a mode that move by top/left
// this.canvas.style.top = `${this.currentTranslateY}px`;
// this.canvas.style.left = `${this.currentTranslateX}px`;
} }
private zoomHandler = () => { private zoomHandler = () => {
@ -539,23 +530,10 @@ export class SpineCanvasOverlay {
} }
private getPageSize() { private getPageSize() {
const width = Math.max( // we need the bounding client rect otherwise decimals won't be returned
document.body.scrollWidth, // this means that during zoom it might occurs that the div would be resized
document.documentElement.scrollWidth, // rounded 1px more making a scrollbar appear
document.body.offsetWidth, return document.body.getBoundingClientRect();
document.documentElement.offsetWidth,
document.documentElement.clientWidth
);
const height = Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight,
document.documentElement.clientHeight
);
return { width, height };
} }
/* /*