taye / interact.js

JavaScript drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+)

Home Page:http://interactjs.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

  1. Restriction modifiers won't let me drag or resize the element out of the <body> bounds
  2. 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

Screenshot 2022-11-18 at 16 42 14
Screenshot 2022-11-18 at 16 42 20

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 or body 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
}