如此处找到 - https://github.com/neave/touch-scroll:
(function($) {
// Define default scroll settings
var defaults = {
y: 0,
scrollHeight: 0,
elastic: !navigator.userAgent.match(/android/i),
momentum: true,
elasticDamp: 0.6,
elasticTime: 50,
reboundTime: 400,
momentumDamp: 0.9,
momentumTime: 300,
iPadMomentumDamp: 0.95,
iPadMomentumTime: 1200,
touchTags: ['select', 'input', 'textarea']
};
// Define methods
var methods = {
init: function(options) {
return this.each(function() {
var o = $.extend(defaults, options);
// Prevent double-init, just update instead
if (!!this._init) {
return this.update();
}
this._init = true;
// Define element variables
var $this = $(this),
scrollY = -o.y,
touchY = 0,
movedY = 0,
pollY = 0,
height = 0,
maxHeight = 0,
scrollHeight = 0,
scrolling = false,
bouncing = false,
moved = false,
timeoutID,
isiPad = !!navigator.platform.match(/ipad/i),
hasMatrix = 'WebKitCSSMatrix' in window,
has3d = hasMatrix && 'm11' in new WebKitCSSMatrix();
// Keep bottom of scroll area at the bottom on resize
var update = this.update = function() {
height = $this.height();
if (o.scrollHeight) {
scrollHeight = o.scrollHeight;
} else if ($this.prop) {
scrollHeight = $this.prop('scrollHeight'); // jQuery 1.6 uses .prop(), older versions use .attr()
} else {
scrollHeight = $this.attr('scrollHeight');
}
if (scrollHeight < height) {
scrollHeight = height;
}
maxHeight = height - scrollHeight;
clearTimeout(timeoutID);
clampScroll(false);
};
// Set up initial variables
update();
// Set up transform CSS
$this.css({'-webkit-transition-property': '-webkit-transform',
'-webkit-transition-timing-function': 'cubic-bezier(0,0,0.2,1)',
'-webkit-transition-duration': '0',
'-webkit-transform': cssTranslate(scrollY)});
// Listen for screen size change event
window.addEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', update, false);
// Listen for touch events
$this.bind('touchstart.touchScroll', touchStart);
$this.bind('touchmove.touchScroll', touchMove);
$this.bind('touchend.touchScroll touchcancel.touchScroll', touchEnd);
$this.bind('webkitTransitionEnd.touchScroll', transitionEnd);
// Set the position of the scroll area using transform CSS
var setPosition = this.setPosition = function(y) {
scrollY = y;
$this.css('-webkit-transform', cssTranslate(scrollY));
};
// Transform using a 3D translate if available
function cssTranslate(y) {
return 'translate' + (has3d ? '3d(0,' : '(0,') + y + 'px' + (has3d ? ',0)' : ')');
}
// Set CSS transition time
function setTransitionTime(time) {
time = time || '0';
$this.css('-webkit-transition-duration', time + 'ms');
}
// Get the actual pixel position made by transform CSS
function getPosition() {
if (hasMatrix) {
var transform = window.getComputedStyle($this[0]).webkitTransform;
if (!!transform && transform !== 'none') {
var matrix = new WebKitCSSMatrix(transform);
return matrix.f;
}
}
return scrollY;
}
// Expose getPosition API
this.getPosition = function() {
return getPosition();
};
// Bounce back to the bounds after momentum scrolling
function reboundScroll() {
if (scrollY > 0) {
scrollTo(0, o.reboundTime);
} else if (scrollY < maxHeight) {
scrollTo(maxHeight, o.reboundTime);
}
}
// Stop everything once the CSS transition in complete
function transitionEnd() {
if (bouncing) {
bouncing = false;
reboundScroll();
}
clearTimeout(timeoutID);
}
// Limit the scrolling to within the bounds
function clampScroll(poll) {
if (!hasMatrix || bouncing) {
return;
}
var oldY = pollY;
pollY = getPosition();
if (pollY > 0) {
if (o.elastic) {
// Slow down outside top bound
bouncing = true;
scrollY = 0;
momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
} else {
// Stop outside top bound
setTransitionTime(0);
setPosition(0);
}
} else if (pollY < maxHeight) {
if (o.elastic) {
// Slow down outside bottom bound
bouncing = true;
scrollY = maxHeight;
momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
} else {
// Stop outside bottom bound
setTransitionTime(0);
setPosition(maxHeight);
}
} else if (poll) {
// Poll the computed position to check if element is out of bounds
timeoutID = setTimeout(clampScroll, 20, true);
}
}
// Animate to a position using CSS
function scrollTo(destY, time) {
if (destY === scrollY) {
return;
}
moved = true;
setTransitionTime(time);
setPosition(destY);
}
// Perform a momentum-based scroll using CSS
function momentumScroll(d, k, minDist, maxDist, t) {
var ad = Math.abs(d),
dy = 0;
// Calculate the total distance
while (ad > 0.1) {
ad *= k;
dy += ad;
}
// Limit to within min and max distances
if (dy > maxDist) {
dy = maxDist;
}
if (dy > minDist) {
if (d < 0) {
dy = -dy;
}
dy += scrollY;
// If outside the bounds, don't go too far
if (height > 0) {
if (dy > height * 2) {
var ody = dy;
dy = height * 2;
} else if (dy < maxHeight - height * 2) {
dy = maxHeight - height * 2;
}
}
// Perform scroll
scrollTo(Math.round(dy), t);
}
clampScroll(true);
}
// Get the touch points from this event
function getTouches(e) {
if (e.originalEvent) {
if (e.originalEvent.touches && e.originalEvent.touches.length) {
return e.originalEvent.touches;
} else if (e.originalEvent.changedTouches && e.originalEvent.changedTouches.length) {
return e.originalEvent.changedTouches;
}
}
return e.touches;
}
// Dispatches a fake mouse event from a touch event
function dispatchMouseEvent(name, touch, target) {
var e = document.createEvent('MouseEvent');
e.initMouseEvent(name, true, true, touch.view, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
target.dispatchEvent(e);
}
// Find the root node of this target
function getRootNode(target) {
while (target.nodeType !== 1) {
target = target.parentNode;
}
return target;
}
// Perform a touch start event
function touchStart(e) {
// Allow certain HTML tags to receive touch events
if ($.inArray(e.target.tagName.toLowerCase(), o.touchTags) !== -1) {
return;
}
// Stop the default touches
e.preventDefault();
e.stopPropagation();
var touch = getTouches(e)[0];
// Dispatch a fake mouse down event
dispatchMouseEvent('mousedown', touch, getRootNode(touch.target));
scrolling = true;
moved = false;
movedY = 0;
clearTimeout(timeoutID);
setTransitionTime(0);
// Check scroll position
if (o.momentum) {
var y = getPosition();
if (y !== scrollY) {
setPosition(y);
moved = true;
}
}
touchY = touch.pageY - scrollY;
}
// Perform a touch move event
function touchMove(e) {
if (!scrolling) {
return;
}
var dy = getTouches(e)[0].pageY - touchY;
// Elastic-drag or stop when moving outside of boundaries
if (dy > 0) {
if (o.elastic) {
dy /= 2;
} else {
dy = 0;
}
} else if (dy < maxHeight) {
if (o.elastic) {
dy = (dy + maxHeight) / 2;
} else {
dy = maxHeight;
}
}
movedY = dy - scrollY;
moved = true;
setPosition(dy);
}
// Perform a touch end event
function touchEnd(e) {
if (!scrolling) {
return;
}
scrolling = false;
if (moved) {
// Ease back to within boundaries
if (scrollY > 0 || scrollY < maxHeight) {
reboundScroll();
} else if (o.momentum) {
// Free scroll with momentum
momentumScroll(movedY, isiPad ? o.iPadMomentumDamp : o.momentumDamp, 40, 2000, isiPad ? o.iPadMomentumTime : o.momentumTime);
}
} else {
var touch = getTouches(e)[0],
target = getRootNode(touch.target);
// Dispatch fake mouse up and click events if this touch event did not move
dispatchMouseEvent('mouseup', touch, target);
dispatchMouseEvent('click', touch, target);
}
}
});
},
update: function() {
return this.each(function() {
this.update();
});
},
getPosition: function() {
var a = [];
this.each(function() {
a.push(-this.getPosition());
});
return a;
},
setPosition: function(y) {
return this.each(function() {
this.setPosition(-y);
});
}
};
// Public method for touchScroll
$.fn.touchScroll = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.touchScroll');
}
};
})(jQuery);
如何使其在具有不同选择器和每个这样的独特选项的多个实例上工作?:
$('#selector1').touchScroll({elastic: false});
$('#selector2').touchScroll({elastic: true});
当前设置的选项会覆盖另一个。