diff --git a/.gitignore b/.gitignore index 58bcbf8..5c183b2 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ Icon Network Trash Folder Temporary Items .apdisk +/jQuery.fakeScroll.js diff --git a/README.md b/README.md index f52042f..d779ce1 100644 --- a/README.md +++ b/README.md @@ -3,40 +3,40 @@ fakeScroll ### [DEMO PAGE](http://yaireo.github.io/fakescroll) -Uber lightweight & robust custom scrollbar replacement jQuery plugin. -The internet deserves a performant custom scrollbar script that is flexible, easy to use and only weights ~5.4k. +Very lightweight & robust custom-looking scrollbar script. -## Example markup for typical use-case +## Example markup: -
+
... ... ...
-## The above will become this (once plugin is applied): +## Initializing: -
-
-
+ document.querySelector('.foo').fakeScroll(); + +## The above will transform into this: + +
+
+
... ... ...
-
+
-## Initializing - $('.fakeScroll').fakeScroll(); ## Settings -Name | Type | Default | Info -------------------- | ---------- | ----------- | -------------------------------------------------------------------------- -theme | String | undefined | Class name which is added to the ".fakeScrollBar" element -offset | String | 0 0 | scroll offset, from start and end -sensitivity | Number | 1 | scroll sensitivity multiplier -minBarSizer | Number | 50 | Minimum size for the fake scroll bar, in pixels +Name | Type | Default | Info +------------------- | ---------- | ----------- | -------------------------------------------------------------------------- +classname | String | "" | Class name which is added to the ".fakeScrollBar" element +offset | String | 0 0 0 0 | scroll offset +track | Boolean/String | false | enable track events. use "smooth" for smooth "jumping" \ No newline at end of file diff --git a/bower.json b/bower.json deleted file mode 100644 index 119b9f6..0000000 --- a/bower.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "fakescroll", - "version": "1.5.0", - "description": "super lightweight & robust scrollbar replacement jQuery plugin. The internet deserves a performant custom scrollbar script that is flexible, easy to use and only weights 4k. -", - "main": [ - "jQuery.fakeScroll.js", - "fakeScroll.css" - ], - "keywords": [ - "scroll", - "javascript", - "custom" - ], - "authors": [ - "Yair Even-Or " - ], - "license": "MIT" -} \ No newline at end of file diff --git a/fakeScroll-slideout.css b/fakeScroll-slideout.css deleted file mode 100644 index 380ed85..0000000 --- a/fakeScroll-slideout.css +++ /dev/null @@ -1,50 +0,0 @@ -.scrollWrap{ - overflow : hidden; - height : 100%; - position : relative; - z-index : 1; -} -.scrollContent{ - height : 100%; - width : 100%; - padding : 0 32px 0 0; - position : relative; - right : -18px; - overflow : auto; - -moz-box-sizing : border-box; - box-sizing : border-box; -} - -.fakeScrollBar{ - position : absolute; - background : rgba(255,255,255,.5); - width : 9px; - border-radius : 4px; - right : -5px; - top : 0; - z-index : 0; - cursor : -moz-grab; - cursor : -webkit-grab; - transition : .25s .3s cubic-bezier(0,.64,.38,1); - transition-property : transform, background, right, width, opacity; -} - -.fakeScroll:hover .fakeScrollBar{ - background : rgba(255,255,255,.7); - opacity : 1; - right : -15px; - transition-delay : 0s; -} - -.fakeScroll:hover .fakeScrollBar:hover{ - background : rgba(255,255,255,.85); -} - -.fakeScrollBar.fakescroll-grabbed, -.fakeScroll:hover .fakeScrollBar:active{ - cursor : -moz-grabbed; - cursor : -webkit-grabbed; - right : -15px !important; - opacity : 1; - background : #FFF; -} \ No newline at end of file diff --git a/fakeScroll.css b/fakeScroll.css index 947ae62..9950d12 100644 --- a/fakeScroll.css +++ b/fakeScroll.css @@ -1,10 +1,11 @@ -.scrollWrap{ +.fakeScroll__wrap{ overflow : hidden; height : 100%; position : relative; z-index : 1; } -.scrollContent{ + +.fakeScroll__content{ height : 100%; width : 100%; padding : 0 32px 0 0; @@ -15,25 +16,42 @@ box-sizing : border-box; } -.fakeScrollBar{ - position : absolute; - background : rgba(255,255,255,.5); - width : 9px; - border-radius : 4px; - right : -15px; - top : 0; - z-index : 0; - cursor : -moz-grab; - cursor : -webkit-grab; +.fakeScroll__track{ + position : absolute; + right : -15px; + top : 0; + bottom : 0; + width : 9px; + cursor : default; } -.fakeScroll:hover .fakeScrollBar.fakescroll-grabbed{ - cursor : -moz-grabbing; - cursor : -webkit-grabbing; - background : #FFF; +.fakeScroll__bar{ + position : relative; + background : rgba(255,255,255, .4); + width : 100%; + border-radius : 4px; + right : 0; + top : 0; + z-index : 0; + transition : background 0.1s; + cursor : -moz-grab; + cursor : -webkit-grab; } -body.fakescroll-grabbed{ +.fakeScroll__bar:hover{ + background : rgba(255,255,255, .55); +} + +.fakeScroll__bar.fakeScroll--grabbed{ cursor : -moz-grabbing; cursor : -webkit-grabbing; + background : white; +} + +body.fakeScroll--grabbed{ + cursor : -moz-grabbing; + cursor : -webkit-grabbing; + -moz-user-select : none; + -webkit-user-select : none; + user-select : none; } \ No newline at end of file diff --git a/fakescroll.js b/fakescroll.js new file mode 100644 index 0000000..ecb6d60 --- /dev/null +++ b/fakescroll.js @@ -0,0 +1,174 @@ +;(function(){ + raf = window.requestAnimationFrame || function(cb) { return window.setTimeout(cb, 1000 / 60) }; + + function FakeScroll(targetElm, settings){ + if( !targetElm ) return; + + this.settings = Object.assign({}, this.defaults, settings || {}); + this.settings.offset = this.settings.offset.split(' '); // convert offset String to Array + this.callback = settings.callback ? settings.callback : null; + + this.state = {}; + this.listeners = {}; + + this.DOM = this.build(targetElm); + this.events.binding.call(this, this.DOM); + + // run "moveBar" once + setTimeout(this.moveBar.bind(this)); + + } + + FakeScroll.prototype = { + defaults : { + classname : "", + track : false, + offset : "0 0 0 0" + }, + + /** + * Build the DOM needed + */ + build( targetElm ){ + var DOM = {}; + scopeHTML = `
+
+
+
+
+
`, + fragment = document.createDocumentFragment(); + + // move all the children of the target element into a fragment + while( targetElm.childNodes.length ){ + fragment.appendChild(targetElm.childNodes[0]); + } + + targetElm.insertAdjacentHTML('afterbegin', scopeHTML); + + DOM.scrollWrap = targetElm.firstElementChild; + DOM.scrollContent = DOM.scrollWrap.firstElementChild; + DOM.scrollContent.appendChild(fragment); + + DOM.track = DOM.scrollWrap.nextElementSibling; + DOM.bar = DOM.track.firstElementChild; + + return DOM; + }, + + events : { + on(elm, eName, cbName){ + // to be able tp unbind the events, callback refferece must be saved somewhere + eName.split(' ').forEach(e => { + if( !(cbName in this.events.callbacks) ) console.warn(cbName, " doesn't exist in Callbacks: ", this.events.callbacks); + + this.listeners[e] = this.events.callbacks[cbName].bind(this); + elm.addEventListener(e, this.listeners[e]) + }); + + return this.events; + }, + + off(elm, eName, cbName){ + eName.split(' ').forEach(e => elm.removeEventListener(e, this.listeners[e])) + return this.events; + }, + + binding(DOM){ + this.events.on.call(this, DOM.scrollContent, 'scroll mouseenter', 'onScrollResize') + .on.call(this, window, 'resize', 'onScrollResize') + .on.call(this, DOM.bar, 'mousedown', 'onBarMouseDown') + + if( this.settings.track ) + this.events.on.call(this, DOM.track, 'click', 'onTrackClick') + }, + + /** + * events only binded when Bar element gets a "mousedown" event + * @param {[type]} onOff [description] + * @return {[type]} [description] + */ + drag(onOff){ + this.events[onOff].call(this, document, 'mousemove', 'onDrag') + [onOff].call(this, document, 'mouseup', 'onStopDrag') + }, + + callbacks : { + onScrollResize(e){ + this.moveBar.call(this) + }, + + onDrag(e){ + var delta = e.pageY - this.state.lastPageY; + this.state.lastPageY = e.pageY; + + raf(() => { + this.DOM.scrollContent.scrollTop += delta / this.scrollRatio; + }); + }, + + onStopDrag(e){ + [this.DOM.bar, document.body].map(el => el.classList.remove('fakeScroll--grabbed')) + this.events.drag.call(this, 'off'); + setTimeout(()=>{ this.state.drag = false }) + }, + + onBarMouseDown(e){ + this.state.drag = true; + this.state.lastPageY = e.pageY; + [this.DOM.bar, document.body].map(el => el.classList.add('fakeScroll--grabbed')) + this.events.drag.call(this, 'on'); + }, + + onTrackClick(e){ + if( this.state.drag ) return; + + var bounds = e.target.getBoundingClientRect(), + styles = window.getComputedStyle(e.target, null), + boundsPad = [parseInt(styles.paddingTop, 10), 0, parseInt(styles.paddingBottom, 10), 0], + perc = (e.clientY - bounds.top) / (bounds.height - boundsPad[0] - boundsPad[2]), + scrollHeight = this.DOM.scrollContent.scrollHeight, + ownHeight = this.DOM.scrollWrap.clientHeight, + newScrollTop = perc * (scrollHeight - ownHeight); + + if( this.settings.track == 'smooth' ){ + this.DOM.scrollContent.style.scrollBehavior = 'smooth'; + setTimeout(()=>{ this.DOM.scrollContent.style.scrollBehavior = 'unset' }, 500) + } + + this.DOM.scrollContent.scrollTop = newScrollTop; + } + } + }, + + destroy(){ + this.events.off.call(this, window, 'resize', 'onScrollResize'); + }, + + moveBar(){ + // if( !this.DOM.scrollContent ) return false; + + var _scrollContent = this.DOM.scrollContent, + scrollHeight = _scrollContent.scrollHeight, + ownHeight = this.DOM.scrollWrap.clientHeight; + + this.scrollRatio = ownHeight / scrollHeight; + + // update fake scrollbar location on the Y axis using requestAnimationFrame + raf(()=> { + var height = (ownHeight / scrollHeight) * 100, + top = (_scrollContent.scrollTop / scrollHeight ) * 100; + + this.DOM.bar.style.cssText = `height : calc(${height}% - ${this.settings.offset[0]}px); + top : calc(${top}% + ${this.settings.offset[0]}px); + display : ${scrollHeight <= ownHeight ? 'none' : ''}`; + }); + } + } + + Element.prototype.fakeScroll = function( settings ){ + this._fakeScroll = this._fakeScroll || new FakeScroll(this, settings || {}); + + return this._fakeScroll; + } +})(); \ No newline at end of file diff --git a/index.html b/index.html index c7b2fcf..3fc2150 100644 --- a/index.html +++ b/index.html @@ -26,32 +26,45 @@ background-image: radial-gradient( top, circle cover, #3c3b52 0%, #252233 100%); } - .fakeScroll{ position:absolute; top:0; right:0; bottom:0; left:0; margin:auto; min-height:70%; height:1px; width:500px; } - .fakeScroll .scrollContent{ padding-top:1em; padding-bottom:1em; } - .fakeScroll .scrollWrap{ background:#FFF; border-radius:4px; -moz-box-sizing:border-box; box-sizing:border-box; } + .foo{ position:absolute; top:0; right:0; bottom:0; left:0; margin:auto; min-height:70%; height:1px; width:500px; } + .foo .fakeScroll__content{ padding-top:1em; padding-bottom:1em; } + .foo .fakeScroll__wrap{ background:#FFF; border-radius:4px; -moz-box-sizing:border-box; box-sizing:border-box; } /* upper and bottom fades */ - .scrollWrap::before{ content:''; position:absolute; z-index:1; top:0; left:0; right:0; height:5%; + .fakeScroll__wrap::before{ content:''; position:absolute; z-index:1; top:0; left:0; right:0; height:5%; background: -webkit-linear-gradient(#FFF 0%, rgba(255,255,255,0) 100%); background: -o-linear-gradient(#FFF 0%, rgba(255,255,255,0) 100%); background: linear-gradient(#FFF 0%, rgba(255,255,255,0) 100%); } - .scrollWrap::after{ content:''; position:absolute; z-index:1; bottom:0; left:0; right:0; height:5%; + .fakeScroll__wrap::after{ content:''; position:absolute; z-index:1; bottom:0; left:0; right:0; height:5%; background: -webkit-linear-gradient(rgba(255,255,255,0) 0%, #FFF 100%); background: -o-linear-gradient(rgba(255,255,255,0) 0%, #FFF 100%); background: linear-gradient(rgba(255,255,255,0) 0%, #FFF 100%); } - .fakeScroll--inside{ min-height:40%; } + /* .fakeScroll--inside .fakeScroll__content::after{ content:''; display:block; min-height:10000px; } */ - .fakeScroll--inside .fakeScrollBar{ - background: #252233; - right: 3px; + + .foo--inside .fakeScroll__track{ + background: rgba(0,0,0,.2); + right: 0; + top: 10%; + bottom: 10%; z-index: 1; + padding: 10px 3px; + border-radius: 20px 0 0 20px; + transition: .1s; + } + + .foo--inside .fakeScroll__track:hover{ + background: rgba(0,0,0,.1); + width: 12px; } - .fakeScroll--inside:hover .fakeScrollBar.fakescroll-grabbed{ + .foo--inside .fakeScroll__bar{ background: #252233; } + + .foo--inside:hover .fakeScroll__bar.fakeScroll--grabbed{ background: #3c3b52; } @@ -60,7 +73,9 @@ GitHub Repo ยป
-
+
+

This text can be edited

+

In this example the scrollbar is outside of the scrollable area and the "track" is not enabled

Uber lightweight & robust scrollbar replacement jQuery plugin. The internet deserves a performant custom scrollbar script that is flexible, easy to use and only weights 4k.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.

Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.

@@ -88,17 +103,14 @@
-
+
+

This text can be edited

+

In this example the scrollbar is within the scrollable area and has an offset from the top and bottom (via CSS). The scrollbar also has the "track" setting enabled

Uber lightweight & robust scrollbar replacement jQuery plugin. The internet deserves a performant custom scrollbar script that is flexible, easy to use and only weights 4k.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.

Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.

Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium.

Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim.

-

Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue.

-

Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.

-

Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante.

-

Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna.

-

Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus.

Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu.

Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.

@@ -115,17 +127,17 @@
- - + + diff --git a/jQuery.fakeScroll.js b/jQuery.fakeScroll.js deleted file mode 100644 index ba528db..0000000 --- a/jQuery.fakeScroll.js +++ /dev/null @@ -1,133 +0,0 @@ -///////////////////////////////////////////////////////// -// fakeScroll - a custom scroll bar jQuery plugin -// By Yair Even-Or - https://github.com/yairEO/fakescroll - -;(function($, win){ - "use strict"; - - var docElm = document.documentElement, - $doc = $(document), - raf = win.requestAnimationFrame || function(cb) { return win.setTimeout(cb, 1000 / 60) }, - defaults = { - offset : "0 0", - sensitivity : 1, - minBarSizer : 50 - }; - - jQuery.fn.fakeScroll = function(settings){ - return this.each(function(idx, selector){ - var $el = $(this), // convert window to the HTML element - fakeScroll = $el.data('_fakeScroll'); - - // if element already the pluging bound to it, return - if( fakeScroll ){ - typeof settings == 'string' && fakeScroll[settings] && fakeScroll[settings](); - return; - } - - // create a new FakeScroll instance - fakeScroll = new FakeScroll($el, settings || {}); - // bind the FakeScroll instance to the DOM component - $el.data('_fakeScroll', fakeScroll); - }); - } - - // Constructor - function FakeScroll($el, settings){ - // this.id = new Array(8).join((Math.random().toString(36)+'00000000000000000').slice(2, 18)).slice(0, 7); // generate an UID for each instance - this.target = $el; - this.settings = $.extend({}, defaults, settings); - this.bar = $('
').addClass(settings.theme); - this.settings.offset = this.settings.offset.split(' '); // convert offset String to Array - - this.callback = settings.callback ? settings.callback : null; - this.maxScrollSoFar = 0; - - // wrap with needed DOM structure - this.el = this.target.wrapInner('
').find('.scrollContent'); - - // insert the fake scroll bar into the container - this.bar.appendTo(this.el.closest(this.target)); - // initiate drag controller on the instance - this.dragDealer(); - // run "moveBar" once - setTimeout(this.moveBar.bind(this), 0); - - this.el.on('scroll.fs_scroll mouseenter.fs_mouseenter', this.moveBar.bind(this) ); - $(win).on('resize.fs_resize.' + this.id, this.moveBar.bind(this) ); - } - - FakeScroll.prototype = { - destroy : function(){ - $el.off('scroll.fs_scroll mousedown.fs_drag').removeData('_fakeScroll'); - }, - - // Mouse drag handler - dragDealer : function($el){ - var lastPageY, - $el = this.bar, - that = this; - - $el.on('mousedown.fs_drag', function(e) { - lastPageY = e.pageY; - $el.add(document.body).addClass('fakescroll-grabbed'); - $doc.on('mousemove.fs_drag', drag).on('mouseup.fs_drag', stop); - return false; - }); - - function drag(e){ - var delta = e.pageY - lastPageY; - lastPageY = e.pageY; - - // raf(function(){ - that.el[0].scrollTop += delta * that.settings.sensitivity / that.scrollRatio; - // }); - } - - function stop() { - $el.add(document.body).removeClass('fakescroll-grabbed'); - $doc.off("mousemove.fs_drag mouseup.fs_drag"); - } - }, - - moveBar: function(e){ - if( !this.el || !this.el[0] ) return false; - - var totalHeight = this.el[0].scrollHeight, // the bigger value - ownHeight = this.el[0].clientHeight, // the inner value - that = this; - - - this.scrollRatio = ownHeight / totalHeight; - - // update fake scrollbar location on the Y axis using requestAnimationFrame - raf(function(){ - var ratio = (ownHeight / totalHeight) * 100, - scroll_percent = (that.el[0].scrollTop / (totalHeight - ownHeight)) * 100, - scrollBarSizeInPixels = ratio/100 * ownHeight, - offset, - height; - - if( scrollBarSizeInPixels < that.settings.minBarSizer ){ - height = that.settings.minBarSizer + "px"; - scrollBarSizeInPixels = that.settings.minBarSizer; - } - else - height = ratio + "% - " + 0 + "px"; - - // adjust the offset according to the scroll percentage, so at "0%" the offset will be the maximum - // start-offset (in "px"), at "50%" the offset should be "0" - offset = scroll_percent < 50 ? - that.settings.offset[0] - (that.settings.offset[0] * scroll_percent/50) : - that.settings.offset[1] * (1 - scroll_percent/50); - - that.bar[0].style.cssText = "height : calc("+ height + "); \ - top : calc("+ scroll_percent + "% + " + offset + "px); \ - transform: translateY(-"+ scroll_percent +"%);"; - - }); - - this.bar.toggleClass('enabled', totalHeight > ownHeight); - } - } -})(jQuery, window); \ No newline at end of file diff --git a/package.json b/package.json index 3ed3090..3caefa3 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,26 @@ { - "name" : "@yaireo/fakeScroll", - "version" : "1.5.0", - "homepage" : "https://github.com/yairEO/fakescroll", - "description" : "Want to simply convert an input field into a tags element, in a easy customizable way, with good performance and smallest code footprint? You are in the right place my friend.", - "_npmUser" : { - "name" : "vsync", - "email" : "vsync.design@gmail.com" + "name": "@yaireo/fakeScroll", + "version": "2.0.0", + "homepage": "https://github.com/yairEO/fakescroll", + "description": "Want to simply convert an input field into a tags element, in a easy customizable way, with good performance and smallest code footprint? You are in the right place my friend.", + "_npmUser": { + "name": "vsync", + "email": "vsync.design@gmail.com" }, "author": { - "name" : "Yair Even-Or", - "email" : "vsync.design@gmail.com" + "name": "Yair Even-Or", + "email": "vsync.design@gmail.com" }, - "main" : "jQuery.fakeScroll.js", - "repository" : { - "type" : "git", - "url" : "git+https://github.com/yairEO/fakescroll.git" + "main": "fakeScroll.js", + "repository": { + "type": "git", + "url": "git+https://github.com/yairEO/fakescroll.git" }, "bugs": { "url": "https://github.com/yaireo/fakescroll/issues" }, - "devDependencies": {} + "devDependencies": { + }, + "dependencies": { + } }