Cannot restrict to viewport
andreasvirkus opened this issue · comments
Description
I've got an issue with restricting my element to the viewport with both drag and resize. The issue is apparent on pages where the html and body element are shorter than the actual visible content. One such example is https://subtlepatterns.com/, but there are loads others. As I'm using interact in a browser extension, it needs to handle different page structures and styling.
When the user has scrolled past the end of the body, then
- Restriction modifiers won't let me drag or resize the element out of the
<body>
bounds - The reflow() method will make it jump out of the viewport, to where the body element ended.
Here's a small replication of the issue:
https://jsfiddle.net/andreasvirkus/ovqga8w3/1/
And a recording (ideally it should touch the bottom of the screen when dragging down):
Screen.Recording.2022-11-18.at.16.36.44.mov
Here you can see how <body>
is cut off, but the content itself stretches further down
Expected behavior
Ideally, I'd want to always restrict the drag and resize to the currently visible window (viewport). No matter if the user has scrolled on the x or y axis. Similarly with reflow, I'd always want the element to reflow to a visible position, not to be offset somewhere off the screen.
Actual behavior
- With drag/resize, i'm currently bound by the
html
orbody
element size. - With reflow, the element is offset and either partially or completely hidden.
System configuration
interact.js version: 1.10.7
Browser name and version:
Brave: 1.43.89 Chromium: 105.0.5195.102 (Official Build) (x86_64)
Chrome: 107.0.5304
Operating System: tested on Mac and Windows
I've also tried restricting with restrictRect
and while that let's me use the full range of the viewport, now users could drag the element off screen (if document.body.scrollHeight
is larger than window.innerHeight
)
function getRestriction() {
const body = document.body
const box = body.getBoundingClientRect()
const bottom = Math.max(box.height, body.scrollHeight)
const right = Math.max(box.width, body.scrollWidth)
return {
top: 0,
left: 0,
bottom: bottom - 20,
right,
}
}
...
modifiers: [
interact.modifiers.restrictRect({
restriction: getRestriction(),
})
]
Here's a recording of the buggy reflow behavior, if we've first scrolled beyond the restriction value:
Screen.Recording.2022-11-18.at.16.50.53.mov
This is our current workaround (it's called on first init, in a resize listener and in the drag/resize end() hooks)
export function repositionWidget() {
if (!widget) return
const box = widget.getBoundingClientRect()
if (!box) return
const height = window.innerHeight
const width = Math.min(window.innerWidth, document.body.clientWidth)
if (box.top < 0) widget.style.top = '0'
if (box.left < 0) widget.style.left = '0'
if (box.right > width) widget.style.left = Math.max(0, width - box.width) + 'px'
if (box.bottom > height) widget.style.top = Math.max(0, height - box.height) + 'px'
const newPos = widget.getBoundingClientRect()
dragTop = newPos.top
dragLeft = newPos.left
}