scrollbarinst

Benchmark created by gurami on


Preparation HTML

<style>
    .track_vertical
    {
        position:absolute;
        top:0;
        bottom:0;
        margin:auto;
        right:10px;
        background:#231f1f;
        width:3px;
        z-index: 9999;
    }
    .thumb_vertical
    {
        position:absolute;
        background:#7C2845;
        width:5px;
        top:0;
        left:-1px;
        cursor: url('../img/grab.cur'),move;
    }
    .track_horizontal
    {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 10px;
        height: 3px;
        margin: auto;
        background: #231f1f;
        z-index: 9999;
    }
    .thumb_horizontal
    {
        position:absolute;
        background:#7C2845;
        height:5px;
        left:0;
        top:-1px;
        cursor: url('../img/grab.cur'),move;
    }
</style>

<div class="viewport" style="background-color:blue;position:relative;overflow:hidden;width:500px;height:300px;">
    <div id="unique_1" style="position: absolute;top: 0;left: 0;width:900px;height:900px;" class="scroller">
        <img id="fake_target" style="position: relative;width:900px;height:900px;" src="http://akhmetelitheatre.ge/cms/scrollbar/img/1.jpg" />
    </div>
</div>

Setup

var _scrollBarInstances = [],
                filterArray = function(a,pop) {
                    return a.filter(function(i) {return pop.indexOf(i) < 0;});
                },
                returnDim = function(x,y)
                {
                    var arr = [];
                    if(x){arr.push(x)}
                    if(y){arr.push(y)}
                    return arr;
                },
                _gd_activeAnimation = null,
                animatedProperty = '-webkit-transform',
            style = { horizontalLow : 'width', verticalLow : 'height',horizontalCap : 'Width',verticalCap : 'Height',horizontalXY : 'X',verticalXY : 'Y' },
                frame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { return window.setTimeout(callback, 1000 / 60);},
                stopFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame || function (callback) { window.clearTimeout(callback);};
    
    
                function mouseDragEvents()
        {
            var mousedown = false,
                    target,
                    firstTarget,
                    targetInstance,
                    targetDim,
                    dimLength = 0,
                    unique = {},
                    start = {},
                    move = {},
                    end = {},
                    change = {},
                    delta = {},
                    startTime,
                    endTime,
                    speed,
                    isThumb = -1,
                    ratio = 1,
                    inverseDim,
                    inverseDimEl,
                    ThumbX,
                    ThumbY;
            /** Attach events **/
            //window.addEventListener("mousedown", MouseDown, false);// IE9, Chrome, Safari, Opera
            //window.addEventListener("mouseup", MouseUp, false);// IE9, Chrome, Safari, Opera
            //window.addEventListener("mousemove", MouseMove, false);// IE9, Chrome, Safari, Opera
            /** Function in work **/
    
    
            //MouseUp(mouseEvent('mouseup', 0, 0, 0, 0));
    
    //        setTimeout(function(){
    //            MouseMove(mouseEvent('mousemove', -5, -5, -5, -5));
    //            MouseMove(mouseEvent('mousemove', -10, -10, -10, -10));
    //            MouseMove(mouseEvent('mousemove', -15, -15, -15, -15));
    //            MouseMove(mouseEvent('mousemove', -20, -20, -20, -20));
    //            MouseMove(mouseEvent('mousemove', -25, -25, -25, -25));
    //            MouseMove(mouseEvent('mousemove', -30, -30, -30, -30));
    //            MouseMove(mouseEvent('mousemove', -35, -35, -35, -35));
    //            MouseMove(mouseEvent('mousemove', -40, -40, -40, -40));
    //            MouseMove(mouseEvent('mousemove', -45, -45, -45, -45));
    //        },200);
    //
    //        setTimeout(function(){
    //            MouseUp(mouseEvent('mouseup', -50, -50, -50, -50));
    //        },500);
    
    
            this.MouseDown = function(e)
            {
                e.preventDefault();
                e.stopPropagation();
    
                target = document.getElementById('fake_target');
                startTime = e.timeStamp;
                firstTarget = target;
    
                while(!target.hasAttribute('gd_scroll')){ target = target.parentNode; }
    
                for(var i = 0,l = _scrollBarInstances.length;i<l;++i){ if(target === _scrollBarInstances[i].viewport.el){  targetInstance = _scrollBarInstances[i]; } }
    
                if(targetInstance !== null)
                {
                    if(targetInstance.thumb)
                    {
                        ThumbX = targetInstance.thumb['horizontal'] ? targetInstance.thumb['horizontal'].el  : null;
                        ThumbY = targetInstance.thumb['vertical'] ? targetInstance.thumb['vertical'].el  : null;
                    }
    
                    isThumb = (ThumbX === firstTarget || ThumbY === firstTarget) ? 1 : -1;
                    ratio = ThumbY == firstTarget ? targetInstance.ratio['vertical'] : ThumbX == firstTarget ? targetInstance.ratio['horizontal'] : 1;
    
                    targetDim = returnDim(targetInstance.dimension.horizontal,targetInstance.dimension.vertical);
                    dimLength = targetDim.length;
    
                    if(dimLength > 0){ targetInstance.viewport.el.setAttribute('dragging','true') }
    
                    for(var k = 0; k < dimLength; ++k)
                    {
                        start[''+targetDim[k]+''] = unique[''+targetDim[k]+''] = e['page'+style[''+targetDim[k]+'XY']+''];
                    }
                    mousedown = true;
                }
    
            }
            this.MouseDownFree = function(e)
            {
                e.preventDefault();
                e.stopPropagation();
    
                target = document.getElementById('fake_target');
                startTime = e.timeStamp;
                firstTarget = target;
    
                while(!target.hasAttribute('gd_scroll')){ target = target.parentNode; }
    
            }
            this.MouseMove = function(e)
            {
    
                if(mousedown)
                {
    
                    for(var k = 0; k < dimLength; ++k)
                    {
                        inverseDim = filterArray(targetDim,targetDim[k])[0];
                        if(targetInstance.thumb)
                        {
                            inverseDimEl = targetInstance.thumb[''+inverseDim+''] ? targetInstance.thumb[''+inverseDim+''].el : null;
                        }
                        else
                        {
                            inverseDimEl = null;
                        }
    
                        move[''+targetDim[k]+''] = e['page'+style[''+targetDim[k]+'XY']+''];
    
                        delta[''+targetDim[k]+''] = isThumb*((start[''+targetDim[k]+'']-move[''+targetDim[k]+''])/Math.abs(start[''+targetDim[k]+'']-move[''+targetDim[k]+'']));
    
                        change[''+targetDim[k]+''] = inverseDimEl == firstTarget ? 0 : Math.abs(start[''+targetDim[k]+'']-move[''+targetDim[k]+''])/ratio;
    
                        start[''+targetDim[k]+''] = e['page'+style[''+targetDim[k]+'XY']+''];
    
                        if(!delta[''+targetDim[k]+'']){ delta[''+targetDim[k]+''] = 1 };
                    }
    
                    new animateScroll(targetInstance.position.scroller,targetInstance.position.thumb,targetInstance.options.easing,
                            targetInstance.Min,targetInstance.Max,targetInstance.ratio,targetInstance.scroller.el.style,
                            targetInstance.thumb.horizontal.el.style,
                            targetInstance.thumb.vertical.el.style,change,delta,true);
    
                }
    
    
            }
            this.MouseUp = function(e)
            {
                endTime = e.timeStamp;
                for(var k = 0; k < dimLength; ++k)
                {
                    inverseDim = filterArray(targetDim,targetDim[k])[0];
                    if(targetInstance.thumb)
                    {
                        inverseDimEl = targetInstance.thumb[''+inverseDim+''] ? targetInstance.thumb[''+inverseDim+''].el : null;
                    }
                    else
                    {
                        inverseDimEl = null;
                    }
                    end[''+targetDim[k]+''] = e['page'+style[''+targetDim[k]+'XY']+''];
                    change[''+targetDim[k]+''] = Math.abs(end[''+targetDim[k]+'']-unique[''+targetDim[k]+'']);
                    speed = change[''+targetDim[k]+'']/(endTime-startTime);
    
                    if(speed < 0.3){ speed = 0 }
    
                    change[''+targetDim[k]+''] = inverseDimEl == firstTarget ? 0 : change[''+targetDim[k]+'']*speed;
                }
    
                new animateScroll(targetInstance.position.scroller,targetInstance.position.thumb,targetInstance.options.easing,
                        targetInstance.Min,targetInstance.Max,targetInstance.ratio,targetInstance.scroller.el.style,
                        targetInstance.thumb.horizontal.el.style,
                        targetInstance.thumb.vertical.el.style,change,delta);
    
                mousedown = false;
    
                if(dimLength > 0){ targetInstance.viewport.el.removeAttribute('dragging') }
            }
        }
                function scrollBar(scroller,options) {
        var self = this;
        /** Define default options **/
        self.options =
        {
            mouseWheel:true,
            mouseWheelSpeed:200,
            duration:0.35,
            trackSize:{vertical:'96%',horizontal:'96%'},
            thumbSize:{vertical:null,horizontal:null},
            visibleScrollbars:true,
            Min:{ vertical:null,horizontal:null},
            Max:{ vertical:null,horizontal:null},
            enable:{horizontal:true,vertical:true},
            /** in easing c = changeInValue,t = time(percent of animation complete),s = start value**/
            easing:
            {
                easeOutSine: function(c,t,s){ return c*Math.sin(t * (Math.PI / 2)) + s },
                easeOutQuad: function(c,t,s){ return c*((t=t-1)*t*t + 1) + s },
                easeOutQuart: function(c,t,s){ return c*((t=t-1)*t*t*t + 1) + s },
                easeOutQuint: function(c,t,s){ return c*((t=t-1)*t*t*t*t + 1) + s },
                easeOutCirc: function(c,t,s){ return c*Math.sqrt(1 - (t=t-1)*t) + s }
            }
        };
        /** Extend options **/
        for ( var j in options ) {
            self.options[j] = options[j];
        }
        /** Initialize new Instance **/
        this.init = function()
        {
            iniContainer();
            _scrollBarInstances.push(self);
        }
        /** Define properties depending on the container **/
        function iniContainer()
        {
            /** scroller/viewport el's **/
            self.scroller = {};
            self.scroller.el = typeof scroller === 'string' ? document.querySelector(scroller) : scroller;
            self.viewport = {};
            self.viewport.el = self.scroller.el.parentNode;
            self.viewport.el.setAttribute('gd_scroll','true');
            /** property controller **/
            self.animatedProperty = {};
            /** position controller **/
            self.position = { scroller: {} };
            /** Min/Max object **/
            self.Min = {};
            self.Max = {};
            /** update contaienr options, create/update scrollbars**/
            self.update();
            //console.log(self);
        }
        /** initialize Scrollbar when needed **/
        function iniScrollbar()
        {
            if(self.dimension.horizontal || self.dimension.vertical)
            {
                self.track = self.track ? self.track : {};
                self.thumb = self.thumb ? self.thumb : {};
                self.ratio = self.ratio ? self.ratio : {};
                self.position.thumb = self.position.thumb ? self.position.thumb : {};
                for ( var k in self.dimension )
                {
                    if(self.dimension[''+k+''] !== false)
                    {
                        _gd_dim = self.dimension[''+k+''];
                        self.track[''+_gd_dim+''] = self.track[''+_gd_dim+''] ? self.track[''+_gd_dim+''] : {};
                        self.thumb[''+_gd_dim+''] = self.thumb[''+_gd_dim+''] ? self.thumb[''+_gd_dim+''] : {};
    
                        self.track[''+_gd_dim+''].el = self.track[''+_gd_dim+''].el ? self.track[''+_gd_dim+''].el : document.createElement('div');
                        self.thumb[''+_gd_dim+''].el = self.thumb[''+_gd_dim+''].el ? self.thumb[''+_gd_dim+''].el : document.createElement('div');
    
                        if(self.track[''+_gd_dim+''].el.parentNode !== self.viewport)
                        {
                            self.track[''+_gd_dim+''].el.appendChild(self.thumb[''+_gd_dim+''].el);
                            self.viewport.el.appendChild(self.track[''+_gd_dim+''].el);
                            self.track[''+_gd_dim+''].el.setAttribute('class','track_'+_gd_dim+'');
                            self.thumb[''+_gd_dim+''].el.setAttribute('class','thumb_'+_gd_dim+'');
                        }
                        self.track[''+_gd_dim+''].styleSize = self.options.trackSize[''+_gd_dim+''];
    
                        self.thumb[''+_gd_dim+''].styleSize =
                                self.options.thumbSize[''+_gd_dim+''] ? self.options.thumbSize[''+_gd_dim+''] : self.viewport[''+style[''+_gd_dim+'Low']+'']/self.scroller[''+style[''+_gd_dim+'Low']+'']*100+'%';
    
                        self.track[''+_gd_dim+''].el.style[''+style[''+_gd_dim+'Low']+''] = self.track[''+_gd_dim+''].styleSize;
                        self.thumb[''+_gd_dim+''].el.style[''+style[''+_gd_dim+'Low']+''] = self.thumb[''+_gd_dim+''].styleSize;
    
                        self.track[''+_gd_dim+''].numSize = self.track[''+_gd_dim+''].el['offset'+style[''+_gd_dim+'Cap']+''];
                        self.thumb[''+_gd_dim+''].numSize = self.thumb[''+_gd_dim+''].el['offset'+style[''+_gd_dim+'Cap']+''];
    
                        self.ratio[''+_gd_dim+''] = self.track[''+_gd_dim+''].numSize/self.scroller[''+style[''+_gd_dim+'Low']+''];
    
                        self.position.thumb[''+_gd_dim+''] = Math.abs(self.position.scroller[''+_gd_dim+'']*self.ratio[''+_gd_dim+'']);
    
                        self.thumb[''+_gd_dim+''].el.style[''+self.animatedProperty[''+_gd_dim+'']+''] = 'translate3d(0,'+self.position.thumb[''+_gd_dim+'']+'px,0)';
                    }
                }
            }
        }
        self.update = function()
        {
            /** get scroller specs **/
            self.scroller.height = self.scroller.el.offsetHeight;
            self.scroller.width = self.scroller.el.offsetWidth;
            /** get viewport specs **/
            self.viewport.height = self.viewport.el.offsetHeight;
            self.viewport.width = self.viewport.el.offsetWidth;
            /** determine scrollbar dimension **/
            self.dimension = {};
            self.dimension.horizontal = self.scroller['width'] > self.viewport['width'] ? 'horizontal' : false;
            self.dimension.vertical = self.scroller['height'] > self.viewport['height'] ? 'vertical' : false;
            /** Determine Min/Max values(can be custom for whatever reason),Animated Property,Start Position for specific dimension */
            for ( var k in self.dimension )
            {
                if(self.dimension[''+k+''] !== false)
                {
                    _gd_dim = self.dimension[''+k+''];
                    self.Min[''+_gd_dim+''] = self.options.Min[''+_gd_dim+''] ? self.options.Min[''+_gd_dim+''] : 0;
                    self.Max[''+_gd_dim+''] = self.options.Max[''+_gd_dim+''] ? self.options.Max[''+_gd_dim+''] : self.viewport[''+style[''+_gd_dim+'Low']+''] - self.scroller[''+style[''+_gd_dim+'Low']+''];
                    self.position.scroller[''+_gd_dim+''] = self.position.scroller[''+_gd_dim+''] ? self.position.scroller[''+_gd_dim+''] : 0;
                }
            }
            if(self.options.visibleScrollbars)
            {
                iniScrollbar();
            }
        }
        self.init();
    }
        function animateScroll(posScroller,posThumb,easing,Min,Max,ratio,scrollerStyle,thumbXStyle,thumbYStyle,change,delta,drag)
        {
            var posThumbX = posThumb.horizontal,
                    posThumbY = posThumb.vertical,
                    deltaX = delta.horizontal,
                    deltaY = delta.vertical,
                    changeX = change.horizontal,
                    changeY = change.vertical,
                    MinX = Min.horizontal,
                    MaxX = Max.horizontal,
                    MinY = Min.vertical,
                    MaxY = Max.vertical,
                    ratioX = ratio.horizontal,
                    ratioY = ratio.vertical,
                    currentPositionX = posScroller.horizontal,
                    currentPositionY = posScroller.vertical,
                    stepSizeX = 0,
                    stepSizeY = 0,
                    currentStep = 0,
                    maxStep = drag ? 1 : 60*0.35,
                    t = 0;
    
            if(_gd_activeAnimation){ stopFrame(_gd_activeAnimation); }
    
            function _step()
            {
                t = drag ? 1 : currentStep/maxStep;
    
                stepSizeX = easing['easeOutSine'](deltaX*changeX,t,currentPositionX);
    
                stepSizeX >= MinX ? stepSizeX = MinX : '';
    
                stepSizeX <= MaxX ? stepSizeX = MaxX : '';
    
                stepSizeY = easing['easeOutSine'](deltaY*changeY,t,currentPositionY);
    
                stepSizeY >= MinY ? stepSizeY = MinY : '';
    
                stepSizeY <= MaxY ? stepSizeY = MaxY : '';
    
                posScroller.horizontal = stepSizeX;
                posScroller.vertical = stepSizeY;
    
                if(posThumb)
                {
                    posThumbX = -1*stepSizeX*ratioX;
    
                    posThumbY = -1*stepSizeY*ratioY;
    
                    thumbXStyle[''+animatedProperty+''] = 'translateX('+posThumbX+'px)';
                    thumbYStyle[''+animatedProperty+''] = 'translateY('+posThumbY+'px)';
                }
    
                scrollerStyle[''+animatedProperty+''] = 'translate('+stepSizeX+'px,'+stepSizeY+'px)';
    
                currentStep++;
    
                if(currentStep <= maxStep){ _gd_activeAnimation = frame(_step) }
    
            }
            _step();
        }
    
    
    
    
    
    
    
    
        function mouseEvent(type, sx, sy, cx, cy) {
            var evt;
            var e = {
                bubbles: true,
                cancelable: (type != "mousemove"),
                view: window,
                detail: 0,
                screenX: sx,
                screenY: sy,
                clientX: cx,
                clientY: cy,
                ctrlKey: false,
                altKey: false,
                shiftKey: false,
                metaKey: false,
                button: 0,
                relatedTarget: undefined
            };
            if (typeof( document.createEvent ) == "function") {
                evt = document.createEvent("MouseEvents");
                evt.initMouseEvent(type,
                        e.bubbles, e.cancelable, e.view, e.detail,
                        e.screenX, e.screenY, e.clientX, e.clientY,
                        e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
                        e.button, document.body.parentNode);
            } else if (document.createEventObject) {
                evt = document.createEventObject();
                for (prop in e) {
                    evt[prop] = e[prop];
                }
                evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button;
            }
            return evt;
        }
        function dispatchEvent (el, evt) {
            if (el.dispatchEvent) {
                el.dispatchEvent(evt);
            } else if (el.fireEvent) {
                el.fireEvent('on' + type, evt);
            }
            return evt;
        }
    
        var scroll = new scrollBar('.scroller',{
            mouseWheelSpeed:300,
            mouseWheel:true,
            visibleScrollbars:true
        });
    
        var drags = new mouseDragEvents();

Test runner

Ready to run.

Testing in
TestOps/sec
in
drags.MouseDown(mouseEvent('mousedown', 0, 0, 0, 0));
ready
inn
drags.MouseDownFree(mouseEvent('mousedown', 0, 0, 0, 0));
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by gurami on