// smoothscroll v0.9.9 // licensed under the terms of the mit license. // people involved // - balazs galambosi: maintainer (changelog.txt) // - patrick brunner (patrickb1991@gmail.com) // - michael herf: ssc_pulse algorithm // scroll variables (tweakable) var ssc_framerate = 150; // [hz] var ssc_animtime = 1800; // [px] var ssc_stepsize = 85; // [px] // ssc_pulse (less tweakable) // ratio of "tail" to "acceleration" var ssc_pulsealgorithm = true; var ssc_pulsescale = 6; var ssc_pulsenormalize = 1; // keyboard settings var ssc_keyboardsupport = true; var ssc_arrowscroll = 50; // [px] // other variables var ssc_frame = false; var ssc_direction = { x: 0, y: 0 }; var ssc_initdone = false; var ssc_fixedback = true; var ssc_root = document.documentelement; var ssc_activeelement; var ssc_key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36 }; /*********************************************** * initialize ***********************************************/ /** * sets up scrolls array, determines if ssc_frames are involved. */ function ssc_init() { if (!document.body) return; var body = document.body; var html = document.documentelement; var windowheight = window.innerheight; var scrollheight = body.scrollheight; // check compat mode for ssc_root element ssc_root = (document.compatmode.indexof('css') >= 0) ? html : body; ssc_activeelement = body; ssc_initdone = true; // checks if this script is running in a ssc_frame if (top != self) { ssc_frame = true; } /** * this fixes a bug where the areas left and right to * the content does not trigger the onmousewheel event * on some pages. e.g.: html, body { height: 100% } */ else if (scrollheight > windowheight && (body.offsetheight <= windowheight || html.offsetheight <= windowheight)) { ssc_root.style.height = "auto"; if (ssc_root.offsetheight <= windowheight) { var underlay = document.createelement("div"); underlay.style.clear = "both"; body.appendchild(underlay); } } if (!ssc_fixedback) { body.style.backgroundattachment = "scroll"; html.style.backgroundattachment = "scroll"; } if (ssc_keyboardsupport) { ssc_addevent("keydown", ssc_keydown); } } /************************************************ * scrolling ************************************************/ var ssc_que = []; var ssc_pending = false; /** * pushes scroll actions to the scrolling queue. */ function ssc_scrollarray(elem, left, top, delay) { delay || (delay = 1000); ssc_directioncheck(left, top); // push a scroll command ssc_que.push({ x: left, y: top, lastx: (left < 0) ? 0.99 : -0.99, lasty: (top < 0) ? 0.99 : -0.99, start: +new date }); // don't act if there's a ssc_pending queue if (ssc_pending) { return; } var step = function() { var now = +new date; var scrollx = 0; var scrolly = 0; for (var i = 0; i < ssc_que.length; i++) { var item = ssc_que[i]; var elapsed = now - item.start; var finished = (elapsed >= ssc_animtime); // scroll position: [0, 1] var position = (finished) ? 1 : elapsed / ssc_animtime; // easing [optional] if (ssc_pulsealgorithm) { position = ssc_pulse(position); } // only need the difference var x = (item.x * position - item.lastx) >> 0; var y = (item.y * position - item.lasty) >> 0; // add this to the total scrolling scrollx += x; scrolly += y; // update last values item.lastx += x; item.lasty += y; // delete and step back if it's over if (finished) { ssc_que.splice(i, 1); i--; } } // scroll left if (left) { var lastleft = elem.scrollleft; elem.scrollleft += scrollx; // scroll left failed (edge) if (scrollx && elem.scrollleft === lastleft) { left = 0; } } // scroll top if (top) { var lasttop = elem.scrolltop; elem.scrolltop += scrolly; // scroll top failed (edge) if (scrolly && elem.scrolltop === lasttop) { top = 0; } } // clean up if there's nothing left to do if (!left && !top) { ssc_que = []; } if (ssc_que.length) { settimeout(step, delay / ssc_framerate + 1); } else { ssc_pending = false; } } // start a new queue of actions settimeout(step, 0); ssc_pending = true; } /*********************************************** * events ***********************************************/ /** * mouse ssc_wheel handler. * @param {object} event */ function ssc_wheel(event) { if (!ssc_initdone) { ssc_init(); } var target = event.target; var overflowing = ssc_overflowingancestor(target); // use default if there's no overflowing // element or default action is prevented if (!overflowing || event.defaultprevented || ssc_isnodename(ssc_activeelement, "embed") || (ssc_isnodename(target, "embed") && /\.pdf/i.test(target.src))) { return true; } var deltax = event.wheeldeltax || 0; var deltay = event.wheeldeltay || 0; // use wheeldelta if deltax/y is not available if (!deltax && !deltay) { deltay = event.wheeldelta || 0; } // scale by step size // delta is 120 most of the time // synaptics seems to send 1 sometimes if (math.abs(deltax) > 1.2) { deltax *= ssc_stepsize / 120; } if (math.abs(deltay) > 1.2) { deltay *= ssc_stepsize / 120; } ssc_scrollarray(overflowing, -deltax, -deltay); event.preventdefault(); } /** * ssc_keydown event handler. * @param {object} event */ function ssc_keydown(event) { var target = event.target; var modifier = event.ctrlkey || event.altkey || event.metakey; // do nothing if user is editing text // or using a modifier ssc_key (except shift) if ( /input|textarea|embed/i.test(target.nodename) || target.iscontenteditable || event.defaultprevented || modifier ) { return true; } // spacebar should trigger button press if (ssc_isnodename(target, "button") && event.keycode === ssc_key.spacebar) { return true; } var shift, x = 0, y = 0; var elem = ssc_overflowingancestor(ssc_activeelement); var clientheight = elem.clientheight; if (elem == document.body) { clientheight = window.innerheight; } switch (event.keycode) { case ssc_key.up: y = -ssc_arrowscroll; break; case ssc_key.down: y = ssc_arrowscroll; break; case ssc_key.spacebar: // (+ shift) shift = event.shiftkey ? 1 : -1; y = -shift * clientheight * 0.9; break; case ssc_key.pageup: y = -clientheight * 0.9; break; case ssc_key.pagedown: y = clientheight * 0.9; break; case ssc_key.home: y = -elem.scrolltop; break; case ssc_key.end: var damt = elem.scrollheight - elem.scrolltop - clientheight; y = (damt > 0) ? damt+10 : 0; break; case ssc_key.left: x = -ssc_arrowscroll; break; case ssc_key.right: x = ssc_arrowscroll; break; default: return true; // a ssc_key we don't care about } ssc_scrollarray(elem, x, y); event.preventdefault(); } /** * ssc_mousedown event only for updating ssc_activeelement */ function ssc_mousedown(event) { ssc_activeelement = event.target; } /*********************************************** * overflow ***********************************************/ var ssc_cache = {}; // cleared out every once in while setinterval(function(){ ssc_cache = {}; }, 10 * 1000); var ssc_uniqueid = (function() { var i = 0; return function (el) { return el.ssc_uniqueid || (el.ssc_uniqueid = i++); }; })(); function ssc_setcache(elems, overflowing) { for (var i = elems.length; i--;) ssc_cache[ssc_uniqueid(elems[i])] = overflowing; return overflowing; } function ssc_overflowingancestor(el) { var elems = []; var ssc_rootscrollheight = ssc_root.scrollheight; do { var cached = ssc_cache[ssc_uniqueid(el)]; if (cached) { return ssc_setcache(elems, cached); } elems.push(el); if (ssc_rootscrollheight === el.scrollheight) { if (!ssc_frame || ssc_root.clientheight + 10 < ssc_rootscrollheight) { return ssc_setcache(elems, document.body); // scrolling ssc_root in webkit } } else if (el.clientheight + 10 < el.scrollheight) { overflow = getcomputedstyle(el, "").getpropertyvalue("overflow"); if (overflow === "scroll" || overflow === "auto") { return ssc_setcache(elems, el); } } } while (el = el.parentnode); } /*********************************************** * helpers ***********************************************/ function ssc_addevent(type, fn, bubble) { if ( window.attachevent ) { window['e'+type+fn] = fn; window[type+fn] = function(){window['e'+type+fn]( window.event );} window.attachevent( 'on'+type, window[type+fn] ); //alert("cao,xing bu xing"); } else { window.addeventlistener( type, fn, (bubble||false) ); } //window.addeventlistener(type, fn, (bubble||false)); } function ssc_removeevent(type, fn, bubble) { if ( window.detachevent ) { window.detachevent( 'on'+type, window[type+fn] ); window[type+fn] = null; } else { window.removeeventlistener( type, fn, (bubble||false) ); } //window.removeeventlistener(type, fn, (bubble||false)); } function ssc_isnodename(el, tag) { return el.nodename.tolowercase() === tag.tolowercase(); } function ssc_directioncheck(x, y) { x = (x > 0) ? 1 : -1; y = (y > 0) ? 1 : -1; if (ssc_direction.x !== x || ssc_direction.y !== y) { ssc_direction.x = x; ssc_direction.y = y; ssc_que = []; } } /*********************************************** * ssc_pulse ***********************************************/ /** * viscous fluid with a ssc_pulse for part and decay for the rest. * - applies a fixed force over an interval (a damped acceleration), and * - lets the exponential bleed away the velocity over a longer interval * - michael herf, http://stereopsis.com/stopping/ */ function ssc_pulse_(x) { var val, start, expx; // test x = x * ssc_pulsescale; if (x < 1) { // acceleartion val = x - (1 - math.exp(-x)); } else { // tail // the previous animation ended here: start = math.exp(-1); // simple viscous drag x -= 1; expx = 1 - math.exp(-x); val = start + (expx * (1 - start)); } return val * ssc_pulsenormalize; } function ssc_pulse(x) { if (x >= 1) return 1; if (x <= 0) return 0; if (ssc_pulsenormalize == 1) { ssc_pulsenormalize /= ssc_pulse_(1); } return ssc_pulse_(x); } ssc_addevent("mousedown", ssc_mousedown); ssc_addevent("mousewheel", ssc_wheel); ssc_addevent("load", ssc_init);