Skip to content

Instantly share code, notes, and snippets.

@kerrishotts
Created June 8, 2012 22:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kerrishotts/2898456 to your computer and use it in GitHub Desktop.
Save kerrishotts/2898456 to your computer and use it in GitHub Desktop.
This library is intended to be similar to iScroll-lite in that it should be a relatively fast method of scrolling content without being horribly laggy or cause incorrect "clicks" to be registered.
/******************************************************************************
*
* SCROLLER
* Author: Kerri Shotts
* Version: 0.1 alpha
* License: MIT
*
* This library is intended to be similar to iScroll-lite in that it should be
* a relatively fast method of scrolling content without being horribly laggy
* or cause incorrect "clicks" to be registered.
*
* This library does *NOT* support physics-based scrolling, except for a small
* inertia animation at the end of a scroll. It is not intended to replicate
* native scrolling /at all/. There are no bounces at the top or bottom. There
* is no visible scroll bar either. Essentially, overflow:scroll as supported
* on iOS 5 with no bounce/inertia scrolling.
*
* Consider this library an experiment. The idea is to be simpler than iScroll
* to use -- for example, the scroller only needs to be created once -- it does
* not need to be refreshed when AJAX content loads. It is intended to be at
* least as fast as iScroll, if not a little faster. It is not, however,
* intended to be a native scrolling solution. At this time, I do not believe
* it truly possible or practice, and users will notice any non-native solution
* that tries to match, so why try?
*
* Usage:
*
* var yourScroller = new SCROLLER.GenericScroller ( "the_element_to_scroll" );
*
* where you have the following DOM tree:
*
* container_element
* - the_element_to_scroll
*
* Future Goals:
*
* - Detect native physics scrolling and use it when possible
* - Detect native overflow:scroll (non-physics) and use it when possible
* - Improve the inertial scrolling at end (this is a very rough implementation)
* - Become irrelevant. I hope for a day when all mobile browsers can scroll
* complex content natively and smoothly.
*
* Supported Platforms:
*
* - Android 2.3+
* - iOS 4.3+
* - probably any webkit browser?
*
* Known Issues:
* - A little too willing to call a scroll a "click".
*
******************************************************************************/
var SCROLLER = SCROLLER || {}; // create the namespace
SCROLLER.GenericScroller = function ( element )
{
var self = this;
// the element that should scroll
self.theElement = {};
// various touch-related coords.
self._touchX = -1;
self._touchY = -1;
self._touchStartX = 0;
self._touchStartY = 0;
self._actualX = 0;
self._actualY = 0;
// delta changes; totalDelta should be total change from start to end.
self._deltaX = 0;
self._deltaY = 0;
self._totalDeltaX = 0;
self._totalDeltaY = 0;
// inertial animation
self._timer = -1;
self._step = 0;
/**
*
* Attaches the scroller to a new element. The element MUST be contained within
* another element that can support the setting of scrollTop/Left.
*
* Any overflow values will be overridden. Any webkit transforms may also be
* overridden.
*/
self.attachToElement = function ( element)
{
// get our element
self.theElement = document.getElementById(element);
// attach our listeners
self.theElement.addEventListener ( "touchstart", self.touchStart, false );
self.theElement.addEventListener ( "touchmove", self.touchMove, false );
self.theElement.addEventListener ( "touchend", self.touchEnd, false );
// turn overflow to hidden; if it is auto or scroll, the native scrolling
// might kick in (if supported) and confuse us. IF YOU WANT NATIVE SCROLLING,
// DON'T USE THIS SCROLLER.
self.theElement.parentNode.style.overflow = "hidden";
// A quandary. on iOS, it is faster to have translate3d(0,0,0) enabled,
// but you have more visual glitches. Without it, it is a little slower,
// but with no visual glitches. For Android, we leave it on, just in case
// it can be used.
if (device)
{
if (device.platform == "Android")
{
self.theElement.style.webkitTransform = "translate3d(0,0,0)";
}
}
else
{
// if we can't detect the device, we'll always use it.
self.theElement.style.webkitTransform = "translate3d(0,0,0)";
}
console.log ("Scroller initiated for element " + element);
}
/**
*
* Get the scroll position
*
*/
self.getScrollTop = function ()
{
return self.theElement.parentNode.scrollTop;
}
self.getScrollLeft = function ()
{
return self.theElement.parentNode.scrollLeft;
}
/**
*
* Scroll to a given location. If the location can't be scrolled to,
* the nearest location will be used.
*
*/
self.scrollTo = function (left, top)
{
self.theElement.parentNode.scrollTop = top;
self.theElement.parentNode.scrollLeft = left;
self._actualX = -left;
self._actualY = -top;
}
/**
*
* touchStart initializes all our values when a touch is received.
*
*/
self.touchStart = function (event)
{
// if an inertia animation is underway, clear it.
if (self._timer!=-1) { clearInterval (self._timer); }
self._timer = -1;
// record our touches.
self._touchX = event.touches[0].screenX;
self._touchY = event.touches[0].screenY;
self._touchStartX = self._touchX;
self._touchStartY = self._touchY;
// zero the deltas
self._deltaX = 0;
self._deltaY = 0;
self._totalDeltaX = 0;
self._totalDeltaY = 0;
// get our actual scroll position
self._actualX = -self.theElement.parentNode.scrollLeft;
self._actualY = -self.theElement.parentNode.scrollTop;
}
/**
*
* When a touch moves, we'll receive the event here.
*
*/
self.touchMove = function (event)
{
// calculate the delta between our last and current
// position
self._deltaX = self._touchX - event.touches[0].screenX;
self._deltaY = self._touchY - event.touches[0].screenY;
// update totalDelta
self._totalDeltaX -= self._deltaX;
self._totalDeltaY -= self._deltaY;
// update our actual scroll position
self._actualX -= self._deltaX;
self._actualY -= self._deltaY;
// store our current screen position
self._touchX = event.touches[0].screenX;
self._touchY = event.touches[0].screenY;
// scroll to the new position
self.theElement.parentNode.scrollTop = -self._actualY;
self.theElement.parentNode.scrollLeft = -self._actualX;
// if there is any movement, prevent the default.
if ( Math.sqrt((self._totalDeltaX * self._totalDeltaX) + (self._totalDeltaY * self._totalDeltaY)) > 0 )
{
event.preventDefault ();
}
}
/**
*
* When a finger is lifted from the screen, we'll get this event.
* We can determine whether or not it was a click if we scrolled at all,
* and if we scrolled a certain distance, we'll do a little inertial
* scrolling
*/
self.touchEnd = function (event)
{
// were we just a click? if so, this'll be zero -- otherwise prevent the default.
if ( Math.sqrt((self._totalDeltaX * self._totalDeltaX) + (self._totalDeltaY * self._totalDeltaY)) > 0 )
{
event.preventDefault ();
}
// how far did we scroll just prior to getting the end event? Is it far enough to have
// some inertia?
if ( Math.sqrt((self._deltaX * self._deltaX) + (self._deltaY * self._deltaY)) > 10 )
{
// yes, set up our animation
self._step = 0;
self._timer = setInterval ( function ()
{
// increment the frame
self._step = self._step + 1;
if (self._step<10)
{
// we'll permit 10 frames of animation
self._actualX -= self._deltaX/(self._step);
self._actualY -= self._deltaY/(self._step);
self.theElement.parentNode.scrollTop = -self._actualY;
self.theElement.parentNode.scrollLeft = -self._actualX;
}
else
{
// animation complete
clearInterval (self._timer);
self._timer = -1;
}
}, 10 ); // 10 = as fast as possible
}
}
// attach to the element passed in the constructor.
self.attachToElement ( element );
}
@kerrishotts
Copy link
Author

If you have tried this on a platform, I'd love to hear your thoughts as to how it compares to the other scrolling solutions. Keep in mind, it isn't intended to be as nice as native scrolling, but is intended to compete favorably with the likes of iScroll-lite and such while also addressing some of my issues with the other libraries that have been offered.

@kerrishotts
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment