ng2-dragula icon indicating copy to clipboard operation
ng2-dragula copied to clipboard

If container is larger than viewport, and drag an item passed viewport top/bottom, window should scroll automatically?

Open quiringk opened this issue 7 years ago • 15 comments

When grabbing a scrollable item and going past the bottom of the viewport or past the top of the viewport, the browser window doesn't scroll. (the dragula container is larger than the browser viewport.) Is there a way to do this? It's super annoying to drag, drop, scroll, drag, drop, scroll, drag, drop, scroll, etc. (because my draggable items are large bootstrap panels.)

I tried dom-autoscroller which was recommend in this dragula issue (#121) but could only get it to work for a draggable items that are in a container with a height + overflow scrollbar.

I want the same functionality but within a container that is larger than the viewport. (So that the window automatically starts scrolling up or down & no extra scroll bar, just the windows scrollbar.) This seems like something that should just work out of the box?

I also tried @Naztar's answer in (#402) with no luck. Maybe I didn't implement his solution correctly, but all I did is copy and paste his javascript changes in dragula.js and still didn't work. (First I tried copying the raw .js file he modified into dragula.js but started getting 'require is not defined' errors, so I tried just pulling the individual changes he made and pasting them in, still no luck)

quiringk avatar Feb 28 '17 22:02 quiringk

@quiringk Did you find any fixes for this issue?

vikram-raj avatar Mar 07 '17 09:03 vikram-raj

The problem seems to be solved on the JS librairie https://github.com/bevacqua/dragula/tree/22f4e03d1303616fc58fb096698f760fefac8fc3

You maybe can include this 3rd party librairie to your project.

PierreDugue avatar Mar 10 '17 16:03 PierreDugue

Hi @PierreDugue,

Is there any option, which I need to pass to enable scrolling while dragging. Because I check it in demo page and it's not working.

vikram-raj avatar Mar 11 '17 09:03 vikram-raj

Right, it seems that this branch is not merged yet, so you will not be able to see it on demo page.

PierreDugue avatar Mar 13 '17 12:03 PierreDugue

I'd really like to get this merged. And is there a way to increase the scroll speed to something like 200? 20 is way too slow. Thanks!!

doctorhilarius avatar May 18 '17 18:05 doctorhilarius

Need this.

Ontic-Entity avatar May 19 '17 21:05 Ontic-Entity

I built a demo that does this, among other things:

https://github.com/cormacrelf/dragula-touch-demo

cormacrelf avatar May 20 '17 06:05 cormacrelf

Also curious to know if this will be incorporated in ng2-dragula, or if we should roll our own.

Cheers!

UPDATE: @cormacrelf's dragula-touch-demo above seems to be easy to adapt to our use.

mmanneva avatar Jul 06 '17 04:07 mmanneva

https://plnkr.co/edit/IWywxumqZHJLw8bZEEWl?p=preview https://plnkr.co/edit/mDNw7pDDp5oHFeHZApB5?p=preview

This may be useful for you guys.

vikram-raj avatar Jul 24 '17 12:07 vikram-raj

This is what I came up with and thought I should share.

dragulaService.drag.subscribe(value => {
    document.onmousemove = (e) => {
        let event = e || window.event;
        let mouseY = event['pageY'];
        let scrollTop = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop; // document.documentElement.scrollTop is undefined on the Edge browser
        let scrollBottom = scrollTop + window.innerHeight;
        let elementHeight = value[1].offsetHeight; // this is to get the height of the dragged element

        if (mouseY - elementHeight / 2 < scrollTop) {
            window.scrollBy(0, -15);
        } else if (mouseY + elementHeight > scrollBottom) {
            window.scrollBy(0, 15);
        }
    };
});

// detach the mouse move event when the drag ends
dragulaService.dragend.subscribe(value => {
    document.onmousemove = null;
});

ht89 avatar Jan 24 '18 00:01 ht89

@ht89 I assume it works great for elements in body tag. I cannot seem to get it working for a parent container which has a certain max-width and overflow: auto setup for it. Any clues?

hassanasad avatar Mar 15 '18 12:03 hassanasad

@ht89 @hassanasad this is what I used to make it work inside a parent container:

const container = document.querySelector('myParentContainer');

this.dragulaService.drag.subscribe(value => {
    document.onmousemove = e => {
        const event = e || window.event;
        const mouseY = event['pageY'] - container.offsetTop;
        const scrollTop = container.scrollTop;
        const scrollBottom = container.offsetHeight - scrollTop;
        const elementHeight = value[1].getBoundingClientRect().height;

        if (mouseY - elementHeight / 2 < scrollTop) {
            container.scrollBy(0, -15);
        } else if (mouseY + elementHeight > scrollBottom) {
            container.scrollBy(0, 15);
        }
    };
});

// detach the mouse move event when the drag ends
this.dragulaService.dragend.subscribe(value => {
    document.onmousemove = null;
});

ndraiman avatar May 30 '18 09:05 ndraiman

Hi @Nexxado , thanks for sharing! I was too busy and forgot @hassanasad question.

ht89 avatar May 30 '18 09:05 ht89

if anyone getting error for container.offsetTop as [ts] Property 'offsetTop' does not exist on type 'Element'. from typescript , make following change to @Nexxado answer -

const container = <HTMLElement>document.querySelector('myParentContainer');

arj060892 avatar Jun 12 '18 19:06 arj060892

I was looking for a solution that keeps on scrolling up/down slowly when a dragged element is moved to the top/bottom of the page and came up with this:

const DRAG_SCROLL_INTERVAL = 100;
const DRAG_SCROLL_DISTANCE = 50;
// ...

  ngOnInit() {
   // ...

    this.dragulaService
      .drag(this.DRAG_GROUP_ID)
      .pipe(takeUntil(this.destroy$))
      .subscribe(({ name, el, source }) => {
        // add an event listener
        document.onmousemove = e => {
          let event = e || this.window.event;
          let mouseY = event['pageY'];
          let scrollTop = this.document.documentElement?.scrollTop || this.document.body.scrollTop;
          let scrollBottom = scrollTop + this.window.innerHeight;
          let elementHeight = el.clientHeight;

          // and check if the event is on top or bottom of the page
          if (mouseY - elementHeight / 2 < scrollTop) {
            this.startScrollingUp();
          } else if (mouseY + elementHeight / 2 > scrollBottom) {
            this.startScrollingDown();
          } else {
            this.stopScrolling();
          }
        };
      });

    // detach the mouse move event when the drag ends
    this.dragulaService.dragend(this.DRAG_GROUP_ID).subscribe(_ => {
      document.onmousemove = null;
      this.stopScrolling();
    });
  }

  private startScrollingUp() {
    if (this.scrollInterval) return; // don't set it multiple times
    this.scrollInterval = this.window.setInterval(() => window.scrollBy({ top: -DRAG_SCROLL_DISTANCE }), DRAG_SCROLL_INTERVAL);
  }

  private startScrollingDown() {
    if (this.scrollInterval) return;
    this.scrollInterval = this.window.setInterval(() => window.scrollBy({ top: DRAG_SCROLL_DISTANCE }), DRAG_SCROLL_INTERVAL);
  }

  private stopScrolling() {
    this.window.clearInterval(this.scrollInterval);
    this.scrollInterval = null;
  }

Thanks to @ndraiman and the others! 👍

PS: I think you missed / 2 in the else branch.

floisloading avatar Mar 29 '22 06:03 floisloading