Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

scrollbooster

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

scrollbooster - npm Package Compare versions

Comparing version 1.1.0 to 2.0.0

dist/scrollbooster.min.js.map

3

dist/scrollbooster.min.js

@@ -1,1 +0,2 @@

!function(t,i){"object"==typeof exports&&"object"==typeof module?module.exports=i():"function"==typeof define&&define.amd?define("ScrollBooster",[],i):"object"==typeof exports?exports.ScrollBooster=i():t.ScrollBooster=i()}("undefined"!=typeof self?self:this,function(){return function(t){function i(o){if(e[o])return e[o].exports;var n=e[o]={i:o,l:!1,exports:{}};return t[o].call(n.exports,n,n.exports,i),n.l=!0,n.exports}var e={};return i.m=t,i.c=e,i.d=function(t,e,o){i.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:o})},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,i){return Object.prototype.hasOwnProperty.call(t,i)},i.p="/dist/",i(i.s=0)}([function(t,i,e){"use strict";function o(t,i){if(!(t instanceof i))throw new TypeError("Cannot call a class as a function")}function n(t){return Math.max(t.offsetWidth,t.scrollWidth)}function s(t){return Math.max(t.offsetHeight,t.scrollHeight)}function r(t,i,e){for(var o=void 0,n=t.childNodes,s=document.createRange(),r=0;o=n[r],r<n.length;r++)if(3===o.nodeType){s.selectNodeContents(o);var h=s.getBoundingClientRect();if(i>=h.left&&e>=h.top&&i<=h.right&&e<=h.bottom)return o}return!1}function h(){var t=window.getSelection?window.getSelection():document.selection;t&&(t.removeAllRanges?t.removeAllRanges():t.empty&&t.empty())}Object.defineProperty(i,"__esModule",{value:!0});var c=Object.assign||function(t){for(var i=1;i<arguments.length;i++){var e=arguments[i];for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o])}return t},l=function(){function t(t,i){for(var e=0;e<i.length;e++){var o=i[e];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(i,e,o){return e&&t(i.prototype,e),o&&t(i,o),i}}(),p=function(){function t(){var i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(o(this,t),!(i.viewport&&i.viewport instanceof Element))return void console.error('"viewport" config property must be present and must be Element');var e={handle:i.viewport,content:i.viewport.children[0],bounce:!0,friction:.05,bounceForce:.1,textSelection:!1,onClick:function(){},shouldScroll:function(){return!0},onUpdate:function(){}};if(this.props=c({},e,i),!this.props.content)return void console.error("Viewport does not have any content");this.viewport={width:this.props.viewport.clientWidth,height:this.props.viewport.clientHeight},this.content={width:n(this.props.content),height:s(this.props.content)},this.position={x:0,y:0},this.velocity={x:0,y:0},this.friction=1-this.props.friction,this.bounceForce=this.props.bounceForce,this.isDragging=!1,this.dragStartPosition={x:0,y:0},this.dragOffsetPosition=c({},this.dragStartPosition),this.dragPosition=c({},this.position),this.isScrollEnabled=!!this.props.emulateScroll,this.isScrolling=!1,this.scrollOffset={x:0,y:0},this.bounce=this.props.bounce,this.textSelection=this.props.textSelection,this.boundX={from:Math.min(-this.content.width+this.viewport.width,0),to:0},this.boundY={from:Math.min(-this.content.height+this.viewport.height,0),to:0},this.mode={x:"x"==this.props.mode,y:"y"==this.props.mode,xy:"x"!==this.props.mode&&"y"!==this.props.mode},this.isRunning=!1,this.rafID=null,this.events={},this.animate(),this.handleEvents()}return l(t,[{key:"run",value:function(){var t=this;this.isRunning=!0,cancelAnimationFrame(this.rafID),this.rafID=requestAnimationFrame(function(){return t.animate()})}},{key:"animate",value:function(){var t=this;this.isRunning&&(this.update(),this.notify(),this.rafID=requestAnimationFrame(function(){return t.animate()}))}},{key:"update",value:function(){this.applyBoundForce(),this.applyDragForce(),this.applyScrollForce(),this.velocity.x*=this.friction,this.velocity.y*=this.friction,this.mode.y||(this.position.x+=this.velocity.x),this.mode.x||(this.position.y+=this.velocity.y),this.bounce&&!this.isScrolling||(this.position.x=Math.max(Math.min(this.position.x,this.boundX.to),this.boundX.from),this.position.y=Math.max(Math.min(this.position.y,this.boundY.to),this.boundY.from)),!this.isDragging&&!this.isScrolling&&Math.abs(this.velocity.x)<.1&&Math.abs(this.velocity.y)<.1&&(this.isRunning=!1)}},{key:"applyForce",value:function(t){this.velocity.x+=t.x,this.velocity.y+=t.y}},{key:"applyBoundForce",value:function(){if(this.bounce&&!this.isDragging){var t=this.position.x<this.boundX.from,i=this.position.x>this.boundX.to,e=this.position.y<this.boundY.from,o=this.position.y>this.boundY.to,n={x:0,y:0};if(t||i){var s=t?this.boundX.from:this.boundX.to,r=s-this.position.x,h=r*this.bounceForce,c=this.position.x+(this.velocity.x+h)/(1-this.friction);t&&c<this.boundX.from||i&&c>this.boundX.to||(h=r*this.bounceForce-this.velocity.x),n.x=h}if(e||o){var l=e?this.boundY.from:this.boundY.to,p=l-this.position.y,a=p*this.bounceForce,u=this.position.y+(this.velocity.y+a)/(1-this.friction);e&&u<this.boundY.from||o&&u>this.boundY.to||(a=p*this.bounceForce-this.velocity.y),n.y=a}this.applyForce(n)}}},{key:"applyDragForce",value:function(){if(this.isDragging){var t={x:this.dragPosition.x-this.position.x,y:this.dragPosition.y-this.position.y},i={x:t.x-this.velocity.x,y:t.y-this.velocity.y};this.applyForce(i)}}},{key:"applyScrollForce",value:function(){if(this.isScrolling){var t={x:this.scrollOffset.x-this.velocity.x,y:this.scrollOffset.y-this.velocity.y};this.scrollOffset.x=0,this.scrollOffset.y=0,this.applyForce(t)}}},{key:"setPosition",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.velocity.x=0,this.velocity.y=0,this.position.x=-t.x||0,this.position.y=-t.y||0,this.run()}},{key:"getUpdate",value:function(){return{isRunning:this.isRunning,isDragging:this.isDragging,isScrolling:this.isScrolling,position:{x:-this.position.x,y:-this.position.y},dragOffsetPosition:this.dragOffsetPosition,viewport:c({},this.viewport),content:c({},this.content)}}},{key:"notify",value:function(){this.props.onUpdate(this.getUpdate())}},{key:"updateMetrics",value:function(){this.viewport.width=this.props.viewport.clientWidth,this.viewport.height=this.props.viewport.clientHeight,this.content.width=n(this.props.content),this.content.height=s(this.props.content),this.boundX.from=Math.min(-this.content.width+this.viewport.width,0),this.boundY.from=Math.min(-this.content.height+this.viewport.height,0),this.run()}},{key:"handleEvents",value:function(){var t=this,i=this.props.viewport,e={x:0,y:0},o={x:0,y:0},n=!1,s=function(i){var e=void 0,s=void 0;n?(e=i.touches[0].pageX,s=i.touches[0].pageY):(e=i.pageX,s=i.pageY),t.dragOffsetPosition.x=e-o.x,t.dragOffsetPosition.y=s-o.y,t.dragPosition.x=t.dragStartPosition.x+t.dragOffsetPosition.x,t.dragPosition.y=t.dragStartPosition.y+t.dragOffsetPosition.y,n||i.preventDefault()};this.events.pointerdown=function(c){var l=void 0,p=void 0,a=void 0,u=void 0;n=!(!c.touches||!c.touches[0]),n?(l=c.touches[0].pageX,p=c.touches[0].pageY,a=c.touches[0].clientX,u=c.touches[0].clientY):(l=c.pageX,p=c.pageY,a=c.clientX,u=c.clientY);var d=i.getBoundingClientRect();if(!(a-d.left>=i.clientLeft+i.clientWidth)&&!(u-d.top>=i.clientTop+i.clientHeight)&&t.props.shouldScroll(t.getUpdate(),c)){if(t.textSelection){if(r(c.target,a,u))return;h()}t.isDragging=!0,(e.x||e.y)&&(t.position.x=e.x,t.position.y=e.y,e.x=0,e.y=0),o.x=l,o.y=p,t.dragStartPosition.x=t.position.x,t.dragStartPosition.y=t.position.y,s(c),t.run();var v=void 0,f=void 0;f=function(i){t.isDragging=!1,n?(window.removeEventListener("touchmove",s),window.removeEventListener("touchend",v)):(window.removeEventListener("mousemove",s),window.removeEventListener("mouseup",v))},n?(v=window.addEventListener("touchend",f),window.addEventListener("touchmove",s)):(v=window.addEventListener("mouseup",f),window.addEventListener("mousemove",s))}};var c=null;this.events.wheel=function(i){t.velocity.x=0,t.isScrollEnabled&&(t.isScrolling=!0,t.scrollOffset.x=-i.deltaX,t.scrollOffset.y=-i.deltaY,t.run(),clearTimeout(c),c=setTimeout(function(){return t.isScrolling=!1},80),i.preventDefault())},this.events.scroll=function(i){var o=t.props.viewport.scrollLeft,n=t.props.viewport.scrollTop;Math.abs(t.position.x+o)>3&&(t.position.x=-o,t.velocity.x=0),Math.abs(t.position.y+n)>3&&(t.position.y=-n,t.velocity.y=0),e.x=-t.props.viewport.scrollLeft,e.y=-t.props.viewport.scrollTop},this.events.click=function(i){t.props.onClick(t.getUpdate(),i)},this.events.resize=this.updateMetrics.bind(this),this.props.handle.addEventListener("mousedown",this.events.pointerdown),this.props.handle.addEventListener("touchstart",this.events.pointerdown),this.props.handle.addEventListener("click",this.events.click),this.props.viewport.addEventListener("wheel",this.events.wheel),this.props.viewport.addEventListener("scroll",this.events.scroll),window.addEventListener("resize",this.events.resize)}},{key:"destroy",value:function(){this.props.handle.removeEventListener("mousedown",this.events.pointerdown),this.props.handle.removeEventListener("touchstart",this.events.pointerdown),this.props.handle.removeEventListener("click",this.events.click),this.props.viewport.removeEventListener("wheel",this.events.wheel),this.props.viewport.removeEventListener("scroll",this.events.scroll),window.removeEventListener("resize",this.events.resize)}}]),t}();i.default=p,t.exports=i.default}])});
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("ScrollBooster",[],e):"object"==typeof exports?exports.ScrollBooster=e():t.ScrollBooster=e()}(window,(function(){return function(t){var e={};function i(o){if(e[o])return e[o].exports;var n=e[o]={i:o,l:!1,exports:{}};return t[o].call(n.exports,n,n.exports,i),n.l=!0,n.exports}return i.m=t,i.c=e,i.d=function(t,e,o){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)i.d(o,n,function(e){return t[e]}.bind(null,n));return o},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=0)}([function(t,e,i){"use strict";function o(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);e&&(o=o.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,o)}return i}function n(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?o(Object(i),!0).forEach((function(e){s(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):o(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function s(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function r(t,e){for(var i=0;i<e.length;i++){var o=e[i];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}i.r(e),i.d(e,"default",(function(){return p}));var h=function(t){return Math.max(t.offsetWidth,t.scrollWidth)},c=function(t){return Math.max(t.offsetHeight,t.scrollHeight)},p=function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t);var i={content:e.viewport.children[0],direction:"all",pointerMode:"all",bounce:!0,friction:.05,bounceForce:.1,textSelection:!1,inputsFocus:!0,emulateScroll:!1,onClick:function(){},onUpdate:function(){},shouldScroll:function(){return!0}};this.props=n({},i,{},e),this.props.viewport&&this.props.viewport instanceof Element?this.props.content?(this.viewport={width:this.props.viewport.clientWidth,height:this.props.viewport.clientHeight},this.content={width:h(this.props.content),height:c(this.props.content)},this.position={x:0,y:0},this.velocity={x:0,y:0},this.friction=1-this.props.friction,this.bounceForce=this.props.bounceForce,this.isDragging=!1,this.dragStartPosition={x:0,y:0},this.dragOffset=n({},this.dragStartPosition),this.dragPosition=n({},this.position),this.isTargetScroll=!1,this.targetPosition={x:0,y:0},this.isScrolling=!1,this.scrollOffset={x:0,y:0},this.bounce=this.props.bounce,this.textSelection=this.props.textSelection,this.boundX={from:Math.min(-this.content.width+this.viewport.width,0),to:0},this.boundY={from:Math.min(-this.content.height+this.viewport.height,0),to:0},this.isRunning=!1,this.rafID=null,this.events={},this.animate(),this.handleEvents()):console.error("ScrollBooster init error: Viewport does not have any content"):console.error('ScrollBooster init error: "viewport" config property must be present and must be Element')}var e,i,o;return e=t,(i=[{key:"run",value:function(){var t=this;this.isRunning=!0,cancelAnimationFrame(this.rafID),this.rafID=requestAnimationFrame((function(){return t.animate()}))}},{key:"animate",value:function(){var t=this;this.isRunning&&(this.update(),this.props.onUpdate(this.getState()),this.rafID=requestAnimationFrame((function(){return t.animate()})))}},{key:"isMoving",value:function(){return this.isDragging||this.isScrolling||Math.abs(this.velocity.x)>=.1||Math.abs(this.velocity.y)>=.1}},{key:"update",value:function(){this.applyBoundForce(),this.applyDragForce(),this.applyScrollForce(),this.applyTargetForce(),this.velocity.x*=this.friction,this.velocity.y*=this.friction,"vertical"!==this.props.direction&&(this.position.x+=this.velocity.x),"horizontal"!==this.props.direction&&(this.position.y+=this.velocity.y),this.bounce&&!this.isScrolling||this.isTargetScroll||(this.position.x=Math.max(Math.min(this.position.x,this.boundX.to),this.boundX.from),this.position.y=Math.max(Math.min(this.position.y,this.boundY.to),this.boundY.from)),this.isMoving()||(this.isRunning=!1,this.isTargetScroll=!1)}},{key:"applyForce",value:function(t){this.velocity.x+=t.x,this.velocity.y+=t.y}},{key:"applyBoundForce",value:function(){if(this.bounce&&!this.isDragging){var t=this.position.x<this.boundX.from,e=this.position.x>this.boundX.to,i=this.position.y<this.boundY.from,o=this.position.y>this.boundY.to,n=t||e,s=i||o;if(n||s){var r=t?this.boundX.from:this.boundX.to,h=i?this.boundY.from:this.boundY.to,c=r-this.position.x,p=h-this.position.y,l={x:c*this.bounceForce,y:p*this.bounceForce},a=this.position.x+(this.velocity.x+l.x)/(1-this.friction),u=this.position.y+(this.velocity.y+l.y)/(1-this.friction);(t&&a>=this.boundX.from||e&&a<=this.boundX.to)&&(l.x=c*this.bounceForce-this.velocity.x),(i&&u>=this.boundY.from||o&&u<=this.boundY.to)&&(l.y=p*this.bounceForce-this.velocity.y),this.applyForce({x:n?l.x:0,y:s?l.y:0})}}}},{key:"applyDragForce",value:function(){if(this.isDragging){var t=this.dragPosition.x-this.position.x,e=this.dragPosition.y-this.position.y,i={x:t-this.velocity.x,y:e-this.velocity.y};this.applyForce(i)}}},{key:"applyScrollForce",value:function(){if(this.isScrolling){var t={x:this.scrollOffset.x-this.velocity.x,y:this.scrollOffset.y-this.velocity.y};this.scrollOffset.x=0,this.scrollOffset.y=0,this.applyForce(t)}}},{key:"applyTargetForce",value:function(){if(this.isTargetScroll){var t={x:.08*(this.targetPosition.x-this.position.x)-this.velocity.x,y:.08*(this.targetPosition.y-this.position.y)-this.velocity.y};this.applyForce(t)}}},{key:"scrollTo",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.isTargetScroll=!0,this.targetPosition.x=-t.x||0,this.targetPosition.y=-t.y||0,this.run()}},{key:"setPosition",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.velocity.x=0,this.velocity.y=0,this.position.x=-t.x||0,this.position.y=-t.y||0,this.run()}},{key:"getState",value:function(){return{isMoving:this.isMoving(),isDragging:!(!this.dragOffset.x&&!this.dragOffset.y),position:{x:-this.position.x,y:-this.position.y},dragOffset:this.dragOffset}}},{key:"updateMetrics",value:function(){this.viewport.width=this.props.viewport.clientWidth,this.viewport.height=this.props.viewport.clientHeight,this.content.width=h(this.props.content),this.content.height=c(this.props.content),this.boundX.from=Math.min(-this.content.width+this.viewport.width,0),this.boundY.from=Math.min(-this.content.height+this.viewport.height,0),this.run()}},{key:"handleEvents",value:function(){var t=this,e={x:0,y:0},i=null,o=!1,n=function(i){if(t.isDragging){var n=o?i.touches[0].pageX:i.pageX,s=o?i.touches[0].pageY:i.pageY;t.dragOffset.x=n-e.x,t.dragOffset.y=s-e.y,t.dragPosition.x=t.dragStartPosition.x+t.dragOffset.x,t.dragPosition.y=t.dragStartPosition.y+t.dragOffset.y}};this.events.pointerdown=function(i){var s=(o=!(!i.touches||!i.touches[0]))?i.touches[0]:i,r=s.pageX,h=s.pageY,c=s.clientX,p=s.clientY,l=t.props.viewport,a=l.getBoundingClientRect();if(!(c-a.left>=l.clientLeft+l.clientWidth)&&!(p-a.top>=l.clientTop+l.clientHeight)&&t.props.shouldScroll(t.getState(),i)&&("mouse"!==t.props.pointerMode||!o)&&("touch"!==t.props.pointerMode||o)&&!(t.props.inputsFocus&&["input","textarea","button","select","label"].indexOf(i.target.nodeName.toLowerCase())>-1)){if(t.textSelection){if(function(t,e,i){for(var o=t.childNodes,n=document.createRange(),s=0;s<o.length;s++){var r=o[s];if(3===r.nodeType){n.selectNodeContents(r);var h=n.getBoundingClientRect();if(e>=h.left&&i>=h.top&&e<=h.right&&i<=h.bottom)return r}}return!1}(i.target,c,p))return;(u=window.getSelection?window.getSelection():document.selection)&&(u.removeAllRanges?u.removeAllRanges():u.empty&&u.empty())}var u;t.isDragging=!0,e.x=r,e.y=h,t.dragStartPosition.x=t.position.x,t.dragStartPosition.y=t.position.y,n(i),t.run(),i.preventDefault()}},this.events.pointermove=function(t){n(t)},this.events.pointerup=function(){t.isDragging=!1},this.events.wheel=function(e){t.props.emulateScroll&&(t.velocity.x=0,t.velocity.y=0,t.isScrolling=!0,t.scrollOffset.x=-e.deltaX,t.scrollOffset.y=-e.deltaY,t.run(),clearTimeout(i),i=setTimeout((function(){return t.isScrolling=!1}),80),e.preventDefault())},this.events.scroll=function(){var e=t.props.viewport,i=e.scrollLeft,o=e.scrollTop;Math.abs(t.position.x+i)>3&&(t.position.x=-i,t.velocity.x=0),Math.abs(t.position.y+o)>3&&(t.position.y=-o,t.velocity.y=0)},this.events.click=function(e){return t.props.onClick(t.getState(),e)},this.events.contentLoad=function(){return t.updateMetrics()},this.events.resize=function(){return t.updateMetrics()},this.props.viewport.addEventListener("mousedown",this.events.pointerdown),this.props.viewport.addEventListener("touchstart",this.events.pointerdown),this.props.viewport.addEventListener("click",this.events.click),this.props.viewport.addEventListener("wheel",this.events.wheel),this.props.viewport.addEventListener("scroll",this.events.scroll),this.props.content.addEventListener("load",this.events.contentLoad,!0),window.addEventListener("mousemove",this.events.pointermove),window.addEventListener("touchmove",this.events.pointermove),window.addEventListener("mouseup",this.events.pointerup),window.addEventListener("touchend",this.events.pointerup),window.addEventListener("resize",this.events.resize)}},{key:"updateOptions",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.props=n({},this.props,{},t),this.props.onUpdate(this.getState()),this.run()}},{key:"destroy",value:function(){this.props.viewport.removeEventListener("mousedown",this.events.pointerdown),this.props.viewport.removeEventListener("touchstart",this.events.pointerdown),this.props.viewport.removeEventListener("click",this.events.click),this.props.viewport.removeEventListener("wheel",this.events.wheel),this.props.viewport.removeEventListener("scroll",this.events.scroll),this.props.content.removeEventListener("load",this.events.contentLoad),window.removeEventListener("mousemove",this.events.pointermove),window.removeEventListener("touchmove",this.events.pointermove),window.removeEventListener("mouseup",this.events.pointerup),window.removeEventListener("touchend",this.events.pointerup),window.removeEventListener("resize",this.events.resize)}}])&&r(e.prototype,i),o&&r(e,o),t}()}]).default}));
//# sourceMappingURL=scrollbooster.min.js.map
{
"name": "scrollbooster",
"description": "Enjoyable content drag-to-scroll library",
"version": "1.1.0",
"version": "2.0.0",
"author": "Ilya Shubin <pixelwake@gmail.com>",
"license": "MIT",
"main": "dist/scrollbooster.min.js",
"module": "scr/index.js",
"module": "src/index.js",
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
"dev": "webpack --mode development --watch",
"build": "webpack --mode production",
"start": "webpack-dev-server --host 0.0.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"browserslist": ["> 0.25%", "not dead"],
"repository": {

@@ -32,11 +29,11 @@ "type": "git",

"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.5",
"webpack": "^3.12.0",
"webpack-dev-server": "^2.9.1"
"@babel/core": "^7.7.7",
"@babel/plugin-proposal-object-rest-spread": "^7.7.7",
"@babel/preset-env": "^7.7.7",
"babel-loader": "^8.0.6",
"babel-plugin-add-module-exports": "^1.0.2",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
}
}
# ScrollBooster
Enjoyable content drag-to-scroll micro (~2KB gzipped) library.
Enjoyable drag-to-scroll micro library (~2KB gzipped). Supports smooth content scroll via mouse/touch dragging, trackpad or mouse wheel.
### Installation
You can install it via `npm` or `yarn` package manager or just drop a `script` tag:
You can install it via `npm` or `yarn` package manager or via `script` tag:

@@ -18,3 +18,3 @@ ``` bash

``` html
<script src="https://unpkg.com/scrollbooster@1.1.0/dist/scrollbooster.min.js"></script>
<script src="https://unpkg.com/scrollbooster@2.0.0/dist/scrollbooster.min.js"></script>
```

@@ -24,11 +24,25 @@

The most simple setup with default settings:
``` js
import ScrollBooster from 'scrollbooster'
import ScrollBooster from 'scrollbooster';
let sb = new ScrollBooster({
viewport: document.querySelector('.viewport') // required
// ...other options
})
const viewport = document.querySelector('.viewport');
const content = document.querySelector('.scrollable-content');
new ScrollBooster({
viewport,
content,
onUpdate: (data) => {
content.style.transform = `translate(
${-data.position.x}px,
${-data.position.y}px
)`;
},
// other options (see below)
});
```
Please note that in order to support IE11 you should replace arrow functions and string templates from code examples to supported equivalents or just use Babel.
### Options

@@ -38,15 +52,17 @@

------ | ---- | ------- | -----------
viewport | element | null | Viewport - outer element
content | element | First child of viewport element | Scrollable content inside viewport
handle | element | Viewport element | Element that respond to drag event
bounce | boolean | true | Inertia bounce effect (scroll past viewport borders)
textSelection | boolean | false | Ability to select text content
friction | float | 0.05 | Scroll friction factor (scroll inertia after pointer release)
bounceForce | float | 0.1 | Bounce effect factor
emulateScroll | boolean | false | Emulate viewport mouse wheel events (for cases when scrolling with `transform` property)
onUpdate | function | noop | User function that updates element properties according to received coordinates (see demo examples). Receives object with properties: `position`, `viewport` and `content`. Each property contains metrics to perform an actual scrolling
onClick | function | noop | Function that receives object with scrolling metrics and event object. Calls after each `click` in scrollable area. Here you can, for example, prevent default event for click on links
shouldScroll | function | noop | Function that receives object with scrolling metrics and event object. Calls on `pointerdown` (mousedown, touchstart) in scrollable area. Here you can return `true` or `false` to start actual scrolling or not
viewport | DOM Node | null | Content viewport element (required)
content | DOM Node | viewport child element | Scrollable content element inside viewport
direction | String | 'all' | Scroll direction. Could be 'horizontal', 'vertical' or 'all'
bounce | Boolean | true | Enables elastic bounce effect when hitting viewport borders
textSelection | Boolean | false | Enables text selection inside viewport
inputsFocus | Boolean | true | Enables focus for elements: 'input', 'textarea', 'button', 'select' and 'label'
pointerMode | String | 'all' | Specify pointer type. Supported values - 'touch' (scroll only on touch devices), 'mouse' (scroll only on desktop), 'all' (mobile and desktop)
friction | Number | 0.05 | Scroll friction factor - how fast scrolling stops after pointer release
bounceForce | Number | 0.1 | Elastic bounce effect factor
emulateScroll | Boolean | false | Enables mouse wheel/trackpad emulation inside viewport
onUpdate | Function | noop | Handler function to perform actual scrolling. Receives scrolling state object with coordinates
onClick | Function | noop | Click handler function. Here you can, for example, prevent default event for click on links. Receives object with scrolling metrics and event object. Calls after each `click` in scrollable area
shouldScroll | Function | noop | Handler function to permit or disable scrolling. Function that receives object with scrolling metrics and event object. Calls on `pointerdown` (mousedown, touchstart) in scrollable area. You can return `true` or `false` to enable or disable scrolling
### Methods to perform custom logic
### List of methods

@@ -56,5 +72,7 @@ Method | Description

setPosition | Sets new scroll position in viewport. Receives an object with properties `x` and `y`
updateMetrics | Updates element sizes. Useful for images loading or other dynamic content
getUpdate | Returns current metrics and coordinates in a same format as `onUpdate`
destroy | Destroys all instance's event listeners
scrollTo | Smooth scroll to position in viewport. Receives an object with properties `x` and `y`
updateMetrics | Forces to recalculate elments metrics. Useful for cases when content in scrollable area change its size dynamically
updateOptions | Updates ScrollBooster options. All properties from `Options` config object are supported
getState | Returns current scroll state in a same format as `onUpdate`
destroy | Removes all instance's event listeners

@@ -64,40 +82,36 @@ ### Full Example

``` js
let viewport = document.querySelector('.viewport')
let content = document.querySelector('.viewport-content')
const viewport = document.querySelector('.viewport');
const content = document.querySelector('.scrollable-content');
let sb = new ScrollBooster({
viewport: viewport,
content: content,
handle: document.querySelector('.viewport-scroller'),
const sb = new ScrollBooster({
viewport,
content,
bounce: true,
textSelection: false,
emulateScroll: false,
onUpdate: (data)=> {
emulateScroll: true,
onUpdate: (state) => {
// state contains useful metrics: position, dragOffset, isDragging, isMoving
content.style.transform = `translate(
${-data.position.x}px,
${-data.position.y}px
)`
// and also metrics: data.viewport['width'|'height'] and data.cotent['width'|'height']
${-state.position.x}px,
${-state.position.y}px
)`;
},
shouldScroll: (data, event) => {
if (event.target.classList.contains('button')) {
return false
} else {
return true
}
shouldScroll: (state, event) => {
// disable scroll if clicked on button
const isButton = event.taget.nodeName.toLowerCase() === 'button';
return !isButton;
},
onClick: (data, event) => {
if (event.target.classList.contains('link')) {
event.preventDefault()
// prevent default link event
const isLink = event.taget.nodeName.toLowerCase() === 'link';
if (isLink) {
event.preventDefault();
}
}
})
});
// methods example:
sb.updateMetrics()
sb.setPosition({
x: 100,
y: 100
})
sb.destroy()
// methods usage examples:
sb.updateMetrics();
sb.setPosition({ x: 100, y: 100 });
sb.destroy();
```

@@ -115,2 +129,2 @@

MIT License (c) Ilya Shubin
MIT License (c) Ilya Shubin

@@ -1,12 +0,35 @@

export default class ScrollBooster {
constructor(props = {}) {
const getFullWidth = (elem) => Math.max(elem.offsetWidth, elem.scrollWidth);
const getFullHeight = (elem) => Math.max(elem.offsetHeight, elem.scrollHeight);
if (!props.viewport || !(props.viewport instanceof Element)) {
console.error('"viewport" config property must be present and must be Element')
return
const textNodeFromPoint = (element, x, y) => {
const nodes = element.childNodes;
const range = document.createRange();
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.nodeType !== 3) { continue; }
range.selectNodeContents(node);
const rect = range.getBoundingClientRect();
if (x >= rect.left && y >= rect.top && x <= rect.right && y <= rect.bottom) {
return node;
}
}
return false;
};
let defaults = {
handle: props.viewport,
content: props.viewport.children[0],
const clearTextSelection = () => {
const selection = window.getSelection ? window.getSelection() : document.selection;
if (!selection) { return; }
if (selection.removeAllRanges) {
selection.removeAllRanges();
} else if (selection.empty) {
selection.empty();
}
};
export default class ScrollBooster {
constructor(options = {}) {
const defaults = {
content: options.viewport.children[0],
direction: 'all', // 'vertical', 'horizontal'
pointerMode: 'all', // 'touch', 'mouse'
bounce: true,

@@ -16,12 +39,19 @@ friction: 0.05,

textSelection: false,
onClick: function () {},
shouldScroll: function () { return true },
onUpdate: function () {}
inputsFocus: true,
emulateScroll: false,
onClick() {},
onUpdate() {},
shouldScroll() { return true }
};
this.props = { ...defaults, ...options };
if (!this.props.viewport || !(this.props.viewport instanceof Element)) {
console.error(`ScrollBooster init error: "viewport" config property must be present and must be Element`);
return;
}
this.props = { ...defaults, ...props }
if (!this.props.content) {
console.error('Viewport does not have any content')
return
console.error(`ScrollBooster init error: Viewport does not have any content`);
return;
}

@@ -32,47 +62,43 @@

height: this.props.viewport.clientHeight
}
};
this.content = {
width: getFullWidth(this.props.content),
height: getFullHeight(this.props.content)
}
};
this.position = { x: 0, y: 0 }
this.velocity = { x: 0, y: 0 }
this.friction = 1 - this.props.friction
this.bounceForce = this.props.bounceForce
this.position = { x: 0, y: 0 };
this.velocity = { x: 0, y: 0 };
this.friction = 1 - this.props.friction;
this.bounceForce = this.props.bounceForce;
this.isDragging = false
this.dragStartPosition = { x: 0, y: 0 }
this.dragOffsetPosition = { ...this.dragStartPosition }
this.dragPosition = { ...this.position }
this.isDragging = false;
this.dragStartPosition = { x: 0, y: 0 };
this.dragOffset = { ...this.dragStartPosition };
this.dragPosition = { ...this.position };
this.isScrollEnabled = !!this.props.emulateScroll
this.isScrolling = false
this.scrollOffset = { x: 0, y: 0 }
this.isTargetScroll = false;
this.targetPosition = { x: 0, y: 0 };
this.bounce = this.props.bounce
this.textSelection = this.props.textSelection
this.isScrolling = false;
this.scrollOffset = { x: 0, y: 0 };
this.bounce = this.props.bounce;
this.textSelection = this.props.textSelection;
this.boundX = {
from: Math.min(-this.content.width + this.viewport.width, 0),
to: 0
}
};
this.boundY = {
from: Math.min(-this.content.height + this.viewport.height, 0),
to: 0
}
};
this.mode = {
x: this.props.mode == 'x',
y: this.props.mode == 'y',
xy: this.props.mode !== 'x' && this.props.mode !== 'y'
}
this.isRunning = false;
this.rafID = null;
this.isRunning = false
this.rafID = null
this.events = {};
this.events = {}
this.animate()
this.handleEvents()
this.animate();
this.handleEvents();
}

@@ -84,43 +110,46 @@

run() {
this.isRunning = true
cancelAnimationFrame(this.rafID)
this.rafID = requestAnimationFrame(() => this.animate())
this.isRunning = true;
cancelAnimationFrame(this.rafID);
this.rafID = requestAnimationFrame(() => this.animate());
}
animate() {
if (!this.isRunning) { return }
this.update()
this.notify()
this.rafID = requestAnimationFrame(() => this.animate())
if (!this.isRunning) {
return;
}
this.update();
this.props.onUpdate(this.getState());
this.rafID = requestAnimationFrame(() => this.animate());
}
isMoving() {
return this.isDragging || this.isScrolling || Math.abs(this.velocity.x) >= 0.1 || Math.abs(this.velocity.y) >= 0.1;
}
update() {
this.applyBoundForce()
this.applyDragForce()
this.applyScrollForce()
this.applyBoundForce();
this.applyDragForce();
this.applyScrollForce();
this.applyTargetForce();
this.velocity.x *= this.friction
this.velocity.y *= this.friction
this.velocity.x *= this.friction;
this.velocity.y *= this.friction;
if (!this.mode.y) {
this.position.x += this.velocity.x
if (this.props.direction !== 'vertical') {
this.position.x += this.velocity.x;
}
if (!this.mode.x) {
this.position.y += this.velocity.y
if (this.props.direction !== 'horizontal') {
this.position.y += this.velocity.y;
}
// if bounce effect is disabled
if (!this.bounce || this.isScrolling) {
this.position.x = Math.max(Math.min(this.position.x, this.boundX.to), this.boundX.from)
this.position.y = Math.max(Math.min(this.position.y, this.boundY.to), this.boundY.from)
// disable bounce effect
if ((!this.bounce || this.isScrolling) && !this.isTargetScroll) {
this.position.x = Math.max(Math.min(this.position.x, this.boundX.to), this.boundX.from);
this.position.y = Math.max(Math.min(this.position.y, this.boundY.to), this.boundY.from);
}
// stop update loop if nothing moves
if (
!this.isDragging &&
!this.isScrolling &&
Math.abs(this.velocity.x) < 0.1 &&
Math.abs(this.velocity.y) < 0.1
) {
this.isRunning = false
if (!this.isMoving()) {
this.isRunning = false;
this.isTargetScroll = false;
}

@@ -130,4 +159,4 @@ }

applyForce(force) {
this.velocity.x += force.x
this.velocity.y += force.y
this.velocity.x += force.x;
this.velocity.y += force.y;
}

@@ -139,47 +168,50 @@

applyBoundForce() {
if (!this.bounce) { return }
if (this.isDragging) { return }
if (!this.bounce || this.isDragging) {
return;
}
let pastLeft = this.position.x < this.boundX.from
let pastRight = this.position.x > this.boundX.to
let pastTop = this.position.y < this.boundY.from
let pastBottom = this.position.y > this.boundY.to
// scrolled past viewport boundaries
const pastLeft = this.position.x < this.boundX.from;
const pastRight = this.position.x > this.boundX.to;
const pastTop = this.position.y < this.boundY.from;
const pastBottom = this.position.y > this.boundY.to;
const pastX = pastLeft || pastRight;
const pastY = pastTop || pastBottom;
let resultForce = { x: 0, y: 0 }
if (!pastX && !pastY) {
return;
}
// scrolled past left of right viewport boundaries
if (pastLeft || pastRight) {
let bound = pastLeft ? this.boundX.from : this.boundX.to
let distance = bound - this.position.x
const bound = {
x: pastLeft ? this.boundX.from : this.boundX.to,
y: pastTop ? this.boundY.from : this.boundY.to,
};
let force = distance * this.bounceForce
var restX = this.position.x + (this.velocity.x + force) / (1 - this.friction)
const distanceToBound = {
x: bound.x - this.position.x,
y: bound.y - this.position.y,
};
if (
!((pastLeft && restX < this.boundX.from) || (pastRight && restX > this.boundX.to))
) {
force = distance * this.bounceForce - this.velocity.x
}
const force = {
x: distanceToBound.x * this.bounceForce,
y: distanceToBound.y * this.bounceForce,
};
resultForce.x = force
const restPosition = {
x: this.position.x + (this.velocity.x + force.x) / (1 - this.friction),
y: this.position.y + (this.velocity.y + force.y) / (1 - this.friction),
};
if ((pastLeft && restPosition.x >= this.boundX.from) || (pastRight && restPosition.x <= this.boundX.to)) {
force.x = distanceToBound.x * this.bounceForce - this.velocity.x;
}
// scrolled past top of bottom viewport boundaries
if (pastTop || pastBottom) {
let bound = pastTop ? this.boundY.from : this.boundY.to
let distance = bound - this.position.y
let force = distance * this.bounceForce
var restY = this.position.y + (this.velocity.y + force) / (1 - this.friction)
if (
!((pastTop && restY < this.boundY.from) || (pastBottom && restY > this.boundY.to))
) {
force = distance * this.bounceForce - this.velocity.y
}
resultForce.y = force
if ((pastTop && restPosition.y >= this.boundY.from) || (pastBottom && restPosition.y <= this.boundY.to)) {
force.y = distanceToBound.y * this.bounceForce - this.velocity.y;
}
this.applyForce(resultForce)
this.applyForce({
x: pastX ? force.x : 0,
y: pastY ? force.y : 0,
});
}

@@ -191,43 +223,70 @@

applyDragForce() {
if (!this.isDragging) { return }
let dragVelocity = {
if (!this.isDragging) {
return;
}
const dragVelocity = {
x: this.dragPosition.x - this.position.x,
y: this.dragPosition.y - this.position.y
}
let dragForce = {
};
const force = {
x: dragVelocity.x - this.velocity.x,
y: dragVelocity.y - this.velocity.y
}
};
this.applyForce(dragForce)
this.applyForce(force);
}
/**
* Apply force to emulate mouse wheel
* Apply force to emulate mouse wheel or trackpad
*/
applyScrollForce() {
if (!this.isScrolling) { return }
if (!this.isScrolling) {
return;
}
let scrollForce = {
const force = {
x: this.scrollOffset.x - this.velocity.x,
y: this.scrollOffset.y - this.velocity.y
};
this.scrollOffset.x = 0;
this.scrollOffset.y = 0;
this.applyForce(force);
}
applyTargetForce() {
if (!this.isTargetScroll) {
return
}
this.scrollOffset.x = 0
this.scrollOffset.y = 0
const force = {
x: (this.targetPosition.x - this.position.x) * 0.08 - this.velocity.x,
y: (this.targetPosition.y - this.position.y) * 0.08 - this.velocity.y,
};
this.applyForce(scrollForce)
this.applyForce(force);
}
/**
* Smooth scroll to target position
*/
scrollTo(position = {}) {
this.isTargetScroll = true;
this.targetPosition.x = -position.x || 0;
this.targetPosition.y = -position.y || 0;
this.run();
}
/**
* Manual position setting
*/
setPosition(newPosition = {}) {
this.velocity.x = 0
this.velocity.y = 0
this.position.x = -newPosition.x || 0
this.position.y = -newPosition.y || 0
this.run()
setPosition(position = {}) {
this.velocity.x = 0;
this.velocity.y = 0;
this.position.x = -position.x || 0;
this.position.y = -position.y || 0;
this.run();
}

@@ -238,235 +297,179 @@

*/
getUpdate() {
getState() {
return {
isRunning: this.isRunning,
isDragging: this.isDragging,
isScrolling: this.isScrolling,
position: {
x: -this.position.x,
y: -this.position.y
},
dragOffsetPosition: this.dragOffsetPosition,
viewport: { ...this.viewport },
content: { ...this.content }
}
isMoving: this.isMoving(),
isDragging: !!(this.dragOffset.x || this.dragOffset.y),
position: { x: -this.position.x, y: -this.position.y },
dragOffset: this.dragOffset,
};
}
notify() {
this.props.onUpdate(this.getUpdate())
}
updateMetrics() {
this.viewport.width = this.props.viewport.clientWidth
this.viewport.height = this.props.viewport.clientHeight
this.viewport.width = this.props.viewport.clientWidth;
this.viewport.height = this.props.viewport.clientHeight;
this.content.width = getFullWidth(this.props.content)
this.content.height = getFullHeight(this.props.content)
this.content.width = getFullWidth(this.props.content);
this.content.height = getFullHeight(this.props.content);
this.boundX.from = Math.min(-this.content.width + this.viewport.width, 0)
this.boundY.from = Math.min(-this.content.height + this.viewport.height, 0)
this.boundX.from = Math.min(-this.content.width + this.viewport.width, 0);
this.boundY.from = Math.min(-this.content.height + this.viewport.height, 0);
this.run()
this.run();
}
handleEvents() {
let vp = this.props.viewport
let scroll = { x: 0, y: 0 }
let mousedown = { x: 0, y: 0 }
const dragOrigin = { x: 0, y: 0 };
let wheelTimer = null;
let isTouch = false;
let isTouch = false
let setDragPosition = (event) => {
let pageX, pageY
if (isTouch) {
pageX = event.touches[0].pageX
pageY = event.touches[0].pageY
} else {
pageX = event.pageX
pageY = event.pageY
const setDragPosition = (event) => {
if (!this.isDragging) {
return
}
this.dragOffsetPosition.x = pageX - mousedown.x
this.dragOffsetPosition.y = pageY - mousedown.y
const pageX = isTouch ? event.touches[0].pageX : event.pageX;
const pageY = isTouch ? event.touches[0].pageY: event.pageY;
this.dragPosition.x = this.dragStartPosition.x + this.dragOffsetPosition.x
this.dragPosition.y = this.dragStartPosition.y + this.dragOffsetPosition.y
this.dragOffset.x = pageX - dragOrigin.x;
this.dragOffset.y = pageY - dragOrigin.y;
if (!isTouch) {
event.preventDefault()
}
}
this.dragPosition.x = this.dragStartPosition.x + this.dragOffset.x;
this.dragPosition.y = this.dragStartPosition.y + this.dragOffset.y;
};
this.events.pointerdown = (event) => {
let pageX, pageY, clientX, clientY
isTouch = !!(event.touches && event.touches[0]);
isTouch = !!(event.touches && event.touches[0])
const eventData = isTouch ? event.touches[0] : event;
const { pageX, pageY, clientX, clientY } = eventData
if (isTouch) {
pageX = event.touches[0].pageX
pageY = event.touches[0].pageY
clientX = event.touches[0].clientX
clientY = event.touches[0].clientY
} else {
pageX = event.pageX
pageY = event.pageY
clientX = event.clientX
clientY = event.clientY
}
const { viewport } = this.props;
const rect = viewport.getBoundingClientRect();
let rect = vp.getBoundingClientRect()
// click on vertical scrollbar
if (clientX - rect.left >= vp.clientLeft + vp.clientWidth) {
return
if (clientX - rect.left >= viewport.clientLeft + viewport.clientWidth) {
return;
}
// click on horizontal scrollbar
if (clientY - rect.top >= vp.clientTop + vp.clientHeight) {
return
if (clientY - rect.top >= viewport.clientTop + viewport.clientHeight) {
return;
}
if (!this.props.shouldScroll(this.getUpdate(), event)) {
return
// interaction disabled by user
if (!this.props.shouldScroll(this.getState(), event)) {
return;
}
// text selection enabled
if (this.textSelection) {
let clickedNode = textNodeFromPoint(event.target, clientX, clientY)
if (clickedNode) {
return
} else {
clearTextSelection()
}
// disable on mobile
if (this.props.pointerMode === 'mouse' && isTouch) {
return;
}
this.isDragging = true
// disable on desktop
if (this.props.pointerMode === 'touch' && !isTouch) {
return;
}
if (scroll.x || scroll.y) {
this.position.x = scroll.x
this.position.y = scroll.y
scroll.x = 0
scroll.y = 0
// focus on form input elements
const formNodes = ['input', 'textarea', 'button', 'select', 'label'];
if (this.props.inputsFocus && formNodes.indexOf(event.target.nodeName.toLowerCase()) > -1) {
return;
}
mousedown.x = pageX
mousedown.y = pageY
this.dragStartPosition.x = this.position.x
this.dragStartPosition.y = this.position.y
setDragPosition(event)
// handle text selection
if (this.textSelection) {
const textNode = textNodeFromPoint(event.target, clientX, clientY);
if (textNode) {
return;
}
clearTextSelection();
}
this.run()
this.isDragging = true;
let pointerUp, removeEvents
dragOrigin.x = pageX;
dragOrigin.y = pageY;
this.dragStartPosition.x = this.position.x;
this.dragStartPosition.y = this.position.y;
removeEvents = (event) => {
this.isDragging = false
setDragPosition(event);
this.run();
event.preventDefault()
};
if (isTouch) {
window.removeEventListener('touchmove', setDragPosition)
window.removeEventListener('touchend', pointerUp)
} else {
window.removeEventListener('mousemove', setDragPosition)
window.removeEventListener('mouseup', pointerUp)
}
}
this.events.pointermove = (event) => {
setDragPosition(event);
};
if (isTouch) {
pointerUp = window.addEventListener('touchend', removeEvents)
window.addEventListener('touchmove', setDragPosition)
} else {
pointerUp = window.addEventListener('mouseup', removeEvents)
window.addEventListener('mousemove', setDragPosition)
}
}
this.events.pointerup = () => {
this.isDragging = false;
};
let scrollTimer = null
this.events.wheel = (event) => {
this.velocity.x = 0
if (!this.props.emulateScroll) {
return;
}
this.velocity.x = 0;
this.velocity.y = 0;
this.isScrolling = true;
if (!this.isScrollEnabled) { return }
this.isScrolling = true
this.scrollOffset.x = -event.deltaX;
this.scrollOffset.y = -event.deltaY;
this.scrollOffset.x = -event.deltaX
this.scrollOffset.y = -event.deltaY
this.run();
this.run()
clearTimeout(wheelTimer);
wheelTimer = setTimeout(() => (this.isScrolling = false), 80);
event.preventDefault();
};
clearTimeout(scrollTimer)
scrollTimer = setTimeout(() => this.isScrolling = false, 80)
event.preventDefault()
}
this.events.scroll = (event) => {
let sl = this.props.viewport.scrollLeft
let st = this.props.viewport.scrollTop
if (Math.abs(this.position.x + sl) > 3) {
this.position.x = -sl
this.velocity.x = 0
this.events.scroll = () => {
const { scrollLeft, scrollTop } = this.props.viewport;
if (Math.abs(this.position.x + scrollLeft) > 3) {
this.position.x = -scrollLeft;
this.velocity.x = 0;
}
if (Math.abs(this.position.y + st) > 3) {
this.position.y = -st
this.velocity.y = 0
if (Math.abs(this.position.y + scrollTop) > 3) {
this.position.y = -scrollTop;
this.velocity.y = 0;
}
scroll.x = -this.props.viewport.scrollLeft
scroll.y = -this.props.viewport.scrollTop
}
};
this.events.click = (event) => {
this.props.onClick(this.getUpdate(), event)
}
this.events.click = (event) => this.props.onClick(this.getState(), event);
this.events.contentLoad = () => this.updateMetrics();
this.events.resize = () => this.updateMetrics();
this.events.resize = this.updateMetrics.bind(this)
this.props.viewport.addEventListener('mousedown', this.events.pointerdown);
this.props.viewport.addEventListener('touchstart', this.events.pointerdown);
this.props.viewport.addEventListener('click', this.events.click);
this.props.viewport.addEventListener('wheel', this.events.wheel);
this.props.viewport.addEventListener('scroll', this.events.scroll);
this.props.content.addEventListener('load', this.events.contentLoad, true);
window.addEventListener('mousemove', this.events.pointermove);
window.addEventListener('touchmove', this.events.pointermove);
window.addEventListener('mouseup', this.events.pointerup);
window.addEventListener('touchend', this.events.pointerup);
window.addEventListener('resize', this.events.resize);
}
this.props.handle.addEventListener('mousedown', this.events.pointerdown)
this.props.handle.addEventListener('touchstart', this.events.pointerdown)
this.props.handle.addEventListener('click', this.events.click)
this.props.viewport.addEventListener('wheel', this.events.wheel)
this.props.viewport.addEventListener('scroll', this.events.scroll)
window.addEventListener('resize', this.events.resize)
updateOptions(options = {}) {
this.props = { ...this.props, ...options };
this.props.onUpdate(this.getState());
this.run();
}
destroy() {
this.props.handle.removeEventListener('mousedown', this.events.pointerdown)
this.props.handle.removeEventListener('touchstart', this.events.pointerdown)
this.props.handle.removeEventListener('click', this.events.click)
this.props.viewport.removeEventListener('wheel', this.events.wheel)
this.props.viewport.removeEventListener('scroll', this.events.scroll)
window.removeEventListener('resize', this.events.resize)
this.props.viewport.removeEventListener('mousedown', this.events.pointerdown);
this.props.viewport.removeEventListener('touchstart', this.events.pointerdown);
this.props.viewport.removeEventListener('click', this.events.click);
this.props.viewport.removeEventListener('wheel', this.events.wheel);
this.props.viewport.removeEventListener('scroll', this.events.scroll);
this.props.content.removeEventListener('load', this.events.contentLoad);
window.removeEventListener('mousemove', this.events.pointermove);
window.removeEventListener('touchmove', this.events.pointermove);
window.removeEventListener('mouseup', this.events.pointerup);
window.removeEventListener('touchend', this.events.pointerup);
window.removeEventListener('resize', this.events.resize);
}
}
function getFullWidth (elem) {
return Math.max(elem.offsetWidth, elem.scrollWidth)
}
function getFullHeight (elem) {
return Math.max(elem.offsetHeight, elem.scrollHeight)
}
function textNodeFromPoint (element, x, y) {
let node
let nodes = element.childNodes
let range = document.createRange()
for (let i = 0; node = nodes[i], i < nodes.length; i++) {
if (node.nodeType !== 3) continue
range.selectNodeContents(node)
let rect = range.getBoundingClientRect()
if (x >= rect.left && y >= rect.top && x <= rect.right && y <= rect.bottom) {
return node
}
}
return false
}
function clearTextSelection () {
let sel = window.getSelection ? window.getSelection() : document.selection
if (sel) {
if (sel.removeAllRanges) {
sel.removeAllRanges()
} else if (sel.empty) {
sel.empty()
}
}
}

@@ -1,38 +0,32 @@

(function() {
var scrollEl = document.querySelector('#init .inner')
var scr
var scrollEl = document.querySelector("#init .inner");
var scr;
beforeEach(function (done) {
beforeEach(function(done) {
scr = new ScrollBooster({
viewport: document.querySelector('#init .wrapper'),
viewport: document.querySelector("#init .wrapper"),
content: scrollEl,
onUpdate: function (data) {
scrollEl.style.transform = 'translate(' + -data.position.x + 'px, ' + -data.position.y + 'px)'
onUpdate: function(data) {
scrollEl.style.transform =
"translate(" + -data.position.x + "px, " + -data.position.y + "px)";
}
})
setTimeout(done, 300)
})
});
setTimeout(done, 300);
});
describe('Init', function () {
describe("Init", function() {
it("Init properties", function() {
chai.expect(scr.position.x).to.equal(0);
chai.expect(scr.position.y).to.equal(0);
it('Init properties', function () {
chai.expect(scr.position.x).to.equal(0)
chai.expect(scr.position.y).to.equal(0)
chai.expect(scr.props.viewport).to.be.an.instanceof(Element);
chai.expect(scr.props.content).to.be.an.instanceof(Element);
chai.expect(scr.props.viewport).to.be.an.instanceof(Element)
chai.expect(scr.props.content).to.be.an.instanceof(Element)
chai.expect(scr.viewport.width).to.equal(300);
chai.expect(scr.viewport.height).to.equal(300);
chai.expect(scr.viewport.width).to.equal(300)
chai.expect(scr.viewport.height).to.equal(300)
chai.expect(scr.content.width).to.gt(300)
chai.expect(scr.content.height).to.gt(300)
chai.expect(scr.mode.xy).to.equal(true)
chai.expect(scr.mode.x).to.equal(false)
chai.expect(scr.mode.y).to.equal(false)
})
})
})()
chai.expect(scr.content.width).to.gt(300);
chai.expect(scr.content.height).to.gt(300);
});
});
})();

@@ -1,12 +0,12 @@

(function () {
var scrollEl = document.querySelector('#nobounce .inner')
(function() {
var scrollEl = document.querySelector("#nobounce .inner");
var scr = new ScrollBooster({
viewport: document.querySelector('#nobounce .wrapper'),
viewport: document.querySelector("#nobounce .wrapper"),
emulateScroll: true,
bounce: false,
onUpdate: function (data) {
scrollEl.style.transform = 'translate(' + -data.position.x + 'px, ' + -data.position.y + 'px)'
onUpdate: function(data) {
scrollEl.style.transform =
"translate(" + -data.position.x + "px, " + -data.position.y + "px)";
}
})
})()
});
})();

@@ -1,28 +0,27 @@

(function () {
var viewportEl = document.querySelector('#scroll .wrapper')
var scr
(function() {
var viewportEl = document.querySelector("#scroll .wrapper");
var scr;
beforeEach(function (done) {
beforeEach(function(done) {
scr = new ScrollBooster({
viewport: viewportEl,
emulateScroll: true,
onUpdate: function (data) {
viewportEl.scrollTop = data.position.y
viewportEl.scrollLeft = data.position.x
onUpdate: function(data) {
viewportEl.scrollTop = data.position.y;
viewportEl.scrollLeft = data.position.x;
}
})
scr.setPosition({ x: 100, y: 100 })
setTimeout(done, 300)
})
});
scr.setPosition({ x: 100, y: 100 });
setTimeout(done, 300);
});
describe('Scroll', function () {
it('Scroll test', function () {
chai.expect(scr.position.x).to.equal(-100)
chai.expect(scr.position.y).to.equal(-100)
describe("Scroll", function() {
it("Scroll test", function() {
chai.expect(scr.position.x).to.equal(-100);
chai.expect(scr.position.y).to.equal(-100);
let st = viewportEl.scrollTop
chai.expect(st).to.equal(100)
})
})
})()
let st = viewportEl.scrollTop;
chai.expect(st).to.equal(100);
});
});
})();

@@ -1,16 +0,15 @@

(function () {
var scrollEl = document.querySelector('#xonly .inner')
(function() {
var scrollEl = document.querySelector("#xonly .inner");
var scr = new ScrollBooster({
viewport: document.querySelector('#xonly .wrapper'),
viewport: document.querySelector("#xonly .wrapper"),
emulateScroll: true,
mode: 'x',
onUpdate: function (data) {
scrollEl.style.transform = 'translate(' + -data.position.x + 'px, ' + -data.position.y + 'px)'
direction: "horizontal",
onUpdate: function(data) {
scrollEl.style.transform =
"translate(" + -data.position.x + "px, " + -data.position.y + "px)";
}
})
});
scr.setPosition({
x: 100
})
})()
});
})();

@@ -1,16 +0,15 @@

(function () {
var scrollEl = document.querySelector('#yonly .inner')
(function() {
var scrollEl = document.querySelector("#yonly .inner");
var scr = new ScrollBooster({
viewport: document.querySelector('#yonly .wrapper'),
mode: 'y',
viewport: document.querySelector("#yonly .wrapper"),
direction: "vertical",
emulateScroll: true,
onUpdate: function (data) {
scrollEl.style.transform = 'translate(' + -data.position.x + 'px, ' + -data.position.y + 'px)'
onUpdate: function(data) {
scrollEl.style.transform =
"translate(" + -data.position.x + "px, " + -data.position.y + "px)";
}
})
});
scr.setPosition({
y: 100
})
})()
});
})();

@@ -1,16 +0,12 @@

let path = require('path')
let webpack = require('webpack')
let path = require('path');
module.exports = {
entry: {
"scrollbooster": "./src/index.js",
"scrollbooster.min": "./src/index.js",
},
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
filename: 'scrollbooster.min.js',
library: 'ScrollBooster',
libraryTarget: 'umd',
umdNamedDefine: true
libraryExport: 'default',
umdNamedDefine: true,
},

@@ -21,4 +17,9 @@ module: {

test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
}
}
}

@@ -28,18 +29,10 @@ ]

devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true,
clientLogLevel: "none"
contentBase: path.join(__dirname),
clientLogLevel: 'none',
open: true,
},
devtool: '#source-map',
plugins: [
new webpack.optimize.UglifyJsPlugin({
include: /\.min\.js$/,
minimize: true
}),
new webpack.LoaderOptionsPlugin({
include: /\.min\.js$/,
minimize: true
})
]
}
devtool: 'source-map',
optimization: {
minimize: true,
},
};

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc