├── .eslintignore ├── .eslintrc.js ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── @types └── index.ts ├── LICENSE ├── README.md ├── babel.config.js ├── jest.config.js ├── lib └── index.js ├── package.json ├── setupFile.ts ├── src ├── assets │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── air.jpg │ └── sw.jpg ├── index.tsx ├── kitchensink │ ├── Routes.tsx │ ├── index.html │ ├── index.tsx │ ├── modules.d.ts │ ├── p1.tsx │ ├── p2.tsx │ ├── p3.tsx │ ├── p4.tsx │ └── p5.tsx ├── modules │ ├── Background.tsx │ ├── Parallax.tsx │ └── ParallaxChildren.tsx └── utils │ ├── dom.test.ts │ ├── dom.ts │ ├── parallax.test.ts │ └── parallax.ts ├── tsconfig.eslint.json ├── tsconfig.json ├── webpack ├── base.config.js ├── build.config.js └── dev.config.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | /lib 2 | /node_modules 3 | /@types 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['@typescript-eslint', 'jest'], 3 | parser: '@typescript-eslint/parser', 4 | parserOptions: { 5 | project: './tsconfig.eslint.json', 6 | tsconfigRootDir: __dirname, 7 | }, 8 | extends: [ 9 | 'airbnb', 10 | 'airbnb-typescript', 11 | 'plugin:react/recommended', 12 | 'prettier', 13 | 'prettier/react', 14 | 'prettier/standard', 15 | 'prettier/@typescript-eslint', 16 | 'plugin:@typescript-eslint/recommended', 17 | 'plugin:jest/recommended', 18 | ], 19 | env: { 20 | browser: true, 21 | node: true, 22 | mocha: true, 23 | }, 24 | rules: { 25 | 'react/jsx-filename-extension': ['error', { extensions: ['.tsx'] }], 26 | 'import/prefer-default-export': 0, 27 | 'no-console': 'off', 28 | 'react/button-has-type': 'off', 29 | 'react/prop-types': 0, 30 | 'no-underscore-dangle': 'off', 31 | curly: 'error', 32 | 'import/no-extraneous-dependencies': 0, 33 | '@typescript-eslint/explicit-function-return-type': 0, 34 | '@typescript-eslint/no-var-requires': 0, 35 | '@typescript-eslint/no-unused-vars': [ 36 | 'error', 37 | { 38 | vars: 'all', 39 | args: 'after-used', 40 | ignoreRestSiblings: false, 41 | argsIgnorePattern: '^_', 42 | }, 43 | ], 44 | 'react/static-property-placement': 0, 45 | }, 46 | globals: { 47 | expect: true, 48 | document: true, 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop/Smartphone (please complete the following information):** 27 | - OS and Device: [e.g. iPhone6 iOS 12] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Code and/or example** 32 | If possible add a repository or codesandbox link where the issue is reproduced. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /www 3 | .sass-cache/ 4 | /test 5 | /npm-debug.log 6 | .DS_Store 7 | coverage 8 | .cache 9 | .dev -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | index.html 3 | webpack.*.js 4 | *.tgz 5 | .cache 6 | .dev 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v10.14.2 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | package.json 3 | package-lock.json 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | printWidth: 100, 3 | "tabWidth": 4, 4 | "singleQuote": true, 5 | "trailingComma": "all" 6 | } 7 | -------------------------------------------------------------------------------- /@types/index.ts: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | 3 | export type DynamicBlurProp = { min: number; max: number }; 4 | export type BlurProp = number | DynamicBlurProp; 5 | export type BgImageProp = string; 6 | export type BgImageSrcSetProp = string; 7 | export type BgImageSizesProp = string; 8 | export interface SplitChildrenResultType { 9 | bgChildren: Array; 10 | children: Array; 11 | } 12 | 13 | export type ParallaxProps = { 14 | bgClassName?: string; 15 | bgImage?: BgImageProp; 16 | bgImageAlt?: string; 17 | bgImageSizes?: BgImageSizesProp; 18 | bgImageSrcSet?: BgImageSrcSetProp; 19 | bgImageStyle?: { [key: string]: any }; 20 | onLoad?: (event: Event) => void; 21 | bgStyle?: { [key: string]: any }; 22 | blur?: BlurProp; 23 | children?: React.ReactNode; 24 | className?: string; 25 | contentClassName?: string; 26 | disabled?: boolean; 27 | parent?: HTMLElement; 28 | renderLayer?: (percentage: number) => any; 29 | strength?: number; 30 | style?: { [key: string]: any }; 31 | lazy?: boolean; 32 | }; 33 | 34 | type ParallaxState = { 35 | bgImage: string; 36 | bgImageSrcSet: string; 37 | bgImageSizes: string; 38 | bgStyle?: { [key: string]: any }; 39 | imgStyle: { [key: string]: any }; 40 | percentage: number; 41 | splitChildren: SplitChildrenResultType; 42 | }; 43 | 44 | export type BackgroundProps = { 45 | className?: string; 46 | children?: React.ReactNode 47 | }; 48 | 49 | export type ParallaxChildrenProps = { 50 | className?: string; 51 | children?: React.ReactNode; 52 | onMount(node: HTMLDivElement): void; 53 | }; 54 | 55 | export class Parallax extends React.Component {} 56 | 57 | export class Background extends React.Component {} 58 | 59 | export interface StyleObjectType { 60 | [key: string]: string; 61 | } 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Richard Rutsche 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-parallax [![NPM version][npm-image]][npm-url] 2 | 3 | ## Install 4 | 5 | ```sh 6 | yarn add react-parallax 7 | ``` 8 | 9 | ### [Demo on codesandbox](https://codesandbox.io/embed/r0yEkozrw?view=preview) 10 | 11 | ## Contribute 12 | 13 | If you find any bug or have problems and/or ideas regarding this library feel free to open an issue or pull request. Either way please create a working example so I can reproduce it. Link to a repository or even easier - fork the demo codesandbox project. This would help a lot. 14 | 15 | This project is maintained during evenings and weekends. If you like it, please consider to buy me a coffee ;-) ...or contribute in other ways. 16 | 17 | 18 | 19 | ## Usage examples 20 | 21 | ### Basic - background image with fixed blur effect 22 | 23 | ```javascript 24 | import { Parallax } from 'react-parallax'; 25 | 26 | const Container = () => ( 27 | 28 | Content goes here. Parallax height grows with content height. 29 | 30 | ); 31 | ``` 32 | 33 | ### Dynamic blur and negative strength for reverse direction 34 | 35 | ```javascript 36 | import { Parallax, Background } from 'react-parallax'; 37 | 38 | const Container = () => ( 39 | 45 | Blur transition from min to max 46 |
47 | 48 | ); 49 | ``` 50 | 51 | ### Custom background element 52 | 53 | Use the background component for custom elements. Unlike the `bgImage` this one will not be scaled depending on the parent/window width. 54 | 55 | ```javascript 56 | import { Parallax, Background } from 'react-parallax'; 57 | 58 | const Container = () => ( 59 | 60 | 61 | fill murray 62 | 63 | 64 | ); 65 | ``` 66 | 67 | ### Render prop 68 | 69 | Calculate your own stuff depending on the `percentage` value. 70 | 71 | ```javascript 72 | import { Parallax, Background } from 'react-parallax'; 73 | 74 | const Container = () => ( 75 | ( 78 |
88 | )} 89 | > 90 |

... Content

91 | 92 | ); 93 | ``` 94 | 95 | ## Background Component 96 | 97 | Child nodes inside this Background will be positioned like the bgImage behind the other children. Unlike the bgImage there is no automatic scaling. 98 | 99 | ## Props 100 | 101 | | Name | Type | Default | Description | example | 102 | | --------------------- | :-----------: | :-----------------------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------------------------------------------------------------------------------------ | 103 | | **bgImage** | `String` | | path to the background image that makes parallax effect | | 104 | | **bgImageAlt** | `String` | | alt text for bgImage. | | 105 | | **bgImageSize** | `String` | | img `sizes` attribute. | | 106 | | **bgImageSrcSet** | `String` | | img `srcset` attribute | | 107 | | **style** | `Object` | | style object for the component itself | | 108 | | **bgStyle** | `Object` | | additional style object for the bg image/children [Valid style attributes](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Properties_Reference) | | 109 | | **bgClassName** | `String` | | custom classname for image | | 110 | | **contentClassName** | `String` | `react-parallax-content` | custom classname for parallax inner | | 111 | | **bgImageStyle** | `Object` | | set background image styling | `{height: '50px', maxWidth: '75px', opacity: '.5'}` | 112 | | **strength** | `Number` | `100` | parallax effect strength (in pixel). this will define the amount of pixels the background image is translated | | 113 | | **blur** | `Number` | `0` or `{min:0, max:5}` | number value for background image blur or object in format `{min:0, max:5}` for dynamic blur depending on scroll position | | 114 | | **renderLayer** | `Function` | | Function that gets a percentage value of the current position as parameter for custom calculationa. It renders a layer above the actual background, below `children`. | `renderLayer={percentage => (
)}` | 115 | | **disabled** | `Boolean` | `false` | turns off parallax effect if set to true | `{height: '50px', maxWidth: '75px', opacity: '.5'}` | 116 | | **className** | `String` | | set an additional className | | 117 | | **parent** | `Node` | `document` | set optional parent for nested scrolling | | 118 | | **children** | | | used to display any content inside the react-parallax component | | 119 | 120 | ## Development 121 | 122 | ```sh 123 | # setup 124 | yarn 125 | # Development, live reload, JSX transpiling, run: 126 | yarn dev 127 | ``` 128 | 129 | Port `3000` on all OS by default. Can be set with option -port=8080 130 | 131 | # License 132 | 133 | MIT 134 | 135 | [npm-image]: https://img.shields.io/npm/v/react-parallax.svg?style=flat-square 136 | [npm-url]: https://www.npmjs.com/package/react-parallax 137 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/preset-env', '@babel/preset-react'], 3 | plugins: ['@babel/plugin-proposal-class-properties'], 4 | env: { 5 | development: { 6 | plugins: ['react-hot-loader/babel'], 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | setupFiles: ['/setupFile.ts'], 4 | }; 5 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | !function webpackUniversalModuleDefinition(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define("react-parallax",["react"],t):"object"==typeof exports?exports["react-parallax"]=t(require("react")):e["react-parallax"]=t(e.react)}("undefined"!=typeof self?self:this,(function(e){return function(e){var t={};function __webpack_require__(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,__webpack_require__),r.l=!0,r.exports}return __webpack_require__.m=e,__webpack_require__.c=t,__webpack_require__.d=function(e,t,n){__webpack_require__.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},__webpack_require__.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.t=function(e,t){if(1&t&&(e=__webpack_require__(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(__webpack_require__.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)__webpack_require__.d(n,r,function(t){return e[t]}.bind(null,r));return n},__webpack_require__.n=function(e){var t=e&&e.__esModule?function getDefault(){return e.default}:function getModuleExports(){return e};return __webpack_require__.d(t,"a",t),t},__webpack_require__.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s=3)}([function(t,n){t.exports=e},function(e,t,n){"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function _typeof(e){return typeof e}:function _typeof(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&_setPrototypeOf(e,t)}function _setPrototypeOf(e,t){return(_setPrototypeOf=Object.setPrototypeOf||function _setPrototypeOf(e,t){return e.__proto__=t,e})(e,t)}function _createSuper(e){var t=function _isNativeReflectConstruct(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function _createSuperInternal(){var n,r=_getPrototypeOf(e);if(t){var o=_getPrototypeOf(this).constructor;n=Reflect.construct(r,arguments,o)}else n=r.apply(this,arguments);return _possibleConstructorReturn(this,n)}}function _possibleConstructorReturn(e,t){return!t||"object"!==_typeof(t)&&"function"!=typeof t?function _assertThisInitialized(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function _getPrototypeOf(e){return(_getPrototypeOf=Object.setPrototypeOf?Object.getPrototypeOf:function _getPrototypeOf(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Background=t.Parallax=void 0;var o=r(n(0)),i=function(e){_inherits(Parallax,e);var t=_createSuper(Parallax);function Parallax(){return _classCallCheck(this,Parallax),t.apply(this,arguments)}return Parallax}(o.default.Component);t.Parallax=i;var a=function(e){_inherits(Background,e);var t=_createSuper(Background);function Background(){return _classCallCheck(this,Background),t.apply(this,arguments)}return Background}(o.default.Component);t.Background=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.canUseDOM=t.getNodeHeight=t.isScrolledIntoView=t.getWindowHeight=void 0,t.getWindowHeight=function(e){if(!e)return 0;var t=window,n=document,r=n.documentElement,o=n.getElementsByTagName("body")[0];return t.innerHeight||r.clientHeight||o.clientHeight},t.isScrolledIntoView=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2?arguments[2]:void 0;if(!r)return!1;var o=e.getBoundingClientRect().top-n,i=e.getBoundingClientRect().bottom+n;return o<=t.getWindowHeight(r)&&i>=0},t.getNodeHeight=function(e,n){return e?n&&"clientHeight"in n?n.clientHeight:t.getWindowHeight(e):0},t.canUseDOM=function(){return!("undefined"==typeof window||!window.document||!window.document.createElement)}},function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Background=t.Parallax=void 0;var o=r(n(4));t.Parallax=o.default;var i=r(n(7));t.Background=i.default},function(e,t,n){"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function _typeof(e){return typeof e}:function _typeof(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _defineProperties(e,t){for(var n=0;n=10&&c.isScrolledIntoView(n.node,100,n.canUseDOM)&&(window.requestAnimationFrame(n.updatePosition),n.timestamp=e)}},n.onContentMount=function(e){n.content=e},n.updatePosition=function(){if(n.content){var e=!1;n.contentHeight=n.content.getBoundingClientRect().height,n.contentWidth=n.node.getBoundingClientRect().width,n.img&&n.img.naturalWidth/n.img.naturalHeight0;r&&n.setImagePosition(t,e),o&&n.setBackgroundPosition(t),r||o||n.setState({percentage:t})}},n.state={bgImage:e.bgImage,bgImageSrcSet:e.bgImageSrcSet,bgImageSizes:e.bgImageSizes,imgStyle:s,bgStyle:Object.assign(Object.assign({},s),e.bgStyle),percentage:0,splitChildren:a.getSplitChildren(e)},n.canUseDOM=c.canUseDOM(),n.node=null,n.content=null,n.bgImageLoaded=!1,n.bgImageRef=void 0,n.parent=e.parent,n.parentHeight=c.getNodeHeight(n.canUseDOM,n.parent),n.timestamp=Date.now(),n.isDynamicBlur=a.getHasDynamicBlur(e.blur),n}return function _createClass(e,t,n){return t&&_defineProperties(e.prototype,t),n&&_defineProperties(e,n),e}(Parallax,[{key:"componentDidMount",value:function componentDidMount(){var e=this.props.parent,t=this.state,n=t.bgImage,r=t.bgImageSrcSet,o=t.bgImageSizes;this.parent=e||document,this.addListeners(),n?this.loadImage(n,r,o):this.updatePosition()}},{key:"componentDidUpdate",value:function componentDidUpdate(e){var t=this.props,n=t.parent,r=t.bgImage,o=t.bgImageSrcSet,i=t.bgImageSizes,a=this.state.bgImage;e.parent!==n&&(this.removeListeners(this.parent),this.parent=n,n&&this.addListeners()),this.parentHeight=c.getNodeHeight(this.canUseDOM,this.parent),a!==r&&this.loadImage(r,o,i)}},{key:"componentWillUnmount",value:function componentWillUnmount(){this.removeListeners(this.parent),this.releaseImage()}},{key:"setBackgroundPosition",value:function setBackgroundPosition(e){var t=this.props,n=t.disabled,r=t.strength,o=Object.assign({},this.state.bgStyle);if(!n){var i="translate3d(-50%, ".concat((r<0?r:0)-r*e,"px, 0)");o.WebkitTransform=i,o.transform=i}this.setState({bgStyle:o,percentage:e})}},{key:"setImagePosition",value:function setImagePosition(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=this.props,r=n.disabled,o=n.strength,i=n.blur,c=t?"auto":"".concat(this.getImageHeight(),"px"),u=t?"".concat(this.contentWidth,"px"):"auto",s=Object.assign(Object.assign({},this.state.imgStyle),{height:c,width:u});if(!r){var l=o<0,f=(l?o:0)-o*e,p="translate3d(-50%, ".concat(f,"px, 0)"),d="none";i&&(d="blur(".concat(a.getBlurValue(this.isDynamicBlur,i,e),"px)")),s.WebkitTransform=p,s.transform=p,s.WebkitFilter=d,s.filter=d}this.setState({imgStyle:s,percentage:e})}},{key:"getImageHeight",value:function getImageHeight(){var e=this.props.strength,t=(e<0?2.5:1)*Math.abs(e);return Math.floor(this.contentHeight+t)}},{key:"loadImage",value:function loadImage(e,t,n){var r=this;this.releaseImage(),this.bgImageRef=new Image,this.bgImageRef.onload=function(o){r.setState({bgImage:e,bgImageSrcSet:t,bgImageSizes:n},(function(){return r.updatePosition()})),r.props.onLoad&&r.props.onLoad(o)},this.bgImageRef.onerror=this.bgImageRef.onload,this.bgImageRef.src=e,this.bgImageRef.srcset=t||"",this.bgImageRef.sizes=n||""}},{key:"releaseImage",value:function releaseImage(){this.bgImageRef&&(this.bgImageRef.onload=null,this.bgImageRef.onerror=null,delete this.bgImageRef)}},{key:"addListeners",value:function addListeners(){this.canUseDOM&&this.parent&&(this.parent.addEventListener("scroll",this.onScroll,!1),window.addEventListener("resize",this.onWindowResize,!1),window.addEventListener("load",this.onWindowLoad,!1))}},{key:"removeListeners",value:function removeListeners(e){this.canUseDOM&&(e&&e.removeEventListener("scroll",this.onScroll,!1),window.removeEventListener("resize",this.onWindowResize,!1),window.removeEventListener("load",this.onWindowLoad,!1))}},{key:"render",value:function render(){var e=this,t=this.props,n=t.className,r=t.style,i=t.bgClassName,a=t.contentClassName,c=t.bgImageAlt,s=t.renderLayer,l=t.bgImageStyle,f=t.lazy,p=this.state,d=p.bgImage,g=p.bgImageSrcSet,_=p.bgImageSizes,y=p.percentage,h=p.imgStyle,b=p.bgStyle,m=p.splitChildren;return o.default.createElement("div",{className:"react-parallax ".concat(n),style:Object.assign({position:"relative",overflow:"hidden"},r),ref:function ref(t){e.node=t}},d?o.default.createElement("img",{className:i,src:d,srcSet:g,sizes:_,ref:function ref(t){e.img=t},alt:c,style:Object.assign(Object.assign({},h),l),loading:f?"lazy":"eager"}):null,s?s(-(y-1)):null,m.bgChildren.length>0?o.default.createElement("div",{className:"react-parallax-background-children",ref:function ref(t){e.bg=t},style:b},m.bgChildren):null,o.default.createElement(u.default,{onMount:this.onContentMount,className:a},m.children))}}],[{key:"getDerivedStateFromProps",value:function getDerivedStateFromProps(e){return{splitChildren:a.getSplitChildren(e)}}}]),Parallax}(i.Parallax);l.defaultProps={bgClassName:"react-parallax-bgimage",bgImageAlt:"",className:"",contentClassName:"",disabled:!1,strength:100},t.default=l},function(e,t,n){"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function _typeof(e){return typeof e}:function _typeof(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.setBlur=t.getBlurValue=t.getHasDynamicBlur=t.getSplitChildren=t.getRelativePosition=t.getPercentage=void 0;var r=n(0),o=n(2);t.getPercentage=function(e,t,n){return(n-e)/(t-e)||0},t.getRelativePosition=function(e,n){if(!n)return 0;var r=e.getBoundingClientRect(),i=r.top,a=r.height,c=o.getNodeHeight(n),u=a>c?a:c,s=Math.round(i>u?u:i);return t.getPercentage(0,u,s)},t.getSplitChildren=function(e){var t=[],n=r.Children.toArray(e.children);return n.forEach((function(e,r){var o=e;o.type&&o.type.isParallaxBackground&&(t=t.concat(n.splice(r,1)))})),{bgChildren:t,children:n}},t.getHasDynamicBlur=function(e){return"object"===_typeof(e)&&void 0!==e.min&&void 0!==e.max},t.getBlurValue=function(e,t,n){return e?t.min+(1-n)*t.max:t},t.setBlur=function(e,t){e.style.webkitFilter="blur(".concat(t,"px)"),e.style.filter="blur(".concat(t,"px)")}},function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(0));t.default=function ParallaxChildren(e){var t=e.children,n=e.onMount,r=e.className;return o.default.createElement("div",{ref:function ref(e){return n(e)},className:r||"react-parallax-content",style:{position:"relative"}},t)}},function(e,t,n){"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function _typeof(e){return typeof e}:function _typeof(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,t){for(var n=0;n { 10 | globalAny.window.innerWidth = width || globalAny.window.innerWidth; 11 | globalAny.window.innerHeight = height || globalAny.window.innerHeight; 12 | globalAny.window.dispatchEvent(new Event('resize')); 13 | }; 14 | -------------------------------------------------------------------------------- /src/assets/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrutsche/react-parallax/1bbefcab47dfa51b2ea491989d3ffa4716212cb5/src/assets/1.jpg -------------------------------------------------------------------------------- /src/assets/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrutsche/react-parallax/1bbefcab47dfa51b2ea491989d3ffa4716212cb5/src/assets/2.jpg -------------------------------------------------------------------------------- /src/assets/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrutsche/react-parallax/1bbefcab47dfa51b2ea491989d3ffa4716212cb5/src/assets/3.jpg -------------------------------------------------------------------------------- /src/assets/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrutsche/react-parallax/1bbefcab47dfa51b2ea491989d3ffa4716212cb5/src/assets/4.jpg -------------------------------------------------------------------------------- /src/assets/air.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrutsche/react-parallax/1bbefcab47dfa51b2ea491989d3ffa4716212cb5/src/assets/air.jpg -------------------------------------------------------------------------------- /src/assets/sw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrutsche/react-parallax/1bbefcab47dfa51b2ea491989d3ffa4716212cb5/src/assets/sw.jpg -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import Parallax from './modules/Parallax'; 2 | import Background from './modules/Background'; 3 | 4 | export { Parallax, Background }; 5 | 6 | // https://www.typescriptlang.org/docs/handbook/react-&-webpack.html 7 | -------------------------------------------------------------------------------- /src/kitchensink/Routes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter, Switch, Route } from 'react-router-dom'; 3 | import { hot } from 'react-hot-loader/root'; 4 | import Page1 from './p1'; 5 | import Page2 from './p2'; 6 | import Page3 from './p3'; 7 | import Page4 from './p4'; 8 | import { Page5 } from './p5'; 9 | 10 | const Routes = () => ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | 22 | export default hot(Routes); 23 | -------------------------------------------------------------------------------- /src/kitchensink/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | -------------------------------------------------------------------------------- /src/kitchensink/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | import Routes from './Routes'; 5 | 6 | render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /src/kitchensink/modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.jpg'; 2 | -------------------------------------------------------------------------------- /src/kitchensink/p1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { Parallax } from '../index'; 4 | 5 | import image2 from '../assets/sw.jpg'; 6 | import image3 from '../assets/air.jpg'; 7 | import image4 from '../assets/4.jpg'; 8 | import image5 from '../assets/3.jpg'; 9 | 10 | const image1 = 11 | 'https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/empire-state-building-black-and-white-square-format-john-farnan.jpg'; 12 | 13 | const style = { 14 | backgroundColor: '#efefef', 15 | color: 'white', 16 | textAlign: 'center' as const, 17 | }; 18 | const fontStyle2 = { 19 | fontFamily: 'Helvetica Neue, Arial, sans-serif', 20 | textAlign: 'center' as const, 21 | fontWeight: 100, 22 | color: 'darkgrey', 23 | }; 24 | 25 | type PageOneProps = Record; 26 | type PageOneState = { 27 | BG: number; 28 | srcSet?: number; 29 | }; 30 | 31 | export default class PageOne extends React.Component { 32 | srcSets: { [key: string]: string }; 33 | 34 | backgrounds: { [key: string]: string }; 35 | 36 | constructor(props: PageOneProps) { 37 | super(props); 38 | this.backgrounds = { 39 | '1': image1, 40 | '2': image2, 41 | '3': image5, 42 | }; 43 | this.srcSets = { 44 | 1: `${image4} 1x, ${image2} 2x`, 45 | 2: `${image3} 1x, ${image1} 2x`, 46 | }; 47 | this.state = { 48 | BG: 1, 49 | }; 50 | } 51 | 52 | toggleBackground = (): void => { 53 | const { BG } = this.state; 54 | this.setState({ 55 | BG: BG === 1 ? 2 : 1, 56 | }); 57 | }; 58 | 59 | toggleSrcSet = (): void => { 60 | const { srcSet } = this.state; 61 | this.setState({ 62 | srcSet: srcSet === 1 ? 2 : 1, 63 | }); 64 | }; 65 | 66 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 67 | render(): any { 68 | const { BG, srcSet } = this.state; 69 | return ( 70 |
71 |
82 | 91 | 100 |
101 |
102 |
103 | ( 108 |
109 |
121 | 122 |
130 | Top {percentage} 131 |
132 |
140 | Bottom {percentage} 141 |
142 |
143 | )} 144 | > 145 |
146 |

