Smooth Scroll (js) Hitskin_logo Hitskin.com

This is a Hitskin.com skin preview
Install the skinReturn to the skin page

The forum of the forums
Would you like to react to this message? Create an account in a few clicks or log in to continue.
2 posters

    Smooth Scroll (js)

    Melanore
    Melanore
    New Member


    Posts : 21
    Reputation : 7
    Language : EN
    Location : TR

    Solved Smooth Scroll (js)

    Post by Melanore July 8th 2015, 2:36 am

    I am trying to add this smoothscroll jscript to my forum: https://gist.github.com/galambalazs/6477177

    I tried to add it as javascript in ACP (applied in all the pages), tried to call raw .js link in my <head> tags by <script url...> or even tried to add whole code to header in <script type...> tags. Failed.

    What should I do?
    Ange Tuteur
    Ange Tuteur
    Forumaster


    Male Posts : 13207
    Reputation : 3000
    Language : English & 日本語
    Location : Pennsylvania

    Solved Re: Smooth Scroll (js)

    Post by Ange Tuteur July 8th 2015, 2:17 pm

    Hi @Melanore,

    I applied it to JS codes management in all the pages, but it threw an error because the person who wrote it forgot a semi-colon at line 561 which causes an error when minified by Forumotion's JS management.
    Code:
    deltaY = Math.abs(deltaY);

    Try adding the code below into JS codes management, it should work.
    Code:
    //
    // SmoothScroll for websites v1.3.8 (Balazs Galambosi)
    // Licensed under the terms of the MIT license.
    //
    // You may use it in your theme if you credit me.
    // It is also free to use on any individual website.
    //
    // Exception:
    // The only restriction would be not to publish any 
    // extension for browsers or native application
    // without getting a written permission first.
    //

    (function () {
     
    // Scroll Variables (tweakable)
    var defaultOptions = {

        // Scrolling Core
        frameRate        : 150, // [Hz]
        animationTime    : 400, // [px]
        stepSize        : 120, // [px]

        // Pulse (less tweakable)
        // ratio of "tail" to "acceleration"
        pulseAlgorithm  : true,
        pulseScale      : 4,
        pulseNormalize  : 1,

        // Acceleration
        accelerationDelta : 20,  // 20
        accelerationMax  : 1,  // 1

        // Keyboard Settings
        keyboardSupport  : true,  // option
        arrowScroll      : 50,    // [px]

        // Other
        touchpadSupport  : true,
        fixedBackground  : true,
        excluded          : ''   
    };

    var options = defaultOptions;


    // Other Variables
    var isExcluded = false;
    var isFrame = false;
    var direction = { x: 0, y: 0 };
    var initDone  = false;
    var root = document.documentElement;
    var activeElement;
    var observer;
    var deltaBuffer = [];
    var isMac = /^Mac/.test(navigator.platform);

    var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,
                pageup: 33, pagedown: 34, end: 35, home: 36 };


    /***********************************************
     * SETTINGS
     ***********************************************/

    var options = defaultOptions;


    /***********************************************
     * INITIALIZE
     ***********************************************/

    /**
     * Tests if smooth scrolling is allowed. Shuts down everything if not.
     */
    function initTest() {
        if (options.keyboardSupport) {
            addEvent('keydown', keydown);
        }
    }

    /**
     * Sets up scrolls array, determines if frames are involved.
     */
    function init() {
     
        if (initDone || !document.body) return;

        initDone = true;

        var body = document.body;
        var html = document.documentElement;
        var windowHeight = window.innerHeight;
        var scrollHeight = body.scrollHeight;
       
        // check compat mode for root element
        root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
        activeElement = body;
       
        initTest();

        // Checks if this script is running in a frame
        if (top != self) {
            isFrame = 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)) {

            var fullPageElem = document.createElement('div');
            fullPageElem.style.cssText = 'position:absolute; z-index:-10000; ' +
                                        'top:0; left:0; right:0; height:' +
                                          root.scrollHeight + 'px';
            document.body.appendChild(fullPageElem);
           
            // DOM changed (throttled) to fix height
            var pendingRefresh;
            var refresh = function () {
                if (pendingRefresh) return; // could also be: clearTimeout(pendingRefresh);
                pendingRefresh = setTimeout(function () {
                    if (isExcluded) return; // could be running after cleanup
                    fullPageElem.style.height = '0';
                    fullPageElem.style.height = root.scrollHeight + 'px';
                    pendingRefresh = null;
                }, 500); // act rarely to stay fast
            };
     
            setTimeout(refresh, 10);

            // TODO: attributeFilter?
            var config = {
                attributes: true,
                childList: true,
                characterData: false
                // subtree: true
            };

            observer = new MutationObserver(refresh);
            observer.observe(body, config);

            if (root.offsetHeight <= windowHeight) {
                var clearfix = document.createElement('div'); 
                clearfix.style.clear = 'both';
                body.appendChild(clearfix);
            }
        }

        // disable fixed background
        if (!options.fixedBackground && !isExcluded) {
            body.style.backgroundAttachment = 'scroll';
            html.style.backgroundAttachment = 'scroll';
        }
    }

    /**
     * Removes event listeners and other traces left on the page.
     */
    function cleanup() {
        observer && observer.disconnect();
        removeEvent(wheelEvent, wheel);
        removeEvent('mousedown', mousedown);
        removeEvent('keydown', keydown);
    }


    /************************************************
     * SCROLLING
     ************************************************/
     
    var que = [];
    var pending = false;
    var lastScroll = Date.now();

    /**
     * Pushes scroll actions to the scrolling queue.
     */
    function scrollArray(elem, left, top) {
       
        directionCheck(left, top);

        if (options.accelerationMax != 1) {
            var now = Date.now();
            var elapsed = now - lastScroll;
            if (elapsed < options.accelerationDelta) {
                var factor = (1 + (50 / elapsed)) / 2;
                if (factor > 1) {
                    factor = Math.min(factor, options.accelerationMax);
                    left *= factor;
                    top  *= factor;
                }
            }
            lastScroll = Date.now();
        }         
       
        // push a scroll command
        que.push({
            x: left,
            y: top,
            lastX: (left < 0) ? 0.99 : -0.99,
            lastY: (top  < 0) ? 0.99 : -0.99,
            start: Date.now()
        });
           
        // don't act if there's a pending queue
        if (pending) {
            return;
        } 

        var scrollWindow = (elem === document.body);
       
        var step = function (time) {
           
            var now = Date.now();
            var scrollX = 0;
            var scrollY = 0;
       
            for (var i = 0; i < que.length; i++) {
               
                var item = que[i];
                var elapsed  = now - item.start;
                var finished = (elapsed >= options.animationTime);
               
                // scroll position: [0, 1]
                var position = (finished) ? 1 : elapsed / options.animationTime;
               
                // easing [optional]
                if (options.pulseAlgorithm) {
                    position = 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) {
                    que.splice(i, 1); i--;
                }         
            }

            // scroll left and top
            if (scrollWindow) {
                window.scrollBy(scrollX, scrollY);
            }
            else {
                if (scrollX) elem.scrollLeft += scrollX;
                if (scrollY) elem.scrollTop  += scrollY;                   
            }
           
            // clean up if there's nothing left to do
            if (!left && !top) {
                que = [];
            }
           
            if (que.length) {
                requestFrame(step, elem, (1000 / options.frameRate + 1));
            } else {
                pending = false;
            }
        };
       
        // start a new queue of actions
        requestFrame(step, elem, 0);
        pending = true;
    }


    /***********************************************
     * EVENTS
     ***********************************************/

    /**
     * Mouse wheel handler.
     * @param {Object} event
     */
    function wheel(event) {

        if (!initDone) {
            init();
        }
       
        var target = event.target;
        var overflowing = overflowingAncestor(target);

        // use default if there's no overflowing
        // element or default action is prevented 
        // or it's a zooming event with CTRL
        if (!overflowing || event.defaultPrevented || event.ctrlKey) {
            return true;
        }
       
        // leave embedded content alone (flash & pdf)
        if (isNodeName(activeElement, 'embed') ||
          (isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) ||
          isNodeName(activeElement, 'object')) {
            return true;
        }

        var deltaX = -event.wheelDeltaX || event.deltaX || 0;
        var deltaY = -event.wheelDeltaY || event.deltaY || 0;
       
        if (isMac) {
            if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) {
                deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX));
            }
            if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) {
                deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY));
            }
        }
       
        // use wheelDelta if deltaX/Y is not available
        if (!deltaX && !deltaY) {
            deltaY = -event.wheelDelta || 0;
        }

        // line based scrolling (Firefox mostly)
        if (event.deltaMode === 1) {
            deltaX *= 40;
            deltaY *= 40;
        }
       
        // check if it's a touchpad scroll that should be ignored
        if (!options.touchpadSupport && isTouchpad(deltaY)) {
            return true;
        }

        // scale by step size
        // delta is 120 most of the time
        // synaptics seems to send 1 sometimes
        if (Math.abs(deltaX) > 1.2) {
            deltaX *= options.stepSize / 120;
        }
        if (Math.abs(deltaY) > 1.2) {
            deltaY *= options.stepSize / 120;
        }
       
        scrollArray(overflowing, deltaX, deltaY);
        event.preventDefault();
        scheduleClearCache();
    }

    /**
     * Keydown event handler.
     * @param {Object} event
     */
    function keydown(event) {

        var target  = event.target;
        var modifier = event.ctrlKey || event.altKey || event.metaKey ||
                      (event.shiftKey && event.keyCode !== key.spacebar);
       
        // our own tracked active element could've been removed from the DOM
        if (!document.contains(activeElement)) {
            activeElement = document.activeElement;
        }

        // do nothing if user is editing text
        // or using a modifier key (except shift)
        // or in a dropdown
        // or inside interactive elements
        var inputNodeNames = /^(textarea|select|embed|object)$/i;
        var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i;
        if ( inputNodeNames.test(target.nodeName) ||
            isNodeName(target, 'input') && !buttonTypes.test(target.type) ||
            isNodeName(activeElement, 'video') ||
            isInsideYoutubeVideo(event) ||
            target.isContentEditable ||
            event.defaultPrevented  ||
            modifier ) {
          return true;
        }
       
        // spacebar should trigger button press
        if ((isNodeName(target, 'button') ||
            isNodeName(target, 'input') && buttonTypes.test(target.type)) &&
            event.keyCode === key.spacebar) {
          return true;
        }
       
        var shift, x = 0, y = 0;
        var elem = overflowingAncestor(activeElement);
        var clientHeight = elem.clientHeight;

        if (elem == document.body) {
            clientHeight = window.innerHeight;
        }

        switch (event.keyCode) {
            case key.up:
                y = -options.arrowScroll;
                break;
            case key.down:
                y = options.arrowScroll;
                break;       
            case key.spacebar: // (+ shift)
                shift = event.shiftKey ? 1 : -1;
                y = -shift * clientHeight * 0.9;
                break;
            case key.pageup:
                y = -clientHeight * 0.9;
                break;
            case key.pagedown:
                y = clientHeight * 0.9;
                break;
            case key.home:
                y = -elem.scrollTop;
                break;
            case key.end:
                var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
                y = (damt > 0) ? damt+10 : 0;
                break;
            case key.left:
                x = -options.arrowScroll;
                break;
            case key.right:
                x = options.arrowScroll;
                break;           
            default:
                return true; // a key we don't care about
        }

        scrollArray(elem, x, y);
        event.preventDefault();
        scheduleClearCache();
    }

    /**
     * Mousedown event only for updating activeElement
     */
    function mousedown(event) {
        activeElement = event.target;
    }


    /***********************************************
     * OVERFLOW
     ***********************************************/

    var uniqueID = (function () {
        var i = 0;
        return function (el) {
            return el.uniqueID || (el.uniqueID = i++);
        };
    })();

    var cache = {}; // cleared out after a scrolling session
    var clearCacheTimer;

    //setInterval(function () { cache = {}; }, 10 * 1000);

    function scheduleClearCache() {
        clearTimeout(clearCacheTimer);
        clearCacheTimer = setInterval(function () { cache = {}; }, 1*1000);
    }

    function setCache(elems, overflowing) {
        for (var i = elems.length; i--;)
            cache[uniqueID(elems[i])] = overflowing;
        return overflowing;
    }

    //  (body)                (root)
    //        | hidden | visible | scroll |  auto  |
    // hidden  |  no  |    no  |  YES  |  YES  |
    // visible |  no  |  YES  |  YES  |  YES  |
    // scroll  |  no  |  YES  |  YES  |  YES  |
    // auto    |  no  |  YES  |  YES  |  YES  |

    function overflowingAncestor(el) {
        var elems = [];
        var body = document.body;
        var rootScrollHeight = root.scrollHeight;
        do {
            var cached = cache[uniqueID(el)];
            if (cached) {
                return setCache(elems, cached);
            }
            elems.push(el);
            if (rootScrollHeight === el.scrollHeight) {
                var topOverflowsNotHidden = overflowNotHidden(root) && overflowNotHidden(body);
                var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root);
                if (isFrame && isContentOverflowing(root) ||
                  !isFrame && isOverflowCSS) {
                    return setCache(elems, getScrollRoot());
                }
            } else if (isContentOverflowing(el) && overflowAutoOrScroll(el)) {
                return setCache(elems, el);
            }
        } while (el = el.parentElement);
    }

    function isContentOverflowing(el) {
        return (el.clientHeight + 10 < el.scrollHeight);
    }

    // typically for <body> and <html>
    function overflowNotHidden(el) {
        var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
        return (overflow !== 'hidden');
    }

    // for all other elements
    function overflowAutoOrScroll(el) {
        var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
        return (overflow === 'scroll' || overflow === 'auto');
    }


    /***********************************************
     * HELPERS
     ***********************************************/

    function addEvent(type, fn) {
        window.addEventListener(type, fn, false);
    }

    function removeEvent(type, fn) {
        window.removeEventListener(type, fn, false); 
    }

    function isNodeName(el, tag) {
        return (el.nodeName||'').toLowerCase() === tag.toLowerCase();
    }

    function directionCheck(x, y) {
        x = (x > 0) ? 1 : -1;
        y = (y > 0) ? 1 : -1;
        if (direction.x !== x || direction.y !== y) {
            direction.x = x;
            direction.y = y;
            que = [];
            lastScroll = 0;
        }
    }

    var deltaBufferTimer;

    if (window.localStorage && localStorage.SS_deltaBuffer) {
        deltaBuffer = localStorage.SS_deltaBuffer.split(',');
    }

    function isTouchpad(deltaY) {
        if (!deltaY) return;
        if (!deltaBuffer.length) {
            deltaBuffer = [deltaY, deltaY, deltaY];
        }
        deltaY = Math.abs(deltaY);
        deltaBuffer.push(deltaY);
        deltaBuffer.shift();
        clearTimeout(deltaBufferTimer);
        deltaBufferTimer = setTimeout(function () {
            if (window.localStorage) {
                localStorage.SS_deltaBuffer = deltaBuffer.join(',');
            }
        }, 1000);
        return !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100);
    }

    function isDivisible(n, divisor) {
        return (Math.floor(n / divisor) == n / divisor);
    }

    function allDeltasDivisableBy(divisor) {
        return (isDivisible(deltaBuffer[0], divisor) &&
                isDivisible(deltaBuffer[1], divisor) &&
                isDivisible(deltaBuffer[2], divisor));
    }

    function isInsideYoutubeVideo(event) {
        var elem = event.target;
        var isControl = false;
        if (document.URL.indexOf ('www.youtube.com/watch') != -1) {
            do {
                isControl = (elem.classList &&
                            elem.classList.contains('html5-video-controls'));
                if (isControl) break;
            } while (elem = elem.parentNode);
        }
        return isControl;
    }

    var requestFrame = (function () {
          return (window.requestAnimationFrame      ||
                  window.webkitRequestAnimationFrame ||
                  window.mozRequestAnimationFrame    ||
                  function (callback, element, delay) {
                    window.setTimeout(callback, delay || (1000/60));
                });
    })();

    var MutationObserver = (window.MutationObserver ||
                            window.WebKitMutationObserver ||
                            window.MozMutationObserver); 

    var getScrollRoot = (function() {
      var SCROLL_ROOT;
      return function() {
        if (!SCROLL_ROOT) {
          var dummy = document.createElement('div');
          dummy.style.cssText = 'height:10000px;width:1px;';
          document.body.appendChild(dummy);
          var bodyScrollTop  = document.body.scrollTop;
          var docElScrollTop = document.documentElement.scrollTop;
          window.scrollBy(0, 1);
          if (document.body.scrollTop != bodyScrollTop)
            (SCROLL_ROOT = document.body);
          else
            (SCROLL_ROOT = document.documentElement);
          window.scrollBy(0, -1);
          document.body.removeChild(dummy);
        }
        return SCROLL_ROOT;
      };
    })();


    /***********************************************
     * PULSE (by Michael Herf)
     ***********************************************/
     
    /**
     * Viscous fluid with a 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 pulse_(x) {
        var val, start, expx;
        // test
        x = x * options.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 * options.pulseNormalize;
    }

    function pulse(x) {
        if (x >= 1) return 1;
        if (x <= 0) return 0;

        if (options.pulseNormalize == 1) {
            options.pulseNormalize /= pulse_(1);
        }
        return pulse_(x);
    }

    var wheelEvent;
    if ('onwheel' in document.createElement('div'))
        wheelEvent = 'wheel';
    else if ('onmousewheel' in document.createElement('div'))
        wheelEvent = 'mousewheel';

    if (wheelEvent) {
        addEvent(wheelEvent, wheel);
        addEvent('mousedown', mousedown);
        addEvent('load', init);
    }

    })();

    From what I tested, it's working on google chrome for me.
    Melanore
    Melanore
    New Member


    Posts : 21
    Reputation : 7
    Language : EN
    Location : TR

    Solved Re: Smooth Scroll (js)

    Post by Melanore July 8th 2015, 2:23 pm

    It works. Thank you very much!  
    Hope I can understand jscript soon, it is still too hard for me!  ^^'
    Topic #Solved thumleft
    Ange Tuteur
    Ange Tuteur
    Forumaster


    Male Posts : 13207
    Reputation : 3000
    Language : English & 日本語
    Location : Pennsylvania

    Solved Re: Smooth Scroll (js)

    Post by Ange Tuteur July 8th 2015, 2:45 pm

    You're welcome ^^

    Topic archived

    JS takes a bit of time to learn, especially if you've no prior experience in programming. This is only my second year of working with it, so there's still much to learn. Smile

    Have a nice day. :rose: