JAVASCRIPT   40

smoothScroll js

Guest on 13th August 2022 12:45:48 AM

  1. /** SMOOTHSCROLL V1.2.1
  2.         Licensed under the terms of the MIT license.
  3.  **************************************************************** **/
  4. (function($) {
  5.         $.extend({
  6.  
  7.                 smoothScroll: function() {
  8.  
  9.                         // Scroll Variables (tweakable)
  10.                         var defaultOptions = {
  11.  
  12.                                 // Scrolling Core
  13.                                 frameRate        : 60, // [Hz]
  14.                                 animationTime    : 700, // [px]
  15.                                 stepSize         : 120, // [px]
  16.  
  17.                                 // Pulse (less tweakable)
  18.                                 // ratio of "tail" to "acceleration"
  19.                                 pulseAlgorithm   : true,
  20.                                 pulseScale       : 10,
  21.                                 pulseNormalize   : 1,
  22.  
  23.                                 // Acceleration
  24.                                 accelerationDelta : 20,  // 20
  25.                                 accelerationMax   : 1,   // 1
  26.  
  27.                                 // Keyboard Settings
  28.                                 keyboardSupport   : true,  // option
  29.                                 arrowScroll       : 50,     // [px]
  30.  
  31.                                 // Other
  32.                                 touchpadSupport   : true,
  33.                                 fixedBackground   : true,
  34.                                 excluded          : ""
  35.                         };
  36.  
  37.                         var options = defaultOptions;
  38.  
  39.                         // Other Variables
  40.                         var isExcluded = false;
  41.                         var isFrame = false;
  42.                         var direction = { x: 0, y: 0 };
  43.                         var initDone  = false;
  44.                         var root = document.documentElement;
  45.                         var activeElement;
  46.                         var observer;
  47.                         var deltaBuffer = [ 120, 120, 120 ];
  48.  
  49.                         var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,
  50.                                                 pageup: 33, pagedown: 34, end: 35, home: 36 };
  51.  
  52.  
  53.                         /***********************************************
  54.                          * INITIALIZE
  55.                          ***********************************************/
  56.  
  57.                         /**
  58.                          * Tests if smooth scrolling is allowed. Shuts down everything if not.
  59.                          */
  60.                         function initTest() {
  61.  
  62.                                 var disableKeyboard = false;
  63.  
  64.                                 // disable keys for google reader (spacebar conflict)
  65.                                 if (document.URL.indexOf("google.com/reader/view") > -1) {
  66.                                         disableKeyboard = true;
  67.                                 }
  68.  
  69.                                 // disable everything if the page is blacklisted
  70.                                 if (options.excluded) {
  71.                                         var domains = options.excluded.split(/[,\n] ?/);
  72.                                         domains.push("mail.google.com"); // exclude Gmail for now
  73.                                         for (var i = domains.length; i--;) {
  74.                                                 if (document.URL.indexOf(domains[i]) > -1) {
  75.                                                         observer && observer.disconnect();
  76.                                                         removeEvent("mousewheel", wheel);
  77.                                                         disableKeyboard = true;
  78.                                                         isExcluded = true;
  79.                                                         break;
  80.                                                 }
  81.                                         }
  82.                                 }
  83.  
  84.                                 // disable keyboard support if anything above requested it
  85.                                 if (disableKeyboard) {
  86.                                         removeEvent("keydown", keydown);
  87.                                 }
  88.  
  89.                                 if (options.keyboardSupport && !disableKeyboard) {
  90.                                         addEvent("keydown", keydown);
  91.                                 }
  92.                         }
  93.  
  94.                         /**
  95.                          * Sets up scrolls array, determines if frames are involved.
  96.                          */
  97.                         function init() {
  98.  
  99.                                 if (!document.body) return;
  100.  
  101.                                 var body = document.body;
  102.                                 var html = document.documentElement;
  103.                                 var windowHeight = window.innerHeight;
  104.                                 var scrollHeight = body.scrollHeight;
  105.  
  106.                                 // check compat mode for root element
  107.                                 root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
  108.                                 activeElement = body;
  109.  
  110.                                 initTest();
  111.                                 initDone = true;
  112.  
  113.                                 // Checks if this script is running in a frame
  114.                                 if (top != self) {
  115.                                         isFrame = true;
  116.                                 }
  117.  
  118.                                 /**
  119.                                  * This fixes a bug where the areas left and right to
  120.                                  * the content does not trigger the onmousewheel event
  121.                                  * on some pages. e.g.: html, body { height: 100% }
  122.                                  */
  123.                                 else if (scrollHeight > windowHeight &&
  124.                                                 (body.offsetHeight <= windowHeight ||
  125.                                                  html.offsetHeight <= windowHeight)) {
  126.  
  127.                                         // DOMChange (throttle): fix height
  128.                                         var pending = false;
  129.                                         var refresh = function () {
  130.                                                 if (!pending && html.scrollHeight != document.height) {
  131.                                                         pending = true; // add a new pending action
  132.                                                         setTimeout(function () {
  133.                                                                 html.style.height = document.height + 'px';
  134.                                                                 pending = false;
  135.                                                         }, 500); // act rarely to stay fast
  136.                                                 }
  137.                                         };
  138.                                         html.style.height = 'auto';
  139.                                         setTimeout(refresh, 10);
  140.  
  141.                                         var config = {
  142.                                                 attributes: true,
  143.                                                 childList: true,
  144.                                                 characterData: false
  145.                                         };
  146.  
  147.                                         observer = new MutationObserver(refresh);
  148.                                         observer.observe(body, config);
  149.  
  150.                                         // clearfix
  151.                                         if (root.offsetHeight <= windowHeight) {
  152.                                                 var underlay = document.createElement("div");
  153.                                                 underlay.style.clear = "both";
  154.                                                 body.appendChild(underlay);
  155.                                         }
  156.                                 }
  157.  
  158.                                 // gmail performance fix
  159.                                 if (document.URL.indexOf("mail.google.com") > -1) {
  160.                                         var s = document.createElement("style");
  161.                                         s.innerHTML = ".iu { visibility: hidden }";
  162.                                         (document.getElementsByTagName("head")[0] || html).appendChild(s);
  163.                                 }
  164.                                 // facebook better home timeline performance
  165.                                 // all the HTML resized images make rendering CPU intensive
  166.                                 else if (document.URL.indexOf("www.facebook.com") > -1) {
  167.                                         var home_stream = document.getElementById("home_stream");
  168.                                         home_stream && (home_stream.style.webkitTransform = "translateZ(0)");
  169.                                 }
  170.                                 // disable fixed background
  171.                                 if (!options.fixedBackground && !isExcluded) {
  172.                                         body.style.backgroundAttachment = "scroll";
  173.                                         html.style.backgroundAttachment = "scroll";
  174.                                 }
  175.                         }
  176.  
  177.  
  178.                         /************************************************
  179.                          * SCROLLING
  180.                          ************************************************/
  181.  
  182.                         var que = [];
  183.                         var pending = false;
  184.                         var lastScroll = +new Date;
  185.  
  186.                         /**
  187.                          * Pushes scroll actions to the scrolling queue.
  188.                          */
  189.                         function scrollArray(elem, left, top, delay) {
  190.  
  191.                                 delay || (delay = 1000);
  192.                                 directionCheck(left, top);
  193.  
  194.                                 if (options.accelerationMax != 1) {
  195.                                         var now = +new Date;
  196.                                         var elapsed = now - lastScroll;
  197.                                         if (elapsed < options.accelerationDelta) {
  198.                                                 var factor = (1 + (30 / elapsed)) / 2;
  199.                                                 if (factor > 1) {
  200.                                                         factor = Math.min(factor, options.accelerationMax);
  201.                                                         left *= factor;
  202.                                                         top  *= factor;
  203.                                                 }
  204.                                         }
  205.                                         lastScroll = +new Date;
  206.                                 }
  207.  
  208.                                 // push a scroll command
  209.                                 que.push({
  210.                                         x: left,
  211.                                         y: top,
  212.                                         lastX: (left < 0) ? 0.99 : -0.99,
  213.                                         lastY: (top  < 0) ? 0.99 : -0.99,
  214.                                         start: +new Date
  215.                                 });
  216.  
  217.                                 // don't act if there's a pending queue
  218.                                 if (pending) {
  219.                                         return;
  220.                                 }
  221.  
  222.                                 var scrollWindow = (elem === document.body);
  223.  
  224.                                 var step = function (time) {
  225.  
  226.                                         var now = +new Date;
  227.                                         var scrollX = 0;
  228.                                         var scrollY = 0;
  229.  
  230.                                         for (var i = 0; i < que.length; i++) {
  231.  
  232.                                                 var item = que[i];
  233.                                                 var elapsed  = now - item.start;
  234.                                                 var finished = (elapsed >= options.animationTime);
  235.  
  236.                                                 // scroll position: [0, 1]
  237.                                                 var position = (finished) ? 1 : elapsed / options.animationTime;
  238.  
  239.                                                 // easing [optional]
  240.                                                 if (options.pulseAlgorithm) {
  241.                                                         position = pulse(position);
  242.                                                 }
  243.  
  244.                                                 // only need the difference
  245.                                                 var x = (item.x * position - item.lastX) >> 0;
  246.                                                 var y = (item.y * position - item.lastY) >> 0;
  247.  
  248.                                                 // add this to the total scrolling
  249.                                                 scrollX += x;
  250.                                                 scrollY += y;
  251.  
  252.                                                 // update last values
  253.                                                 item.lastX += x;
  254.                                                 item.lastY += y;
  255.  
  256.                                                 // delete and step back if it's over
  257.                                                 if (finished) {
  258.                                                         que.splice(i, 1); i--;
  259.                                                 }
  260.                                         }
  261.  
  262.                                         // scroll left and top
  263.                                         if (scrollWindow) {
  264.                                                 window.scrollBy(scrollX, scrollY);
  265.                                         }
  266.                                         else {
  267.                                                 if (scrollX) elem.scrollLeft += scrollX;
  268.                                                 if (scrollY) elem.scrollTop  += scrollY;
  269.                                         }
  270.  
  271.                                         // clean up if there's nothing left to do
  272.                                         if (!left && !top) {
  273.                                                 que = [];
  274.                                         }
  275.  
  276.                                         if (que.length) {
  277.                                                 requestFrame(step, elem, (delay / options.frameRate + 1));
  278.                                         } else {
  279.                                                 pending = false;
  280.                                         }
  281.                                 };
  282.  
  283.                                 // start a new queue of actions
  284.                                 requestFrame(step, elem, 0);
  285.                                 pending = true;
  286.                         }
  287.  
  288.  
  289.                         /***********************************************
  290.                          * EVENTS
  291.                          ***********************************************/
  292.  
  293.                         /**
  294.                          * Mouse wheel handler.
  295.                          * @param {Object} event
  296.                          */
  297.                         function wheel(event) {
  298.  
  299.                                 if (!initDone) {
  300.                                         init();
  301.                                 }
  302.  
  303.                                 var target = event.target;
  304.                                 var overflowing = overflowingAncestor(target);
  305.  
  306.                                 // use default if there's no overflowing
  307.                                 // element or default action is prevented
  308.                                 if (!overflowing || event.defaultPrevented ||
  309.                                         isNodeName(activeElement, "embed") ||
  310.                                    (isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {
  311.                                         return true;
  312.                                 }
  313.  
  314.                                 var deltaX = event.wheelDeltaX || 0;
  315.                                 var deltaY = event.wheelDeltaY || 0;
  316.  
  317.                                 // use wheelDelta if deltaX/Y is not available
  318.                                 if (!deltaX && !deltaY) {
  319.                                         deltaY = event.wheelDelta || 0;
  320.                                 }
  321.  
  322.                                 // check if it's a touchpad scroll that should be ignored
  323.                                 if (!options.touchpadSupport && isTouchpad(deltaY)) {
  324.                                         return true;
  325.                                 }
  326.  
  327.                                 // scale by step size
  328.                                 // delta is 120 most of the time
  329.                                 // synaptics seems to send 1 sometimes
  330.                                 if (Math.abs(deltaX) > 1.2) {
  331.                                         deltaX *= options.stepSize / 120;
  332.                                 }
  333.                                 if (Math.abs(deltaY) > 1.2) {
  334.                                         deltaY *= options.stepSize / 120;
  335.                                 }
  336.  
  337.                                 scrollArray(overflowing, -deltaX, -deltaY);
  338.                                 event.preventDefault();
  339.                         }
  340.  
  341.                         /**
  342.                          * Keydown event handler.
  343.                          * @param {Object} event
  344.                          */
  345.                         function keydown(event) {
  346.  
  347.                                 var target   = event.target;
  348.                                 var modifier = event.ctrlKey || event.altKey || event.metaKey ||
  349.                                                           (event.shiftKey && event.keyCode !== key.spacebar);
  350.  
  351.                                 // do nothing if user is editing text
  352.                                 // or using a modifier key (except shift)
  353.                                 // or in a dropdown
  354.                                 if ( /input|textarea|select|embed/i.test(target.nodeName) ||
  355.                                          target.isContentEditable ||
  356.                                          event.defaultPrevented   ||
  357.                                          modifier ) {
  358.                                   return true;
  359.                                 }
  360.                                 // spacebar should trigger button press
  361.                                 if (isNodeName(target, "button") &&
  362.                                         event.keyCode === key.spacebar) {
  363.                                   return true;
  364.                                 }
  365.  
  366.                                 var shift, x = 0, y = 0;
  367.                                 var elem = overflowingAncestor(activeElement);
  368.                                 var clientHeight = elem.clientHeight;
  369.  
  370.                                 if (elem == document.body) {
  371.                                         clientHeight = window.innerHeight;
  372.                                 }
  373.  
  374.                                 switch (event.keyCode) {
  375.                                         case key.up:
  376.                                                 y = -options.arrowScroll;
  377.                                                 break;
  378.                                         case key.down:
  379.                                                 y = options.arrowScroll;
  380.                                                 break;
  381.                                         case key.spacebar: // (+ shift)
  382.                                                 shift = event.shiftKey ? 1 : -1;
  383.                                                 y = -shift * clientHeight * 0.9;
  384.                                                 break;
  385.                                         case key.pageup:
  386.                                                 y = -clientHeight * 0.9;
  387.                                                 break;
  388.                                         case key.pagedown:
  389.                                                 y = clientHeight * 0.9;
  390.                                                 break;
  391.                                         case key.home:
  392.                                                 y = -elem.scrollTop;
  393.                                                 break;
  394.                                         case key.end:
  395.                                                 var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
  396.                                                 y = (damt > 0) ? damt+10 : 0;
  397.                                                 break;
  398.                                         case key.left:
  399.                                                 x = -options.arrowScroll;
  400.                                                 break;
  401.                                         case key.right:
  402.                                                 x = options.arrowScroll;
  403.                                                 break;
  404.                                         default:
  405.                                                 return true; // a key we don't care about
  406.                                 }
  407.  
  408.                                 scrollArray(elem, x, y);
  409.                                 event.preventDefault();
  410.                         }
  411.  
  412.                         /**
  413.                          * Mousedown event only for updating activeElement
  414.                          */
  415.                         function mousedown(event) {
  416.                                 activeElement = event.target;
  417.                         }
  418.  
  419.  
  420.                         /***********************************************
  421.                          * OVERFLOW
  422.                          ***********************************************/
  423.  
  424.                         var cache = {}; // cleared out every once in while
  425.                         setInterval(function () { cache = {}; }, 10 * 1000);
  426.  
  427.                         var uniqueID = (function () {
  428.                                 var i = 0;
  429.                                 return function (el) {
  430.                                         return el.uniqueID || (el.uniqueID = i++);
  431.                                 };
  432.                         })();
  433.  
  434.                         function setCache(elems, overflowing) {
  435.                                 for (var i = elems.length; i--;)
  436.                                         cache[uniqueID(elems[i])] = overflowing;
  437.                                 return overflowing;
  438.                         }
  439.  
  440.                         function overflowingAncestor(el) {
  441.                                 var elems = [];
  442.                                 var rootScrollHeight = root.scrollHeight;
  443.                                 do {
  444.                                         var cached = cache[uniqueID(el)];
  445.                                         if (cached) {
  446.                                                 return setCache(elems, cached);
  447.                                         }
  448.                                         elems.push(el);
  449.                                         if (rootScrollHeight === el.scrollHeight) {
  450.                                                 if (!isFrame || root.clientHeight + 10 < rootScrollHeight) {
  451.                                                         return setCache(elems, document.body); // scrolling root in WebKit
  452.                                                 }
  453.                                         } else if (el.clientHeight + 10 < el.scrollHeight) {
  454.                                                 overflow = getComputedStyle(el, "").getPropertyValue("overflow-y");
  455.                                                 if (overflow === "scroll" || overflow === "auto") {
  456.                                                         return setCache(elems, el);
  457.                                                 }
  458.                                         }
  459.                                 } while (el = el.parentNode);
  460.                         }
  461.  
  462.  
  463.                         /***********************************************
  464.                          * HELPERS
  465.                          ***********************************************/
  466.  
  467.                         function addEvent(type, fn, bubble) {
  468.                                 window.addEventListener(type, fn, (bubble||false));
  469.                         }
  470.  
  471.                         function removeEvent(type, fn, bubble) {
  472.                                 window.removeEventListener(type, fn, (bubble||false));
  473.                         }
  474.  
  475.                         function isNodeName(el, tag) {
  476.                                 return (el.nodeName||"").toLowerCase() === tag.toLowerCase();
  477.                         }
  478.  
  479.                         function directionCheck(x, y) {
  480.                                 x = (x > 0) ? 1 : -1;
  481.                                 y = (y > 0) ? 1 : -1;
  482.                                 if (direction.x !== x || direction.y !== y) {
  483.                                         direction.x = x;
  484.                                         direction.y = y;
  485.                                         que = [];
  486.                                         lastScroll = 0;
  487.                                 }
  488.                         }
  489.  
  490.                         var deltaBufferTimer;
  491.  
  492.                         function isTouchpad(deltaY) {
  493.                                 if (!deltaY) return;
  494.                                 deltaY = Math.abs(deltaY)
  495.                                 deltaBuffer.push(deltaY);
  496.                                 deltaBuffer.shift();
  497.                                 clearTimeout(deltaBufferTimer);
  498.                                 var allEquals    = (deltaBuffer[0] == deltaBuffer[1] &&
  499.                                                                         deltaBuffer[1] == deltaBuffer[2]);
  500.                                 var allDivisable = (isDivisible(deltaBuffer[0], 120) &&
  501.                                                                         isDivisible(deltaBuffer[1], 120) &&
  502.                                                                         isDivisible(deltaBuffer[2], 120));
  503.                                 return !(allEquals || allDivisable);
  504.                         }
  505.  
  506.                         function isDivisible(n, divisor) {
  507.                                 return (Math.floor(n / divisor) == n / divisor);
  508.                         }
  509.  
  510.                         var requestFrame = (function () {
  511.                                   return  window.requestAnimationFrame       ||
  512.                                                   window.webkitRequestAnimationFrame ||
  513.                                                   function (callback, element, delay) {
  514.                                                           window.setTimeout(callback, delay || (1000/60));
  515.                                                   };
  516.                         })();
  517.  
  518.                         var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
  519.  
  520.  
  521.                         /***********************************************
  522.                          * PULSE
  523.                          ***********************************************/
  524.  
  525.                         /**
  526.                          * Viscous fluid with a pulse for part and decay for the rest.
  527.                          * - Applies a fixed force over an interval (a damped acceleration), and
  528.                          * - Lets the exponential bleed away the velocity over a longer interval
  529.                          * - Michael Herf, http://stereopsis.com/stopping/
  530.                          */
  531.                         function pulse_(x) {
  532.                                 var val, start, expx;
  533.                                 // test
  534.                                 x = x * options.pulseScale;
  535.                                 if (x < 1) { // acceleartion
  536.                                         val = x - (1 - Math.exp(-x));
  537.                                 } else {     // tail
  538.                                         // the previous animation ended here:
  539.                                         start = Math.exp(-1);
  540.                                         // simple viscous drag
  541.                                         x -= 1;
  542.                                         expx = 1 - Math.exp(-x);
  543.                                         val = start + (expx * (1 - start));
  544.                                 }
  545.                                 return val * options.pulseNormalize;
  546.                         }
  547.  
  548.                         function pulse(x) {
  549.                                 if (x >= 1) return 1;
  550.                                 if (x <= 0) return 0;
  551.  
  552.                                 if (options.pulseNormalize == 1) {
  553.                                         options.pulseNormalize /= pulse_(1);
  554.                                 }
  555.                                 return pulse_(x);
  556.                         }
  557.  
  558.                         addEvent("mousedown", mousedown);
  559.                         addEvent("mousewheel", wheel);
  560.                         addEvent("load", init);
  561.  
  562.                 }
  563.  
  564.         });
  565. })(jQuery);

Raw Paste


Login or Register to edit or fork this paste. It's free.