147 |

148 | The sails and the English flag were hoisted at ten minutes past three. 149 | Mr. Fogg and Aouda, who were seated on deck, cast a last glance at the 150 | quay, in the hope of espying Passepartout. Fix was not without his fears 151 | lest chance should direct the steps of the unfortunate servant, whom he 152 | had so badly treated, in this direction; in which case an explanation 153 | the reverse of satisfactory to the detective must have ensued. But the 154 | Frenchman did not appear, and, without doubt, was still lying under the 155 | stupefying influence of the opium. 156 |

157 |

158 | 159 |
160 | Page Two 161 |
162 | ); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/kitchensink/p2.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { Parallax, Background } from '../index'; 4 | 5 | import image3 from '../assets/3.jpg'; 6 | 7 | type PageTwoProps = Record; 8 | type PageTwoState = { 9 | image: string; 10 | children: React.ReactNode; 11 | }; 12 | 13 | export default class PageTwo extends React.Component { 14 | constructor(props: PageTwoProps) { 15 | super(props); 16 | const fontStyle = { 17 | fontFamily: 'Helvetica Neue, Arial, sans-serif', 18 | textAlign: 'center' as const, 19 | fontWeight: 100, 20 | }; 21 | this.state = { 22 | image: 23 | 'https://totallycoolpix.com/images/tcp_images_before/1013/itp_shamma_esoof_012__tcp_gallery_image.jpg', 24 | children:

children no1

, 25 | }; 26 | } 27 | 28 | changeImage(): void { 29 | this.setState({ 30 | image: 'http://combiboilersleeds.com/images/rocket/rocket-6.jpg', 31 | }); 32 | } 33 | 34 | changeChildren(): void { 35 | const fontStyle = { 36 | fontFamily: 'Helvetica Neue, Arial, sans-serif', 37 | textAlign: 'center' as const, 38 | fontWeight: 100, 39 | }; 40 | this.setState({ 41 | children:

children no2!!!!

, 42 | }); 43 | } 44 | 45 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 46 | render(): any { 47 | const style = { 48 | backgroundColor: '#efefef', 49 | color: 'white', 50 | textAlign: 'center' as const, 51 | }; 52 | const fontStyle2 = { 53 | fontFamily: 'Helvetica Neue, Arial, sans-serif', 54 | textAlign: 'center' as const, 55 | fontWeight: 100, 56 | color: 'darkgrey', 57 | }; 58 | const { image, children } = this.state; 59 | return ( 60 |
61 | Page One 62 | 63 | 64 | fill murray 65 | 66 |

67 |
68 |

69 | ”Plague victims,” he announced. ”That’s the way 70 | they died everywhere in the last days. This must have been a family, 71 | running away from the contagion and perishing here on the Cliff House 72 | beach. They—what are you doing, Edwin?” 73 |

74 |
75 |
76 |

77 |
78 | 79 |
80 | {children} 81 |

82 |

83 | ”Queequeg,” said I, when they had dragged me, the last man, 84 | to the deck, and I was still shaking myself in my jacket to fling off 85 | the water; ”Queequeg, my fine friend, does this sort of thing 86 | often happen?” Without much emotion, though soaked through just 87 | like me, he gave me to understand that such things did often happen. 88 |

89 |

90 | ”Mr. Stubb,” said I, turning to that worthy, who, buttoned 91 | up in his oil-jacket, was now calmly smoking his pipe in the rain; 92 | ”Mr. Stubb, I think I have heard you say that of all whalemen you 93 | ever met, our chief mate, Mr. Starbuck, is by far the most careful and 94 | prudent. I suppose then, that going plump on a flying whale with your 95 | sail set in a foggy squall is the height of a whaleman’s 96 | discretion?” 97 |

98 |

99 |
100 |
101 |

102 |

103 | Quieting him with a word of command and a caress, I looked hurriedly through 104 | the approaching gloom for a sign of Dejah Thoris, and then, not seeing her, 105 | I called her name. There was an answering murmur from the far corner of the 106 | apartment, and with a couple of quick strides I was standing beside her 107 | where she crouched among the furs and silks upon an ancient carved wooden 108 | seat. As I waited she rose to her full height and looking me straight in the 109 | eye said: 110 |

111 |

112 |
113 |

114 | ”It’s certain,” thought he, ”though rascal as he is, 115 | he is a polite one!” 116 |

117 |

118 | The sails and the English flag were hoisted at ten minutes past three. Mr. 119 | Fogg and Aouda, who were seated on deck, cast a last glance at the quay, in 120 | the hope of espying Passepartout. Fix was not without his fears lest chance 121 | should direct the steps of the unfortunate servant, whom he had so badly 122 | treated, in this direction; in which case an explanation the reverse of 123 | satisfactory to the detective must have ensued. But the Frenchman did not 124 | appear, and, without doubt, was still lying under the stupefying influence 125 | of the opium. 126 |

127 | 130 | 133 |
134 |
135 | 136 |
137 | {children} 138 |

139 |

140 | ”It’s certain,” thought he, ”though rascal 141 | as he is, he is a polite one!” 142 |

143 |

144 | The sails and the English flag were hoisted at ten minutes past 145 | three. Mr. Fogg and Aouda, who were seated on deck, cast a last 146 | glance at the quay, in the hope of espying Passepartout. Fix was not 147 | without his fears lest chance should direct the steps of the 148 | unfortunate servant, whom he had so badly treated, in this 149 | direction; in which case an explanation the reverse of satisfactory 150 | to the detective must have ensued. But the Frenchman did not appear, 151 | and, without doubt, was still lying under the stupefying influence 152 | of the opium. 153 |

154 |

155 |
156 |
157 | 158 | 159 | fill murray 160 |
161 | fill murray 162 | 163 |

164 | A collection of textile samples lay spread out on the table - Samsa was a 165 | travelling salesman - and above it there hung a picture that he had recently 166 | cut out of an illustrated magazine and housed in a nice, gilded frame. It 167 | showed a lady fitted out with a fur hat and fur boa who sat upright, raising 168 | a heavy fur muff that covered the whole of her lower arm towards the viewer. 169 | Gregor then turned to look out the window at the dull weather. Drops 170 |

171 | 172 | 173 |
174 |
175 |

176 | A collection of textile samples lay spread out on the table - Samsa was a 177 | travelling salesman - and above it there hung a picture that he had recently 178 | cut out of an illustrated magazine and housed in a nice, gilded frame. It 179 | showed a lady fitted out with a fur hat and fur boa who sat upright, raising 180 | a heavy fur muff that covered the whole of her lower arm towards the viewer. 181 | Gregor then turned to look out the window at the dull weather. Drops 182 |

183 |

184 | A collection of textile samples lay spread out on the table - Samsa was a 185 | travelling salesman - and above it there hung a picture that he had recently 186 | cut out of an illustrated magazine and housed in a nice, gilded frame. It 187 | showed a lady fitted out with a fur hat and fur boa who sat upright, raising 188 | a heavy fur muff that covered the whole of her lower arm towards the viewer. 189 | Gregor then turned to look out the window at the dull weather. Drops 190 |

191 |

192 | A collection of textile samples lay spread out on the table - Samsa was a 193 | travelling salesman - and above it there hung a picture that he had recently 194 | cut out of an illustrated magazine and housed in a nice, gilded frame. It 195 | showed a lady fitted out with a fur hat and fur boa who sat upright, raising 196 | a heavy fur muff that covered the whole of her lower arm towards the viewer. 197 | Gregor then turned to look out the window at the dull weather. Drops 198 |

199 |

200 | A collection of textile samples lay spread out on the table - Samsa was a 201 | travelling salesman - and above it there hung a picture that he had recently 202 | cut out of an illustrated magazine and housed in a nice, gilded frame. It 203 | showed a lady fitted out with a fur hat and fur boa who sat upright, raising 204 | a heavy fur muff that covered the whole of her lower arm towards the viewer. 205 | Gregor then turned to look out the window at the dull weather. Drops 206 |

207 |
208 |
209 |
210 | ); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/kitchensink/p3.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Parallax } from '../index'; 3 | 4 | import image3 from '../assets/3.jpg'; 5 | 6 | type PageThreeProps = Record; 7 | type PageThreeState = { 8 | theParentRef: HTMLDivElement | null; 9 | }; 10 | 11 | export default class PageThree extends React.Component { 12 | constructor(props: PageThreeProps) { 13 | super(props); 14 | this.state = { 15 | theParentRef: null, 16 | }; 17 | } 18 | 19 | onParentRefMounted(ref: HTMLDivElement): void { 20 | const { theParentRef } = this.state; 21 | if (!theParentRef) { 22 | this.setState({ 23 | theParentRef: ref, 24 | }); 25 | } 26 | } 27 | 28 | getParentRef(): HTMLDivElement { 29 | const { theParentRef } = this.state; 30 | return theParentRef; 31 | } 32 | 33 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 34 | render(): any { 35 | const { theParentRef } = this.state; 36 | const style = { 37 | backgroundColor: '#efefef', 38 | color: 'white', 39 | textAlign: 'center' as const, 40 | position: 'absolute' as const, 41 | top: 0, 42 | height: '300px', 43 | overflow: 'scroll', 44 | }; 45 | const fontStyle2 = { 46 | fontFamily: 'Helvetica Neue, Arial, sans-serif', 47 | textAlign: 'center' as const, 48 | fontWeight: 100, 49 | color: 'darkgrey', 50 | }; 51 | return ( 52 |
this.onParentRefMounted(ref)}> 53 |

54 | We need rest. The spirit is willing, but the flesh is spongy and bruised. Um, is 55 | this the boring, peaceful kind of taking to the streets? Bender, this is 56 | Fry’s decision… and he made it wrong. So it is time for us to interfere in 57 | his life. I am just glad my fat, ugly mama is not alive to see this day. Enough 58 | about your promiscuous mother, Hermes! We have bigger problems. I didn’t 59 | ask for a completely reasonable excuse! I asked you to get busy! Aww, it’s 60 | true. I’ve been hiding it for so long. Yes, I saw. You were doing well, 61 | until everyone died. Maybe I love you so much I love you no matter who you are 62 | pretending to be. Ow, my spirit! You’re going back for the Countess, 63 | aren’t you? No! Don’t jump! Oh, I think we should just stay friends. 64 | Take me to your leader! We’ll need to have a look inside you with this 65 | camera. Bender, we’re trying our best. Eeeee! Now say ”nuclear 66 | wessels”! Son, as your lawyer, I declare y’all are in a 12-piece 67 | bucket o’ trouble. But I done struck you a deal: Five hours of community 68 | service cleanin’ up that ol’ mess you caused. I love this planet! 69 | I’ve got wealth, fame, and access to the depths of sleaze that those 70 | things bring. I’ll get my kit! Tell them I hate them. So, how ’bout 71 | them Knicks? Oh, how I wish I could believe or understand that! There’s 72 | only one reasonable course of action now: kill Flexo! Large bet on myself in 73 | round one. Now what? Five hours? Aw, man! Couldn’t you just get me the 74 | death penalty? Ooh, name it after me! Really?! 75 |

76 | 77 |

78 | And I’d do it again! And perhaps a third time! But that would be it. 79 | Tell her she looks thin. What are their names? Tell her she looks thin. 80 | Anyhoo, your net-suits will allow you to experience Fry’s worm 81 | infested bowels as if you were actually wriggling through them. No, of 82 | course not. It was… uh… porno. Yeah, that’s it. I love this planet! 83 | I’ve got wealth, fame, and access to the depths of sleaze that those 84 | things bring. Shinier than yours, meatbag. We’re also Santa Claus! 85 | Bender, being God isn’t easy. If you do too much, people get dependent 86 | on you, and if you do nothing, they lose hope. You have to use a light 87 | touch. Like a safecracker, or a pickpocket. Bender, being God isn’t 88 | easy. If you do too much, people get dependent on you, and if you do 89 | nothing, they lose hope. You have to use a light touch. Like a safecracker, 90 | or a pickpocket. You’ve killed me! Oh, you’ve killed me! Yes, 91 | except the Dave Matthews Band doesn’t rock. Oh sure! Blame the 92 | wizards! And why did ’I’ have to take a cab? Oh sure! Blame the 93 | wizards! I don’t ’need’ to drink. I can quit anytime I 94 | want! Shut up and get to the point! I was all of history’s great robot 95 | actors - Acting Unit 0.8; Thespomat; David Duchovny! Ah, the 96 | ’Breakfast Club’ soundtrack! I can’t wait til I’m 97 | old enough to feel ways about stuff! I suppose I could part with 98 | ’one’ and still be feared… Fry, you can’t just sit here in 99 | the dark listening to classical music. I barely knew Philip, but as a 100 | clergyman I have no problem telling his most intimate friends all about him. 101 | Check it out, y’all. Everyone who was invited is here. Fry! Stay back! 102 | He’s too powerful! Why would a robot need to drink? Soothe us with 103 | sweet lies. But I know you in the future. I cleaned your poop. 104 |

105 |
106 |
107 | 108 |
109 |

110 | Too much work. Let’s burn it and say we dumped it in the sewer. 111 | You’ll have all the Slurm you can drink when you’re partying 112 | with Slurms McKenzie! It may comfort you to know that Fry’s death took 113 | only fifteen seconds, yet the pain was so intense, that it felt to him like 114 | fifteen years. And it goes without saying, it caused him to empty his 115 | bowels. Tell her you just want to talk. It has nothing to do with mating. I 116 | had more, but you go ahead. Ok, we’ll go deliver this crate like 117 | professionals, and then we’ll go ride the bumper cars. With gusto. 118 | There, now he’s trapped in a book I wrote: a crummy world of plot 119 | holes and spelling errors! Say it in Russian! Son, as your lawyer, I declare 120 | y’all are in a 12-piece bucket o’ trouble. But I done struck you 121 | a deal: Five hours of community service cleanin’ up that ol’ 122 | mess you caused. I just told you! You’ve killed me! You’re going 123 | to do his laundry? Kif, I have mated with a woman. Inform the men. The alien 124 | mothership is in orbit here. If we can hit that bullseye, the rest of the 125 | dominoes will fall like a house of cards. Checkmate. I’m a thing. Of 126 | all the friends I’ve had… you’re the first. Tell her you just 127 | want to talk. It has nothing to do with mating. Too much work. Let’s 128 | burn it and say we dumped it in the sewer. Yeah, I do that with my 129 | stupidness. 130 |

131 |
132 |
133 | ); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/kitchensink/p4.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState } from 'react'; 2 | import { Parallax } from '../index'; 3 | 4 | import image3 from '../assets/3.jpg'; 5 | 6 | type PageThreeProps = Record; 7 | 8 | const style = { 9 | backgroundColor: '#efefef', 10 | color: 'white', 11 | textAlign: 'center' as const, 12 | position: 'absolute' as const, 13 | top: 0, 14 | }; 15 | const fontStyle2 = { 16 | fontFamily: 'Helvetica Neue, Arial, sans-serif', 17 | textAlign: 'center' as const, 18 | fontWeight: 100, 19 | color: 'darkgrey', 20 | }; 21 | 22 | const PageFour: FC = () => { 23 | const [val, setVal] = useState(''); 24 | console.log('val outside', val); 25 | return ( 26 |
27 |

28 | We need rest. The spirit is willing, but the flesh is spongy and bruised. Um, is 29 | this the boring, peaceful kind of taking to the streets? Bender, this is Fry’s 30 | decision… and he made it wrong. So it is time for us to interfere in his life. I am 31 | just glad my fat, ugly mama is not alive to see this day. Enough about your 32 | promiscuous mother, Hermes! We have bigger problems. I didn’t ask for a 33 | completely reasonable excuse! I asked you to get busy! Aww, it’s true. 34 | I’ve been hiding it for so long. Yes, I saw. You were doing well, until 35 | everyone died. Maybe I love you so much I love you no matter who you are pretending 36 | to be. Ow, my spirit! You’re going back for the Countess, aren’t you? 37 | No! Don’t jump! Oh, I think we should just stay friends. Take me to your 38 | leader! We’ll need to have a look inside you with this camera. Bender, 39 | we’re trying our best. Eeeee! Now say ”nuclear wessels”! Son, as 40 | your lawyer, I declare y’all are in a 12-piece bucket o’ trouble. But I 41 | done struck you a deal: Five hours of community service cleanin’ up that 42 | ol’ mess you caused. I love this planet! I’ve got wealth, fame, and 43 | access to the depths of sleaze that those things bring. I’ll get my kit! Tell 44 | them I hate them. So, how ’bout them Knicks? Oh, how I wish I could believe or 45 | understand that! There’s only one reasonable course of action now: kill Flexo! 46 | Large bet on myself in round one. Now what? Five hours? Aw, man! Couldn’t you 47 | just get me the death penalty? Ooh, name it after me! Really?! 48 |

49 | 50 | setVal(e.target.value)} /> 51 |

52 | And I’d do it again! And perhaps a third time! But that would be it. Tell 53 | her she looks thin. What are their names? Tell her she looks thin. Anyhoo, your 54 | net-suits will allow you to experience Fry’s worm infested bowels as if 55 | you were actually wriggling through them. No, of course not. It was… uh… porno. 56 | Yeah, that’s it. I love this planet! I’ve got wealth, fame, and 57 | access to the depths of sleaze that those things bring. Shinier than yours, 58 | meatbag. We’re also Santa Claus! Bender, being God isn’t easy. If 59 | you do too much, people get dependent on you, and if you do nothing, they lose 60 | hope. You have to use a light touch. Like a safecracker, or a pickpocket. 61 | Bender, being God isn’t easy. If you do too much, people get dependent on 62 | you, and if you do nothing, they lose hope. You have to use a light touch. Like 63 | a safecracker, or a pickpocket. You’ve killed me! Oh, you’ve killed 64 | me! Yes, except the Dave Matthews Band doesn’t rock. Oh sure! Blame the 65 | wizards! And why did ’I’ have to take a cab? Oh sure! Blame the 66 | wizards! I don’t ’need’ to drink. I can quit anytime I want! 67 | Shut up and get to the point! I was all of history’s great robot actors - 68 | Acting Unit 0.8; Thespomat; David Duchovny! Ah, the ’Breakfast Club’ 69 | soundtrack! I can’t wait til I’m old enough to feel ways about 70 | stuff! I suppose I could part with ’one’ and still be feared… Fry, 71 | you can’t just sit here in the dark listening to classical music. I barely 72 | knew Philip, but as a clergyman I have no problem telling his most intimate 73 | friends all about him. Check it out, y’all. Everyone who was invited is 74 | here. Fry! Stay back! He’s too powerful! Why would a robot need to drink? 75 | Soothe us with sweet lies. But I know you in the future. I cleaned your poop. 76 |

77 |
78 |
79 |
80 | ); 81 | }; 82 | 83 | export default PageFour; 84 | -------------------------------------------------------------------------------- /src/kitchensink/p5.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Parallax } from '../index'; 3 | 4 | const style = { 5 | backgroundColor: '#efefef', 6 | color: 'white', 7 | textAlign: 'center' as const, 8 | }; 9 | 10 | export const Page5 = (): JSX.Element => { 11 | return ( 12 |
13 |
14 | ( 18 |
27 | {percentage} 28 |
29 | )} 30 | > 31 |
32 | 33 |
34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/modules/Background.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Background as BackgroundClass } from '../../@types'; 4 | 5 | class Background extends BackgroundClass { 6 | static defaultProps = { 7 | className: '', 8 | }; 9 | 10 | static isParallaxBackground = true; 11 | 12 | render(): JSX.Element { 13 | const { className, children } = this.props; 14 | return
{children}
; 15 | } 16 | } 17 | 18 | export default Background; 19 | -------------------------------------------------------------------------------- /src/modules/Parallax.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { 4 | ParallaxProps, 5 | BgImageProp, 6 | BgImageSrcSetProp, 7 | BgImageSizesProp, 8 | Parallax as ParallaxClass, 9 | SplitChildrenResultType, 10 | StyleObjectType, 11 | } from '../../@types'; 12 | 13 | import { 14 | getRelativePosition, 15 | getSplitChildren, 16 | getHasDynamicBlur, 17 | getBlurValue, 18 | } from '../utils/parallax'; 19 | import { getNodeHeight, canUseDOM, isScrolledIntoView } from '../utils/dom'; 20 | import ParallaxChildren from './ParallaxChildren'; 21 | 22 | const initialStyle = { 23 | position: 'absolute', 24 | left: '50%', 25 | WebkitTransform: 'translate3d(-50%, 0, 0)', 26 | transform: 'translate3d(-50%, 0, 0)', 27 | WebkitTransformStyle: 'preserve-3d', 28 | WebkitBackfaceVisibility: 'hidden', 29 | MozBackfaceVisibility: 'hidden', 30 | MsBackfaceVisibility: 'hidden', 31 | }; 32 | 33 | class Parallax extends ParallaxClass { 34 | bg: HTMLDivElement; 35 | 36 | canUseDOM: boolean; 37 | 38 | contentHeight: number; 39 | 40 | contentWidth: number; 41 | 42 | node: HTMLElement; 43 | 44 | content: HTMLElement; 45 | 46 | img: HTMLImageElement; 47 | 48 | bgImageLoaded: boolean; 49 | 50 | bgImageRef: HTMLImageElement; 51 | 52 | parent: HTMLElement | Document; 53 | 54 | parentHeight: number; 55 | 56 | timestamp: number; 57 | 58 | isDynamicBlur: boolean; 59 | 60 | static defaultProps = { 61 | bgClassName: 'react-parallax-bgimage', 62 | bgImageAlt: '', 63 | className: '', 64 | contentClassName: '', 65 | disabled: false, 66 | strength: 100, 67 | }; 68 | 69 | constructor(props: ParallaxProps) { 70 | super(props); 71 | 72 | this.state = { 73 | bgImage: props.bgImage, 74 | bgImageSrcSet: props.bgImageSrcSet, 75 | bgImageSizes: props.bgImageSizes, 76 | imgStyle: initialStyle, 77 | bgStyle: { 78 | ...initialStyle, 79 | ...props.bgStyle, 80 | }, 81 | percentage: 0, 82 | splitChildren: getSplitChildren(props), 83 | }; 84 | 85 | this.canUseDOM = canUseDOM(); 86 | 87 | this.node = null; 88 | this.content = null; 89 | this.bgImageLoaded = false; 90 | this.bgImageRef = undefined; 91 | 92 | this.parent = props.parent; 93 | this.parentHeight = getNodeHeight(this.canUseDOM, this.parent); 94 | this.timestamp = Date.now(); 95 | this.isDynamicBlur = getHasDynamicBlur(props.blur); 96 | } 97 | 98 | /** 99 | * bind some eventlisteners for page load, scroll and resize 100 | * save component ref after rendering, update all values and set static style values 101 | */ 102 | componentDidMount(): void { 103 | const { parent } = this.props; 104 | const { bgImage, bgImageSrcSet, bgImageSizes } = this.state; 105 | 106 | this.parent = parent || document; 107 | this.addListeners(); 108 | // ref to component itself 109 | 110 | if (bgImage) { 111 | this.loadImage(bgImage, bgImageSrcSet, bgImageSizes); 112 | } else { 113 | this.updatePosition(); 114 | } 115 | } 116 | 117 | static getDerivedStateFromProps( 118 | props: ParallaxProps, 119 | ): { 120 | splitChildren: SplitChildrenResultType; 121 | } { 122 | return { 123 | splitChildren: getSplitChildren(props), 124 | }; 125 | } 126 | 127 | componentDidUpdate(prevProps: ParallaxProps): void { 128 | const { parent, bgImage, bgImageSrcSet, bgImageSizes } = this.props; 129 | const { bgImage: stateBgImage } = this.state; 130 | 131 | if (prevProps.parent !== parent) { 132 | this.removeListeners(this.parent); 133 | this.parent = parent; 134 | if (parent) { 135 | this.addListeners(); 136 | } 137 | } 138 | 139 | this.parentHeight = getNodeHeight(this.canUseDOM, this.parent); 140 | 141 | if (stateBgImage !== bgImage) { 142 | this.loadImage(bgImage, bgImageSrcSet, bgImageSizes); 143 | } 144 | } 145 | 146 | /** 147 | * remove all eventlisteners before component is destroyed 148 | */ 149 | componentWillUnmount(): void { 150 | this.removeListeners(this.parent); 151 | this.releaseImage(); 152 | } 153 | 154 | /** 155 | * update window height and positions on window resize 156 | */ 157 | onWindowResize = (): void => { 158 | this.parentHeight = getNodeHeight(this.canUseDOM, this.parent); 159 | this.updatePosition(); 160 | }; 161 | 162 | onWindowLoad = (): void => { 163 | this.updatePosition(); 164 | }; 165 | 166 | onScroll = (): void => { 167 | if (!this.canUseDOM) { 168 | return; 169 | } 170 | const stamp = Date.now(); 171 | if (stamp - this.timestamp >= 10 && isScrolledIntoView(this.node, 100, this.canUseDOM)) { 172 | window.requestAnimationFrame(this.updatePosition); 173 | this.timestamp = stamp; 174 | } 175 | }; 176 | 177 | onContentMount = (content: HTMLElement): void => { 178 | this.content = content; 179 | }; 180 | 181 | setBackgroundPosition(percentage: number): void { 182 | const { disabled, strength } = this.props; 183 | const bgStyle: StyleObjectType = { 184 | ...this.state.bgStyle, 185 | }; 186 | 187 | if (!disabled) { 188 | const inverse = strength < 0; 189 | const pos = (inverse ? strength : 0) - strength * percentage; 190 | const transform = `translate3d(-50%, ${pos}px, 0)`; 191 | bgStyle.WebkitTransform = transform; 192 | bgStyle.transform = transform; 193 | } 194 | 195 | this.setState({ 196 | bgStyle, 197 | percentage, 198 | }); 199 | } 200 | 201 | /** 202 | * sets position for the background image 203 | */ 204 | setImagePosition(percentage: number, autoHeight = false): void { 205 | const { disabled, strength, blur } = this.props; 206 | const height = autoHeight ? 'auto' : `${this.getImageHeight()}px`; 207 | const width = !autoHeight ? 'auto' : `${this.contentWidth}px`; 208 | const imgStyle: StyleObjectType = { 209 | ...this.state.imgStyle, 210 | height, 211 | width, 212 | }; 213 | 214 | if (!disabled) { 215 | const inverse = strength < 0; 216 | const pos = (inverse ? strength : 0) - strength * percentage; 217 | 218 | const transform = `translate3d(-50%, ${pos}px, 0)`; 219 | let filter = 'none'; 220 | if (blur) { 221 | filter = `blur(${getBlurValue(this.isDynamicBlur, blur, percentage)}px)`; 222 | } 223 | imgStyle.WebkitTransform = transform; 224 | imgStyle.transform = transform; 225 | imgStyle.WebkitFilter = filter; 226 | imgStyle.filter = filter; 227 | } 228 | 229 | this.setState({ 230 | imgStyle, 231 | percentage, 232 | }); 233 | } 234 | 235 | /** 236 | * The image height depends on parallax direction. If strength value is negative we have to give it more height 237 | * so there is no white space at start/end of container visiblility. 238 | */ 239 | getImageHeight(): number { 240 | const { strength } = this.props; 241 | const inverse = strength < 0; 242 | const factor = inverse ? 2.5 : 1; 243 | const strengthWithFactor = factor * Math.abs(strength); 244 | return Math.floor(this.contentHeight + strengthWithFactor); 245 | } 246 | 247 | /** 248 | * updates scroll position of this component and also its width and height. 249 | * defines, if the background image should have autoHeight or autoWidth to 250 | * fit the component space optimally 251 | */ 252 | updatePosition = (): void => { 253 | if (!this.content) { 254 | return; 255 | } 256 | let autoHeight = false; 257 | this.contentHeight = this.content.getBoundingClientRect().height; 258 | this.contentWidth = this.node.getBoundingClientRect().width; 259 | 260 | // set autoHeight or autoWidth 261 | if ( 262 | this.img && 263 | this.img.naturalWidth / this.img.naturalHeight < 264 | this.contentWidth / this.getImageHeight() 265 | ) { 266 | autoHeight = true; 267 | } 268 | 269 | // get relative scroll-y position of parallax component in percentage 270 | const percentage = getRelativePosition(this.node, this.canUseDOM); 271 | const hasBgImage = !!this.img; 272 | const hasBgChildren = this.bg && this.state.splitChildren.bgChildren.length > 0; 273 | 274 | // update bg image position if set 275 | if (hasBgImage) { 276 | this.setImagePosition(percentage, autoHeight); 277 | } 278 | // update position of Background children if exist 279 | if (hasBgChildren) { 280 | this.setBackgroundPosition(percentage); 281 | } 282 | 283 | // be sure to set the percentage if neither image nor bg component was set 284 | if (!hasBgImage && !hasBgChildren) { 285 | this.setState({ percentage }); 286 | } 287 | }; 288 | 289 | /** 290 | * Makes sure that the image was loaded before render 291 | */ 292 | loadImage( 293 | bgImage: BgImageProp, 294 | bgImageSrcSet: BgImageSrcSetProp, 295 | bgImageSizes: BgImageSizesProp, 296 | ): void { 297 | this.releaseImage(); 298 | this.bgImageRef = new Image(); 299 | this.bgImageRef.onload = (e) => { 300 | this.setState( 301 | { 302 | bgImage, 303 | bgImageSrcSet, 304 | bgImageSizes, 305 | }, 306 | () => this.updatePosition(), 307 | ); 308 | if (this.props.onLoad) { 309 | this.props.onLoad(e); 310 | } 311 | }; 312 | this.bgImageRef.onerror = this.bgImageRef.onload; 313 | this.bgImageRef.src = bgImage; 314 | this.bgImageRef.srcset = bgImageSrcSet || ''; 315 | this.bgImageRef.sizes = bgImageSizes || ''; 316 | } 317 | 318 | /** 319 | * Unbind eventlistener of bg image and delete it 320 | */ 321 | releaseImage(): void { 322 | if (this.bgImageRef) { 323 | this.bgImageRef.onload = null; 324 | this.bgImageRef.onerror = null; 325 | delete this.bgImageRef; 326 | } 327 | } 328 | 329 | addListeners(): void { 330 | if (this.canUseDOM && this.parent) { 331 | this.parent.addEventListener('scroll', this.onScroll, false); 332 | window.addEventListener('resize', this.onWindowResize, false); 333 | window.addEventListener('load', this.onWindowLoad, false); 334 | } 335 | } 336 | 337 | removeListeners(parent?: HTMLElement | Document): void { 338 | if (this.canUseDOM) { 339 | if (parent) { 340 | parent.removeEventListener('scroll', this.onScroll, false); 341 | } 342 | window.removeEventListener('resize', this.onWindowResize, false); 343 | window.removeEventListener('load', this.onWindowLoad, false); 344 | } 345 | } 346 | 347 | render(): JSX.Element { 348 | const { 349 | className, 350 | style, 351 | bgClassName, 352 | contentClassName, 353 | bgImageAlt, 354 | renderLayer, 355 | bgImageStyle, 356 | lazy, 357 | } = this.props; 358 | const { 359 | bgImage, 360 | bgImageSrcSet, 361 | bgImageSizes, 362 | percentage, 363 | imgStyle, 364 | bgStyle, 365 | splitChildren, 366 | } = this.state; 367 | return ( 368 |
{ 372 | this.node = node; 373 | }} 374 | > 375 | {bgImage ? ( 376 | { 382 | this.img = bg; 383 | }} 384 | alt={bgImageAlt} 385 | style={{ ...imgStyle, ...bgImageStyle }} 386 | loading={lazy ? 'lazy' : 'eager'} 387 | /> 388 | ) : null} 389 | {renderLayer ? renderLayer(-(percentage - 1)) : null} 390 | {splitChildren.bgChildren.length > 0 ? ( 391 |
{ 394 | this.bg = bg; 395 | }} 396 | style={bgStyle} 397 | > 398 | {splitChildren.bgChildren} 399 |
400 | ) : null} 401 | 402 | {splitChildren.children} 403 | 404 |
405 | ); 406 | } 407 | } 408 | 409 | export default Parallax; 410 | -------------------------------------------------------------------------------- /src/modules/ParallaxChildren.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { ParallaxChildrenProps } from '../../@types'; 4 | 5 | const ParallaxChildren: React.SFC = ({ children, onMount, className }) => ( 6 |
onMount(node)} 8 | className={className || 'react-parallax-content'} 9 | style={{ position: 'relative' }} 10 | > 11 | {children} 12 |
13 | ); 14 | 15 | export default ParallaxChildren; 16 | -------------------------------------------------------------------------------- /src/utils/dom.test.ts: -------------------------------------------------------------------------------- 1 | import { getWindowHeight } from './dom'; 2 | 3 | describe('Utils.dom', () => { 4 | test('getWindowHeight', () => { 5 | window.resizeTo(1000, 1000); 6 | expect(getWindowHeight(true)).toEqual(1000); 7 | window.resizeTo(1100, 768); 8 | expect(getWindowHeight(true)).toEqual(768); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/utils/dom.ts: -------------------------------------------------------------------------------- 1 | export const getWindowHeight = (useDOM: boolean): number => { 2 | if (!useDOM) { 3 | return 0; 4 | } 5 | const w = window; 6 | const d = document; 7 | const e = d.documentElement; 8 | const g = d.getElementsByTagName('body')[0]; 9 | 10 | return w.innerHeight || e.clientHeight || g.clientHeight; 11 | }; 12 | 13 | export const isScrolledIntoView = (element: HTMLElement, offset = 0, useDOM: boolean): boolean => { 14 | if (!useDOM) { 15 | return false; 16 | } 17 | const elementTop = element.getBoundingClientRect().top - offset; 18 | const elementBottom = element.getBoundingClientRect().bottom + offset; 19 | return elementTop <= getWindowHeight(useDOM) && elementBottom >= 0; 20 | }; 21 | 22 | export const getNodeHeight = (useDOM: boolean, node?: HTMLElement | Document): number => { 23 | if (!useDOM) { 24 | return 0; 25 | } 26 | 27 | if (!node || !('clientHeight' in node)) { 28 | return getWindowHeight(useDOM); 29 | } 30 | 31 | return node.clientHeight; 32 | }; 33 | 34 | export const canUseDOM = (): boolean => { 35 | return !!(typeof window !== 'undefined' && window.document && window.document.createElement); 36 | }; 37 | -------------------------------------------------------------------------------- /src/utils/parallax.test.ts: -------------------------------------------------------------------------------- 1 | import { getPercentage } from './parallax'; 2 | 3 | describe('Utils.parallax', () => { 4 | test('getPercentage', () => { 5 | expect(getPercentage(0, 200, 50)).toEqual(0.25); 6 | expect(getPercentage(0, 0, 0)).toEqual(0); 7 | expect(getPercentage(0, 100, 170)).toEqual(1.7); 8 | expect(getPercentage(0, 100, -100)).toEqual(-1); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/utils/parallax.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode, Children } from 'react'; 2 | import { BlurProp, DynamicBlurProp, SplitChildrenResultType } from '../../@types'; 3 | import { getNodeHeight } from './dom'; 4 | 5 | export const getPercentage = (startpos: number, endpos: number, currentpos: number): number => { 6 | const distance = endpos - startpos; 7 | const displacement = currentpos - startpos; 8 | return displacement / distance || 0; 9 | }; 10 | 11 | export const getRelativePosition = (node: HTMLElement, useDOM: boolean): number => { 12 | if (!useDOM) { 13 | return 0; 14 | } 15 | const { top, height } = node.getBoundingClientRect(); 16 | const parentHeight = getNodeHeight(useDOM); 17 | const maxHeight = height > parentHeight ? height : parentHeight; 18 | const y = Math.round(top > maxHeight ? maxHeight : top); 19 | 20 | return getPercentage(0, maxHeight, y); 21 | }; 22 | 23 | interface SplitChildrenProps { 24 | children?: ReactNode; 25 | } 26 | 27 | /** 28 | * Extracts children with type Background from others and returns an object with both arrays: 29 | * { 30 | * bgChildren: bgChildren, // typeof child === 'Background' 31 | * children: children // rest of this.props.children 32 | * } 33 | */ 34 | export const getSplitChildren = (props: SplitChildrenProps): SplitChildrenResultType => { 35 | let bgChildren: Array = []; 36 | 37 | const children = Children.toArray(props.children); 38 | children.forEach((child, index) => { 39 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 40 | const c = child as any; 41 | if (c.type && c.type.isParallaxBackground) { 42 | bgChildren = bgChildren.concat(children.splice(index, 1)); 43 | } 44 | }); 45 | return { 46 | bgChildren, 47 | children, 48 | }; 49 | }; 50 | 51 | export const getHasDynamicBlur = (blur: BlurProp): boolean => 52 | typeof blur === 'object' && blur.min !== undefined && blur.max !== undefined; 53 | 54 | export const getBlurValue = ( 55 | isDynamicBlur: boolean, 56 | blur: BlurProp, 57 | percentage: number, 58 | ): BlurProp => { 59 | return isDynamicBlur 60 | ? (blur as DynamicBlurProp).min + (1 - percentage) * (blur as DynamicBlurProp).max 61 | : blur; 62 | }; 63 | 64 | export const setBlur = (node: HTMLElement, blur: number): void => { 65 | // eslint-disable-next-line no-param-reassign 66 | node.style.webkitFilter = `blur(${blur}px)`; 67 | // eslint-disable-next-line no-param-reassign 68 | node.style.filter = `blur(${blur}px)`; 69 | }; 70 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["babel.config.js", "src/**/*.ts", "src/**/*.tsx", ".eslintrc.js", "jest.config.js"], 4 | "exclude": ["node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./lib/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "jsx": "react", 7 | "esModuleInterop": true, 8 | "noUnusedLocals": true, 9 | "moduleResolution": "node", 10 | "lib": ["dom", "es2015", "es2016"], 11 | "target": "es6", 12 | "module": "commonjs", 13 | "noUnusedParameters": true, 14 | "skipLibCheck": true, 15 | "isolatedModules": true, 16 | "baseUrl": "./src", 17 | "paths": { 18 | "~*": ["./*"] 19 | } 20 | }, 21 | "include": ["src", "@types"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /webpack/base.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolve: { 3 | extensions: ['*', '.js', '.jsx', '.tsx', '.ts'], 4 | }, 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.(js|jsx|tsx|ts)$/, 9 | exclude: /node_modules/, 10 | use: ['babel-loader', 'ts-loader'], 11 | }, 12 | { 13 | test: /\.(jpe?g|png)(\?[a-z0-9=&.]+)?$/, 14 | exclude: /inline/, 15 | use: [ 16 | { 17 | loader: 'file-loader', 18 | options: { 19 | name: 'img/[name]_[hash].[ext]', 20 | }, 21 | }, 22 | ], 23 | }, 24 | ], 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /webpack/build.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const TerserPlugin = require('terser-webpack-plugin'); 3 | const { merge } = require('webpack-merge'); 4 | 5 | const common = require('./base.config.js'); 6 | 7 | module.exports = merge(common, { 8 | entry: path.resolve(__dirname, '../src'), 9 | output: { 10 | path: path.resolve(__dirname, '../lib/'), 11 | filename: 'index.js', 12 | library: 'react-parallax', 13 | libraryTarget: 'umd', 14 | umdNamedDefine: true, 15 | globalObject: `(typeof self !== 'undefined' ? self : this)`, 16 | }, 17 | optimization: { 18 | minimize: true, 19 | minimizer: [ 20 | new TerserPlugin({ 21 | cache: true, 22 | parallel: true, 23 | sourceMap: false, 24 | terserOptions: { keep_fnames: true }, 25 | }), 26 | ], 27 | }, 28 | externals: { 29 | react: 'react', 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /webpack/dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { merge } = require('webpack-merge'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | const common = require('./base.config.js'); 6 | 7 | module.exports = merge(common, { 8 | entry: ['react-hot-loader/patch', path.resolve(__dirname, '../src/kitchensink/index.tsx')], 9 | output: { 10 | path: path.resolve(__dirname, 'www'), 11 | filename: 'bundle.js', 12 | }, 13 | devtool: 'eval-source-map', 14 | devServer: { 15 | host: process.env.HOST || 'localhost', 16 | port: 3000, 17 | disableHostCheck: true, 18 | historyApiFallback: true, 19 | hot: true, 20 | inline: true, 21 | }, 22 | stats: 'minimal', 23 | plugins: [ 24 | new HtmlWebpackPlugin({ 25 | template: './src/kitchensink/index.html', 26 | inject: 'body', 27 | }), 28 | ], 29 | }); 30 | --------------------------------------------------------------------------------