Scroll the page on drag with jQuery

2019-01-13 13:52发布

问题:

I have tried using kinetic.js and the code below, however when I try this in IE11 it keeps jumping to the top every time I scroll:

$("html").kinetic();

I want to make the page scrollable on tablets and IE10 and 11 so that users can just push the page up to scroll down as you would on mobile devices.

How can I do this in pure-JS or jQuery without it jumping to the top?

回答1:

You can do this quite simply by recording the position of the mouse when clicked, and the current position when being dragged. Try this:

var clicked = false, clickY;
$(document).on({
    'mousemove': function(e) {
        clicked && updateScrollPos(e);
    },
    'mousedown': function(e) {
        clicked = true;
        clickY = e.pageY;
    },
    'mouseup': function() {
        clicked = false;
        $('html').css('cursor', 'auto');
    }
});

var updateScrollPos = function(e) {
    $('html').css('cursor', 'row-resize');
    $(window).scrollTop($(window).scrollTop() + (clickY - e.pageY));
}

To prevent text selection while dragging, add the following CSS:

body {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

Example fiddle


Update

Here's an version of the above as a jQuery plugin, extended to allow both vertical and horizontal scrolling via the settings. It also allows you to change the cursor that's used too.

(function($) {
  $.dragScroll = function(options) {
    var settings = $.extend({
      scrollVertical: true,
      scrollHorizontal: true,
      cursor: null
    }, options);

    var clicked = false,
      clickY, clickX;

    var getCursor = function() {
      if (settings.cursor) return settings.cursor;
      if (settings.scrollVertical && settings.scrollHorizontal) return 'move';
      if (settings.scrollVertical) return 'row-resize';
      if (settings.scrollHorizontal) return 'col-resize';
    }

    var updateScrollPos = function(e, el) {
      $('html').css('cursor', getCursor());
      var $el = $(el);
      settings.scrollVertical && $el.scrollTop($el.scrollTop() + (clickY - e.pageY));
      settings.scrollHorizontal && $el.scrollLeft($el.scrollLeft() + (clickX - e.pageX));
    }

    $(document).on({
      'mousemove': function(e) {
        clicked && updateScrollPos(e, this);
      },
      'mousedown': function(e) {
        clicked = true;
        clickY = e.pageY;
        clickX = e.pageX;
      },
      'mouseup': function() {
        clicked = false;
        $('html').css('cursor', 'auto');
      }
    });
  }
}(jQuery))

$.dragScroll();
/* Note: CSS is not relevant to the solution. 
   This is only needed for this demonstration */

body,
html {
  padding: 0;
  margin: 0;
}

div {
  height: 1000px;
  width: 2000px;
  border-bottom: 3px dashed #EEE;
  /* gradient is only to make the scroll movement more obvious */
  background: rgba(201, 2, 2, 1);
  background: -moz-linear-gradient(-125deg, rgba(201, 2, 2, 1) 0%, rgba(204, 0, 204, 1) 16%, rgba(94, 0, 201, 1) 31%, rgba(0, 153, 199, 1) 43%, rgba(0, 199, 119, 1) 56%, rgba(136, 199, 0, 1) 69%, rgba(199, 133, 0, 1) 83%, rgba(107, 0, 0, 1) 100%);
  background: -webkit-gradient(left top, right bottom, color-stop(0%, rgba(201, 2, 2, 1)), color-stop(16%, rgba(204, 0, 204, 1)), color-stop(31%, rgba(94, 0, 201, 1)), color-stop(43%, rgba(0, 153, 199, 1)), color-stop(56%, rgba(0, 199, 119, 1)), color-stop(69%, rgba(136, 199, 0, 1)), color-stop(83%, rgba(199, 133, 0, 1)), color-stop(100%, rgba(107, 0, 0, 1)));
  background: -webkit-linear-gradient(-125deg, rgba(201, 2, 2, 1) 0%, rgba(204, 0, 204, 1) 16%, rgba(94, 0, 201, 1) 31%, rgba(0, 153, 199, 1) 43%, rgba(0, 199, 119, 1) 56%, rgba(136, 199, 0, 1) 69%, rgba(199, 133, 0, 1) 83%, rgba(107, 0, 0, 1) 100%);
  background: -o-linear-gradient(-125deg, rgba(201, 2, 2, 1) 0%, rgba(204, 0, 204, 1) 16%, rgba(94, 0, 201, 1) 31%, rgba(0, 153, 199, 1) 43%, rgba(0, 199, 119, 1) 56%, rgba(136, 199, 0, 1) 69%, rgba(199, 133, 0, 1) 83%, rgba(107, 0, 0, 1) 100%);
  background: -ms-linear-gradient(-125deg, rgba(201, 2, 2, 1) 0%, rgba(204, 0, 204, 1) 16%, rgba(94, 0, 201, 1) 31%, rgba(0, 153, 199, 1) 43%, rgba(0, 199, 119, 1) 56%, rgba(136, 199, 0, 1) 69%, rgba(199, 133, 0, 1) 83%, rgba(107, 0, 0, 1) 100%);
  background: linear-gradient(-110deg, rgba(201, 2, 2, 1) 0%, rgba(204, 0, 204, 1) 16%, rgba(94, 0, 201, 1) 31%, rgba(0, 153, 199, 1) 43%, rgba(0, 199, 119, 1) 56%, rgba(136, 199, 0, 1) 69%, rgba(199, 133, 0, 1) 83%, rgba(107, 0, 0, 1) 100%);
  filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#c90202', endColorstr='#6b0000', GradientType=1);
  color: #EEE;
  padding: 20px;
  font-size: 2em;
}

body {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>First...</div>

<div>Second...</div>



回答2:

I just like to add. Using Rory's code I made horizontal scrolling.

var clicked = false, base = 0;

$('#someDiv').on({
    mousemove: function(e) {
        clicked && function(xAxis) {
            var _this = $(this);
            if(base > xAxis) {
                base = xAxis;
                _this.css('margin-left', '-=1px');
            }
            if(base < xAxis) {
                base = xAxis;
                _this.css('margin-left', '+=1px');
            }
        }.call($(this), e.pageX);
    },
    mousedown: function(e) {
        clicked = true;
        base = e.pageX;
    },
    mouseup: function(e) {
        clicked = false;
        base = 0;
    }
});


回答3:

This code will work on horizontal and vertical mouse drag scroll. It's pretty simple.

var curYPos = 0,
    curXPos = 0,
    curDown = false;

window.addEventListener('mousemove', function(e){ 
  if(curDown === true){
    window.scrollTo(document.body.scrollLeft + (curXPos - e.pageX), document.body.scrollTop + (curYPos - e.pageY));
  }
});

window.addEventListener('mousedown', function(e){ curDown = true; curYPos = e.pageY; curXPos = e.pageX; });
window.addEventListener('mouseup', function(e){ curDown = false; }); 


回答4:

Base on Rory McCrossan's idea, implemented with AngularJS2.

import {Directive, ElementRef, OnDestroy, Input} from "@angular/core";

declare var jQuery: any;

@Directive({
    selector: '[appDragScroll]'
})
export class DragScrollDirective implements OnDestroy {

    @Input() scrollVertical: boolean = true;
    @Input() scrollHorizontal: boolean = true;

    private dragging = false;
    private originalMousePositionX: number;
    private originalMousePositionY: number;
    private originalScrollLeft: number;
    private originalScrollTop: number;

    constructor(private nodeRef: ElementRef) {
        let self = this;

        jQuery(document).on({
            "mousemove": function (e) {
                self.dragging && self.updateScrollPos(e);
            },
            "mousedown": function (e) {
                self.originalMousePositionX = e.pageX;
                self.originalMousePositionY = e.pageY;
                self.originalScrollLeft = jQuery(self.nodeRef.nativeElement).scrollLeft();
                self.originalScrollTop = jQuery(self.nodeRef.nativeElement).scrollTop();
                self.dragging = true;
            },
            "mouseup": function (e) {
                jQuery('html').css('cursor', 'auto');
                self.dragging = false;
            }
        });

    }

    ngOnDestroy(): void {
        jQuery(document).off("mousemove");
        jQuery(document).off("mousedown");
        jQuery(document).off("mouseup");
    }

    private updateScrollPos(e) {
        jQuery('html').css('cursor', this.getCursor());

        let $el = jQuery(this.nodeRef.nativeElement);
        if (this.scrollHorizontal) {
            $el.scrollLeft(this.originalScrollLeft + (this.originalMousePositionX - e.pageX));
        }
        if (this.scrollVertical) {
            $el.scrollTop(this.originalScrollTop + (this.originalMousePositionY - e.pageY));
        }
    }

    private getCursor() {
        if (this.scrollVertical && this.scrollHorizontal) return 'move';
        if (this.scrollVertical) return 'row-resize';
        if (this.scrollHorizontal) return 'col-resize';
    }

}


回答5:

Based on the first answer, this is the code for horizontal scroll on mouse drag:

var clicked = false, clickX;
$(document).on({
    'mousemove': function(e) {
        clicked && updateScrollPos(e);
    },
    'mousedown': function(e) {
        e.preventDefault();        
        clicked = true;
        clickX = e.pageX;
    },
    'mouseup': function() {
        clicked = false;
        $('html').css('cursor', 'auto');
    }
});

var updateScrollPos = function(e) {
    $('html').css('cursor', 'grabbing');
    $(window).scrollLeft($(window).scrollLeft() + (clickX - e.pageX));
}