├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── dist └── react-smooth-scrollbar.js ├── gulpfile.js ├── package-lock.json ├── package.json ├── src └── react-smooth-scrollbar.js ├── test ├── index.html ├── main.js ├── style.css └── your_diary.jpg ├── webpack.dev.config.js └── webpack.prod.config.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "ecmaFeatures": { 4 | "arrowFunctions": true, 5 | "blockBindings": true, 6 | "classes": true, 7 | "defaultParams": true, 8 | "modules": true, 9 | "objectLiteralShorthandMethods": true, 10 | "objectLiteralShorthandProperties": true, 11 | "restParams": true, 12 | "spread": true, 13 | "superInFunctions": true, 14 | "templateStrings": true 15 | }, 16 | "env": { 17 | "browser": true, 18 | "es6": true 19 | }, 20 | "rules": { 21 | "accessor-pairs": 1, 22 | "curly": [1, "multi-line"], 23 | "quotes": [1, "single"], 24 | "new-cap": [2, {"newIsCap": true, "capIsNew": false}], 25 | "no-mixed-spaces-and-tabs": [2, "smart-tabs"], 26 | "no-var": 2, 27 | "no-else-return": 2, 28 | "semi": 1, 29 | "no-unused-vars": 1, 30 | "no-multiple-empty-lines": [1, {"max": 2}], 31 | "no-underscore-dangle": 0, 32 | "constructor-super": 2, 33 | "no-this-before-super": 2, 34 | "no-dupe-class-members": 2, 35 | "object-shorthand": 1, 36 | "prefer-arrow-callback": 1, 37 | "prefer-spread": 1, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | # Build 36 | .tmp -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | test/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dolphin Wood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-smooth-scrollbar 2 | 3 | [smooth-scrollbar](https://github.com/idiotWu/smooth-scrollbar) for react projects. 4 | 5 | ## Requirements 6 | 7 | React 0.14+ 8 | 9 | ## Install 10 | 11 | ``` 12 | npm install react-smooth-scrollbar smooth-scrollbar --save 13 | ``` 14 | 15 | ## Demo 16 | 17 | [http://idiotwu.github.io/react-smooth-scrollbar/](http://idiotwu.github.io/react-smooth-scrollbar/) 18 | 19 | ## Usage 20 | 21 | ```js 22 | import React from 'react'; 23 | import ReactDOM from 'react-dom'; 24 | import Scrollbar from 'react-smooth-scrollbar'; 25 | 26 | class App extends React.Component { 27 | render() { 28 | return ( 29 | 40 | your contents here... 41 | 42 | ); 43 | } 44 | } 45 | 46 | ReactDOM.render(, document.body); 47 | ``` 48 | 49 | ### Available Options 50 | 51 | | parameter | type | default | description | 52 | | :--------: | :--: | :-----: | :---------- | 53 | | damping | `number` | `0.1` | Momentum reduction damping factor, a float value between `(0, 1)`. The lower the value is, the more smooth the scrolling will be (also the more paint frames). | 54 | | thumbMinSize | `number` | `20` | Minimal size for scrollbar thumbs. | 55 | | renderByPixels | `boolean` | `true` | Render every frame in integer pixel values, set to `true` to improve scrolling performance. | 56 | | alwaysShowTracks | `boolean` | `false` | Keep scrollbar tracks visible. | 57 | | continuousScrolling | `boolean` | `true` | Set to `true` to allow outer scrollbars continue scrolling when current scrollbar reaches edge. | 58 | | wheelEventTarget | `EventTarget` | `null` | Element to be used as a listener for mouse wheel scroll events. By default, the container element is used. This option will be useful for dealing with fixed elements. | 59 | | plugins | `object` | `{}` | Options for plugins, see [Plugin System](https://github.com/idiotWu/smooth-scrollbar/blob/master/docs/plugin.md). | 60 | 61 | 62 | **Confusing with the option field? Try edit tool [here](http://idiotwu.github.io/smooth-scrollbar/)!** 63 | 64 | ## Using Scrollbar Plugins 65 | 66 | ```js 67 | import { Component } from 'react'; 68 | import PropTypes from 'prop-types'; 69 | import SmoothScrollbar from 'smooth-scrollbar'; 70 | import OverscrollPlugin from 'smooth-scrollbar/plugins/overscroll'; 71 | import Scrollbar from 'react-smooth-scrollbar'; 72 | 73 | SmoothScrollbar.use(OverscrollPlugin); 74 | 75 | class App2 extends Component { 76 | render() { 77 | return ( 78 | ... 79 | ); 80 | } 81 | } 82 | ``` 83 | 84 | ## Get Scrollbar Instance 85 | 86 | 1. Use `ref` in **parent component**: 87 | 88 | ```javascript 89 | class Parent extends React.Component { 90 | componentDidMount() { 91 | const { scrollbar } = this.$container; 92 | } 93 | 94 | render() { 95 | return ( 96 | this.$container = c}> 97 | your content... 98 | 99 | ); 100 | } 101 | } 102 | ``` 103 | 104 | 2. Use `Context` in **child component**: 105 | 106 | ```javascript 107 | class Child extends React.Component { 108 | static contextTypes = { 109 | getScrollbar: React.PropTypes.func 110 | }; 111 | 112 | componentDidMount() { 113 | this.context.getScrollbar((scrollbar) => { 114 | // ... 115 | }); 116 | } 117 | 118 | render() { 119 | return
this is child component.
; 120 | } 121 | } 122 | 123 | class App extends React.Component { 124 | render(){ 125 | return ( 126 | 127 | 128 | 129 | ); 130 | } 131 | } 132 | ``` 133 | 134 | 135 | ## APIs 136 | 137 | [Documents](https://github.com/idiotWu/smooth-scrollbar/tree/develop/docs) 138 | 139 | ## License 140 | 141 | MIT. 142 | 143 | [![Sponsor](https://app.codesponsor.io/embed/haJ2RqCqwBLZtPKnMNBYgn4M/idiotWu/react-smooth-scrollbar.svg)](https://app.codesponsor.io/link/haJ2RqCqwBLZtPKnMNBYgn4M/idiotWu/react-smooth-scrollbar) 144 | -------------------------------------------------------------------------------- /dist/react-smooth-scrollbar.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react"),require("prop-types"),require("smooth-scrollbar")):"function"==typeof define&&define.amd?define(["react","prop-types","smooth-scrollbar"],t):"object"==typeof exports?exports.Scrollbar=t(require("react"),require("prop-types"),require("smooth-scrollbar")):e.Scrollbar=t(e.React,e.PropTypes,e.Scrollbar)}(this,function(e,t,r){return function(e){function t(o){if(r[o])return r[o].exports;var n=r[o]={exports:{},id:o,loaded:!1};return e[o].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}function n(e,t){var r={};for(var o in e)t.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(r[o]=e[o]);return r}function l(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function c(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var a=Object.assign||function(e){for(var t=1;t { 32 | if (typeof cb !== 'function') return; 33 | 34 | if (this.scrollbar) setTimeout(() => cb(this.scrollbar)); 35 | else this.callbacks.push(cb); 36 | } 37 | }; 38 | } 39 | 40 | componentDidMount() { 41 | this.scrollbar = SmoothScrollbar.init(this.$container, this.props); 42 | 43 | this.callbacks.forEach((cb) => { 44 | requestAnimationFrame(() => cb(this.scrollbar)); 45 | }); 46 | 47 | this.scrollbar.addListener(this.handleScroll.bind(this)); 48 | } 49 | 50 | componentWillUnmount() { 51 | if (this.scrollbar) { 52 | this.scrollbar.destroy(); 53 | } 54 | } 55 | 56 | componentWillReceiveProps(nextProps) { 57 | Object.keys(nextProps).forEach((key) => { 58 | if (!key in this.scrollbar.options) { 59 | return; 60 | } 61 | 62 | if (key === 'plugins') { 63 | Object.keys(nextProps.plugins).forEach((pluginName) => { 64 | this.scrollbar.updatePluginOptions(pluginName, nextProps.plugins[pluginName]); 65 | }); 66 | } else { 67 | this.scrollbar.options[key] = nextProps[key]; 68 | } 69 | }); 70 | } 71 | 72 | componentDidUpdate() { 73 | this.scrollbar && this.scrollbar.update(); 74 | } 75 | 76 | handleScroll(status) { 77 | if (this.props.onScroll) { 78 | this.props.onScroll(status, this.scrollbar); 79 | } 80 | } 81 | 82 | render() { 83 | const { 84 | damping, 85 | thumbMinSize, 86 | syncCallbacks, 87 | renderByPixels, 88 | alwaysShowTracks, 89 | continuousScrolling, 90 | wheelEventTarget, 91 | plugins, 92 | 93 | onScroll, 94 | children, 95 | ...others, 96 | } = this.props; 97 | 98 | return ( 99 |
this.$container = element} {...others}> 100 |
{children}
101 |
102 | ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React Smooth Scrollbar 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Scrollbar from '../src/react-smooth-scrollbar.js'; 4 | import SmoothScrollbar from 'smooth-scrollbar'; 5 | import OverscrollPlugin from 'smooth-scrollbar/plugins/overscroll'; 6 | 7 | SmoothScrollbar.use(OverscrollPlugin); 8 | 9 | class App extends React.Component { 10 | state = { 11 | damping: 0.1, 12 | count: 3, 13 | }; 14 | 15 | randomDamping() { 16 | const nextState = { 17 | damping: Math.random() * 0.5 + 0.1, 18 | count: Math.random() * 10 | 0, 19 | }; 20 | 21 | console.log(nextState); 22 | 23 | this.setState(nextState); 24 | 25 | setTimeout(this.randomDamping.bind(this), 3000); 26 | } 27 | 28 | componentDidMount() { 29 | this.scrollbar = this.$container.scrollbar; 30 | 31 | this.randomDamping(); 32 | } 33 | 34 | _randomItem() { 35 | const res = []; 36 | 37 | for (let i = 0; i < this.state.count; i++) { 38 | res.push(
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 39 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 40 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 41 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 42 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 43 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
); 44 | } 45 | 46 | return res; 47 | } 48 | 49 | render() { 50 | return ( 51 | this.$container = c} 53 | damping={this.state.damping} 54 | plugins={{ 55 | overscroll: { damping: this.state.damping } 56 | }} 57 | > 58 | 59 | {this._randomItem()} 60 | 61 | ); 62 | } 63 | } 64 | 65 | ReactDOM.render( 66 | , 67 | document.getElementById('app') 68 | ); 69 | -------------------------------------------------------------------------------- /test/style.css: -------------------------------------------------------------------------------- 1 | main { 2 | position: absolute; 3 | top: 50%; 4 | left: 50%; 5 | transform: translate(-50%, -50%); 6 | } 7 | 8 | [data-scrollbar] { 9 | width: 600px; 10 | height: 400px; 11 | border: 3px solid #ccc; 12 | margin: 0 auto; 13 | } 14 | 15 | [data-scrollbar] img { 16 | display: block; 17 | width: 1920px; 18 | } 19 | 20 | [data-scrollbar] ol { 21 | width: 600px; 22 | } 23 | 24 | [data-scrollbar] footer { 25 | position: fixed; 26 | bottom: 0; 27 | left: 0; 28 | width: 600px; 29 | text-align: center; 30 | font-size: 2em; 31 | background: skyblue; 32 | text-align: center; 33 | color: #fff; 34 | transition: opacity .5s; 35 | } 36 | -------------------------------------------------------------------------------- /test/your_diary.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idiotWu/react-smooth-scrollbar/9e0b6a5ec0b8816f982b5a5d6e0d5766ebff6fd1/test/your_diary.jpg -------------------------------------------------------------------------------- /webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | devtool: 'inline-source-map', 3 | module: { 4 | preLoaders: [{ 5 | test: /\.jsx?$/, 6 | exclude: /(node_modules|bower_components)/, 7 | loader: 'eslint-loader' 8 | }], 9 | loaders: [{ 10 | test: /\.jsx?$/, 11 | loader: 'babel-loader', 12 | query: { 13 | presets: ['es2015', 'stage-0', 'react'] 14 | } 15 | }] 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | output: { 3 | library: 'Scrollbar', 4 | libraryTarget: 'umd' 5 | }, 6 | externals: { 7 | react: { 8 | root: 'React', 9 | commonjs: 'react', 10 | commonjs2: 'react', 11 | amd: 'react' 12 | }, 13 | 'prop-types': { 14 | root: 'PropTypes', 15 | commonjs: 'prop-types', 16 | commonjs2: 'prop-types', 17 | amd: 'prop-types' 18 | }, 19 | 'smooth-scrollbar': { 20 | root: 'Scrollbar', 21 | commonjs: 'smooth-scrollbar', 22 | commonjs2: 'smooth-scrollbar', 23 | amd: 'smooth-scrollbar' 24 | } 25 | }, 26 | module: { 27 | preLoaders: [{ 28 | test: /\.jsx?$/, 29 | exclude: /(node_modules|bower_components)/, 30 | loader: 'eslint-loader' 31 | }], 32 | loaders: [{ 33 | test: /\.jsx?$/, 34 | exclude: /(node_modules|bower_components)/, 35 | loader: 'babel-loader', 36 | query: { 37 | presets: ['es2015', 'stage-0', 'react'], 38 | plugins: [ 39 | 'add-module-exports', 40 | ] 41 | } 42 | }] 43 | } 44 | }; 45 | --------------------------------------------------------------------------------