├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .prettierrc ├── README.md ├── docz ├── doczrc.js ├── package-lock.json ├── package.json ├── postinstall.sh ├── public │ ├── sky-square.jpg │ ├── sky-wide.jpg │ ├── tailwind.min.css │ └── zurich.jpg ├── src │ ├── images.js │ ├── pages │ │ ├── GettingStarted │ │ │ ├── Simple.tsx │ │ │ ├── curve.mdx │ │ │ └── intro.mdx │ │ ├── Player │ │ │ ├── Player.tsx │ │ │ ├── PlayerFull.tsx │ │ │ ├── PlayerMini.tsx │ │ │ └── player.mdx │ │ ├── Progress │ │ │ └── progress.mdx- │ │ ├── Tests │ │ │ └── toggle.mdx- │ │ ├── Travel │ │ │ ├── Travel.tsx │ │ │ ├── tavel.mdx │ │ │ └── travel.css │ │ ├── api │ │ │ ├── useFade.mdx │ │ │ ├── useMorphKeys.mdx │ │ │ └── useMorphList.mdx │ │ ├── demos │ │ │ └── docs.mdx │ │ ├── options.mdx │ │ └── styling │ │ │ ├── styling.js │ │ │ └── styling.mdx │ └── utilities.css └── tsconfig.json ├── examples ├── react-morph-simple.gif └── react-morph.gif ├── lib ├── Morph.d.ts ├── Morph.js ├── Morph.js.map ├── easings.d.ts ├── easings.js ├── easings.js.map ├── index.d.ts ├── index.js ├── index.js.map ├── morphTransition.d.ts ├── morphTransition.js ├── morphTransition.js.map ├── presets.d.ts ├── presets.js ├── presets.js.map ├── useFade.d.ts ├── useFade.js ├── useFade.js.map ├── useMorph.d.ts ├── useMorph.js ├── useMorph.js.map ├── useMorphKeys.d.ts ├── useMorphKeys.js ├── useMorphKeys.js.map ├── useMorphList.d.ts ├── useMorphList.js ├── useMorphList.js.map ├── useMorphs.d.ts ├── useMorphs.js ├── useMorphs.js.map ├── util.d.ts ├── util.js └── util.js.map ├── package-lock.json ├── package.json ├── src ├── Morph.tsx ├── bck_Morph.jsx ├── easings.ts ├── index.ts ├── morphTransition.ts ├── presets.ts ├── types.d.ts ├── useFade.ts ├── useMorph.ts ├── useMorphList.ts ├── useMorphs.ts └── util.ts └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@babel/env"]], 3 | "plugins": [ 4 | ["transform-class-properties"], 5 | ["transform-regenerator"], 6 | ["transform-object-rest-spread", { "useBuiltIns": true }] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | indent_style = space 2 | indent_size = 2 3 | 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib 2 | **/node_modules 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | parserOptions: { parser: "@typescript-eslint/parser" }, 3 | "extends": 4 | [ 5 | "airbnb-base", 6 | "plugin:@typescript-eslint/recommended", 7 | "prettier", 8 | "prettier/@typescript-eslint", 9 | ], 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Directory for instrumented libs generated by jscoverage/JSCover 7 | lib-cov 8 | 9 | # Coverage directory used by tools like istanbul 10 | coverage 11 | 12 | # Dependency directories 13 | node_modules/ 14 | jspm_packages/ 15 | 16 | # Typescript v1 declaration files 17 | typings/ 18 | 19 | # Optional npm cache directory 20 | .npm 21 | 22 | # Optional eslint cache 23 | .eslintcache 24 | 25 | # Optional REPL history 26 | .node_repl_history 27 | 28 | # Output of 'npm pack' 29 | *.tgz 30 | 31 | # Yarn Integrity file 32 | .yarn-integrity 33 | 34 | # dotenv environment variables file 35 | .env 36 | .cache 37 | 38 | .DS_Store 39 | 40 | .docz 41 | docz/.docz 42 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | examples 3 | docz 4 | src 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Morph ️🦋 2 | 3 | #### Morphing UI transitions made simple 4 | 5 | [![npm version](https://badge.fury.io/js/react-morph.svg?v0)](https://www.npmjs.com/package/react-morph) 6 | 7 | 8 | 9 | Magically animates one element into another just by tagging the first and last state. 10 | 11 | ## Getting Started 🐛 12 | 13 | ```sh 14 | npm install react-morph 15 | # or 16 | yarn add react-morph 17 | ``` 18 | 19 | Import the `useMorph` hook. 20 | 21 | ```js 22 | const morph = useMorph(options); 23 | ``` 24 | 25 | Then spread the props to the elements you want to morph. 26 | 27 | ```jsx 28 | 29 | ``` 30 | 31 | ```jsx 32 | 33 | ``` 34 | 35 | > Make sure you have just **ONE element rendered at same time**. 36 | 37 | ## Simple Example 🦋 38 | 39 | 1. Create two states as you normally would (HTML + CSS). 40 | 2. Call `useMorph` hook. 41 | 3. Spread the elements you want to morph with `{...morph}` 42 | 4. Add and remove the element from the DOM 43 | 44 | ```js 45 | import React from 'react'; 46 | import { useMorph } from 'react-morph'; 47 | ``` 48 | 49 | ```jsx 50 | () => { 51 | // Handle toggle state as you normally would. 52 | const [toggle, setToggle] = useState(true); 53 | const morph = useMorph(); 54 | 55 | return ( 56 |
57 | 58 |
59 | 60 | {toggle ? ( 61 | 62 | ) : ( 63 | 64 | )} 65 |
66 | ); 67 | }; 68 | ``` 69 | 70 | ## [Documentation](https://brunnolou.github.io/react-morph) 71 | 72 | Please check the [documentation](https://brunnolou.github.io/react-morph). 73 | 74 | ## Features 🌟 75 | 76 | - Simplicity 77 | - No hardcoded absolute positions 78 | - All GPU accelerated props 79 | - No layout or paint browser rendering 80 | 81 | ## Live Demos 82 | 83 | - [Hello world](https://codesandbox.io/s/yqyymqn8z1) 84 | - [Apple App Store](https://codesandbox.io/s/7ywk4o0xmj) 85 | -------------------------------------------------------------------------------- /docz/doczrc.js: -------------------------------------------------------------------------------- 1 | import { css } from 'docz-plugin-css'; 2 | 3 | export default { 4 | title: 'React Morph - Docs', 5 | base: '/react-morph/', 6 | typescript: true, 7 | files: './**/*.{md,markdown,mdx}', 8 | public: './public', 9 | menu: [ 10 | { name: 'Getting Started' }, 11 | { name: 'Styling' }, 12 | { name: 'Api' }, 13 | ], 14 | codeSandbox: false, 15 | plugins: [css({ preprocessor: 'postcss' })], 16 | themeConfig: { 17 | styles: { 18 | container: { 19 | width: ['100%', '100%', 1920], 20 | padding: ['20px', '0 40px 40px'], 21 | }, 22 | }, 23 | ordering: 'ascending', 24 | colors: { 25 | primary: '#00C0FF', 26 | }, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /docz/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-morph-docz", 3 | "version": "0.4.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "postinstall": "./postinstall.sh", 8 | "watch-lib": "onchange '../lib/**/*.js' -- npm run postinstall", 9 | "dev": "docz dev && npm run watch-lib", 10 | "deploy": "gh-pages -d .docz/dist", 11 | "build": "docz build" 12 | }, 13 | "keywords": [], 14 | "dependencies": { 15 | "@types/react-router-dom": "^4.3.2", 16 | "@types/styled-components": "^4.1.14", 17 | "docz-plugin-css": "^0.11.0", 18 | "react": "^16.8.6", 19 | "react-dom": "^16.8.6", 20 | "react-router-dom": "^5.0.0", 21 | "styled-components": "^4.2.0" 22 | }, 23 | "devDependencies": { 24 | "@types/react": "^16.8.13", 25 | "@types/react-dom": "^16.8.3", 26 | "docz": "^1.0.0", 27 | "docz-theme-default": "^1.0.0", 28 | "onchange": "^5.2.0" 29 | }, 30 | "homepage": "https://brunnolou.github.io/react-morph", 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/brunnolou/react-morph.git" 34 | }, 35 | "contributors": [ 36 | "Bruno Lourenço" 37 | ], 38 | "license": "MIT", 39 | "bugs": { 40 | "url": "https://github.com/brunnolou/react-morph/issues" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docz/postinstall.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ./node_modules/react-morph 2 | cp ../package.json ./node_modules/react-morph 3 | cp -r ../lib/* ./node_modules/react-morph 4 | -------------------------------------------------------------------------------- /docz/public/sky-square.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunnolou/react-morph/f277f639213bc32d46bc918e79deb989d8d52e6b/docz/public/sky-square.jpg -------------------------------------------------------------------------------- /docz/public/sky-wide.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunnolou/react-morph/f277f639213bc32d46bc918e79deb989d8d52e6b/docz/public/sky-wide.jpg -------------------------------------------------------------------------------- /docz/public/zurich.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunnolou/react-morph/f277f639213bc32d46bc918e79deb989d8d52e6b/docz/public/zurich.jpg -------------------------------------------------------------------------------- /docz/src/pages/GettingStarted/Simple.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useMorph } from 'react-morph/'; 3 | 4 | const logo = 5 | 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA4NDEuOSA1OTUuMyI+ICAgIDxnIGZpbGw9IiM2MURBRkIiPiAgICAgICAgPHBhdGggZD0iTTY2Ni4zIDI5Ni41YzAtMzIuNS00MC43LTYzLjMtMTAzLjEtODIuNCAxNC40LTYzLjYgOC0xMTQuMi0yMC4yLTEzMC40LTYuNS0zLjgtMTQuMS01LjYtMjIuNC01LjZ2MjIuM2M0LjYgMCA4LjMuOSAxMS40IDIuNiAxMy42IDcuOCAxOS41IDM3LjUgMTQuOSA3NS43LTEuMSA5LjQtMi45IDE5LjMtNS4xIDI5LjQtMTkuNi00LjgtNDEtOC41LTYzLjUtMTAuOS0xMy41LTE4LjUtMjcuNS0zNS4zLTQxLjYtNTAgMzIuNi0zMC4zIDYzLjItNDYuOSA4NC00Ni45Vjc4Yy0yNy41IDAtNjMuNSAxOS42LTk5LjkgNTMuNi0zNi40LTMzLjgtNzIuNC01My4yLTk5LjktNTMuMnYyMi4zYzIwLjcgMCA1MS40IDE2LjUgODQgNDYuNi0xNCAxNC43LTI4IDMxLjQtNDEuMyA0OS45LTIyLjYgMi40LTQ0IDYuMS02My42IDExLTIuMy0xMC00LTE5LjctNS4yLTI5LTQuNy0zOC4yIDEuMS02Ny45IDE0LjYtNzUuOCAzLTEuOCA2LjktMi42IDExLjUtMi42Vjc4LjVjLTguNCAwLTE2IDEuOC0yMi42IDUuNi0yOC4xIDE2LjItMzQuNCA2Ni43LTE5LjkgMTMwLjEtNjIuMiAxOS4yLTEwMi43IDQ5LjktMTAyLjcgODIuMyAwIDMyLjUgNDAuNyA2My4zIDEwMy4xIDgyLjQtMTQuNCA2My42LTggMTE0LjIgMjAuMiAxMzAuNCA2LjUgMy44IDE0LjEgNS42IDIyLjUgNS42IDI3LjUgMCA2My41LTE5LjYgOTkuOS01My42IDM2LjQgMzMuOCA3Mi40IDUzLjIgOTkuOSA1My4yIDguNCAwIDE2LTEuOCAyMi42LTUuNiAyOC4xLTE2LjIgMzQuNC02Ni43IDE5LjktMTMwLjEgNjItMTkuMSAxMDIuNS00OS45IDEwMi41LTgyLjN6bS0xMzAuMi02Ni43Yy0zLjcgMTIuOS04LjMgMjYuMi0xMy41IDM5LjUtNC4xLTgtOC40LTE2LTEzLjEtMjQtNC42LTgtOS41LTE1LjgtMTQuNC0yMy40IDE0LjIgMi4xIDI3LjkgNC43IDQxIDcuOXptLTQ1LjggMTA2LjVjLTcuOCAxMy41LTE1LjggMjYuMy0yNC4xIDM4LjItMTQuOSAxLjMtMzAgMi00NS4yIDItMTUuMSAwLTMwLjItLjctNDUtMS45LTguMy0xMS45LTE2LjQtMjQuNi0yNC4yLTM4LTcuNi0xMy4xLTE0LjUtMjYuNC0yMC44LTM5LjggNi4yLTEzLjQgMTMuMi0yNi44IDIwLjctMzkuOSA3LjgtMTMuNSAxNS44LTI2LjMgMjQuMS0zOC4yIDE0LjktMS4zIDMwLTIgNDUuMi0yIDE1LjEgMCAzMC4yLjcgNDUgMS45IDguMyAxMS45IDE2LjQgMjQuNiAyNC4yIDM4IDcuNiAxMy4xIDE0LjUgMjYuNCAyMC44IDM5LjgtNi4zIDEzLjQtMTMuMiAyNi44LTIwLjcgMzkuOXptMzIuMy0xM2M1LjQgMTMuNCAxMCAyNi44IDEzLjggMzkuOC0xMy4xIDMuMi0yNi45IDUuOS00MS4yIDggNC45LTcuNyA5LjgtMTUuNiAxNC40LTIzLjcgNC42LTggOC45LTE2LjEgMTMtMjQuMXpNNDIxLjIgNDMwYy05LjMtOS42LTE4LjYtMjAuMy0yNy44LTMyIDkgLjQgMTguMi43IDI3LjUuNyA5LjQgMCAxOC43LS4yIDI3LjgtLjctOSAxMS43LTE4LjMgMjIuNC0yNy41IDMyem0tNzQuNC01OC45Yy0xNC4yLTIuMS0yNy45LTQuNy00MS03LjkgMy43LTEyLjkgOC4zLTI2LjIgMTMuNS0zOS41IDQuMSA4IDguNCAxNiAxMy4xIDI0IDQuNyA4IDkuNSAxNS44IDE0LjQgMjMuNHpNNDIwLjcgMTYzYzkuMyA5LjYgMTguNiAyMC4zIDI3LjggMzItOS0uNC0xOC4yLS43LTI3LjUtLjctOS40IDAtMTguNy4yLTI3LjguNyA5LTExLjcgMTguMy0yMi40IDI3LjUtMzJ6bS03NCA1OC45Yy00LjkgNy43LTkuOCAxNS42LTE0LjQgMjMuNy00LjYgOC04LjkgMTYtMTMgMjQtNS40LTEzLjQtMTAtMjYuOC0xMy44LTM5LjggMTMuMS0zLjEgMjYuOS01LjggNDEuMi03Ljl6bS05MC41IDEyNS4yYy0zNS40LTE1LjEtNTguMy0zNC45LTU4LjMtNTAuNiAwLTE1LjcgMjIuOS0zNS42IDU4LjMtNTAuNiA4LjYtMy43IDE4LTcgMjcuNy0xMC4xIDUuNyAxOS42IDEzLjIgNDAgMjIuNSA2MC45LTkuMiAyMC44LTE2LjYgNDEuMS0yMi4yIDYwLjYtOS45LTMuMS0xOS4zLTYuNS0yOC0xMC4yek0zMTAgNDkwYy0xMy42LTcuOC0xOS41LTM3LjUtMTQuOS03NS43IDEuMS05LjQgMi45LTE5LjMgNS4xLTI5LjQgMTkuNiA0LjggNDEgOC41IDYzLjUgMTAuOSAxMy41IDE4LjUgMjcuNSAzNS4zIDQxLjYgNTAtMzIuNiAzMC4zLTYzLjIgNDYuOS04NCA0Ni45LTQuNS0uMS04LjMtMS0xMS4zLTIuN3ptMjM3LjItNzYuMmM0LjcgMzguMi0xLjEgNjcuOS0xNC42IDc1LjgtMyAxLjgtNi45IDIuNi0xMS41IDIuNi0yMC43IDAtNTEuNC0xNi41LTg0LTQ2LjYgMTQtMTQuNyAyOC0zMS40IDQxLjMtNDkuOSAyMi42LTIuNCA0NC02LjEgNjMuNi0xMSAyLjMgMTAuMSA0LjEgMTkuOCA1LjIgMjkuMXptMzguNS02Ni43Yy04LjYgMy43LTE4IDctMjcuNyAxMC4xLTUuNy0xOS42LTEzLjItNDAtMjIuNS02MC45IDkuMi0yMC44IDE2LjYtNDEuMSAyMi4yLTYwLjYgOS45IDMuMSAxOS4zIDYuNSAyOC4xIDEwLjIgMzUuNCAxNS4xIDU4LjMgMzQuOSA1OC4zIDUwLjYtLjEgMTUuNy0yMyAzNS42LTU4LjQgNTAuNnpNMzIwLjggNzguNHoiLz4gICAgICAgIDxjaXJjbGUgY3g9IjQyMC45IiBjeT0iMjk2LjUiIHI9IjQ1LjciLz4gICAgICAgIDxwYXRoIGQ9Ik01MjAuNSA3OC4xeiIvPiAgICA8L2c+PC9zdmc+'; 6 | 7 | function Simple() { 8 | const morph = useMorph(); 9 | const [toggle, setToggle] = useState(false); 10 | 11 | return ( 12 |
13 | 14 |
15 | 16 | {toggle ? ( 17 | 18 | ) : ( 19 | 20 | )} 21 |
22 | ); 23 | } 24 | 25 | export default Simple; 26 | -------------------------------------------------------------------------------- /docz/src/pages/GettingStarted/curve.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Curves & Easings 3 | route: /curves-easings 4 | --- 5 | 6 | import { Playground } from 'docz'; 7 | import { useState } from 'react'; 8 | import { useMorph } from 'react-morph/index'; 9 | import { easeInSin, easeOutSin, easeIn, easeOut } from 'react-morph/easings'; 10 | import { butterfly, larva } from '../../images'; 11 | import { 12 | circIn, 13 | circOut, 14 | circInOut, 15 | reversed, 16 | createMirroredEasing, 17 | } from '@popmotion/easing'; 18 | 19 | # Curves & Easings 20 | 21 | ## Curves 22 | 23 | It's possible to create a curve instead of a linear translation between morph states. 24 | 25 | In order to achieve that you need to set different easings between `X` and `Y` values. 26 | 27 | ```js 28 | useMorph({ 29 | easings: { 30 | translateX: easeOutSin, 31 | translateY: easeInSin, 32 | }, 33 | }); 34 | ``` 35 | 36 | ## Demo 37 | 38 | { 39 | () => { 40 | // Handle toggle state as you normally would. 41 | const [toggle, setToggle] = useState(false); 42 | const morph = useMorph({ 43 | isReversed: !toggle, 44 | easings: { 45 | translateX: easeOutSin, 46 | translateY: easeInSin, 47 | }, 48 | }); 49 | return ( 50 |
51 | 52 |
53 | 54 | {toggle && } 55 | 56 |
57 |
58 |
59 |
60 | 61 | {!toggle && ( 62 | 68 | )} 69 |
70 | 71 | )}} 72 | 73 |
74 | 75 | ## Easings 76 | 77 | The valid CSS property keys are: `translateX`, `translateY`, `scaleX`, and `scaleY`. 78 | 79 | The value should be an easing function that **doesn't clamp** the values. 80 | 81 | React Morph provides some "safe" easing functions. You can import them like these: 82 | 83 | ```js 84 | import { easeInSin, easeOutSin, easeIn, easeOut } from 'react-morph/easings'; 85 | ``` 86 | 87 | ### Fine-tuning 88 | 89 | Different ease functions will result in different curve shapes. 90 | Depending on the easing and spring values you might need to fine-tune other spring properties like `restDisplacementThreshold` or `overshootClamping` 91 | 92 | ```js 93 | useMorph({ 94 | spring: { 95 | restDisplacementThreshold: 0.0001, 96 | overshootClamping: true, 97 | }, 98 | }); 99 | ``` 100 | -------------------------------------------------------------------------------- /docz/src/pages/GettingStarted/intro.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Getting Started 3 | route: / 4 | --- 5 | 6 | import { Playground } from 'docz'; 7 | import { useState } from 'react'; 8 | import { useMorph } from 'react-morph/'; 9 | import { butterfly, larva } from '../../images'; 10 | 11 | # React Morph ️🦋 12 | 13 | #### Morphing UI transitions made simple 14 | 15 | Magically animates one element into another just by tagging the first and last state. 16 | 17 | ## Getting Started 🐛 18 | 19 | Import the `useMorph` hook. 20 | 21 | ```js 22 | import { useMorph } from 'react-morph'; 23 | ``` 24 | 25 | Call the hook in you function component. 26 | 27 | ```js 28 | const morph = useMorph(options); 29 | ``` 30 | 31 | Then spread the props to the elements you want to morph. 32 | 33 | ```jsx 34 | 35 | ``` 36 | 37 | ```jsx 38 | 39 | ``` 40 | 41 | You should handle the state as you normally would for mounting and unmounting the elements. 42 | 43 | > Make sure you have just **ONE element rendered at same time**. 44 | 45 | 46 | {() => { 47 | // Handle toggle state as you normally would. 48 | const [toggle, setToggle] = useState(true) 49 | const morph = useMorph(); 50 | 51 | return ( 52 |
53 | 54 | 55 |
56 | {toggle && } 57 |
58 |
59 |
60 | {!toggle && } 61 |
62 | ) 63 | } 64 | } 65 |
66 | 67 | ## Controling the state 68 | 69 | React morph doesn't handle the state for you. There is no play or stop method. 70 | 71 | Also, it won't hide or show the elements for you. It's up to you how you want to handle it. 72 | 73 | The animation will be triggered as soon as the new element with the same `{...morph}` enters the DOM. 74 | 75 | That's why you cannot have the two state elements at the same time. 76 | 77 | The elements can use `useState` 78 | 79 | ```js 80 | { 81 | toggle ? ( 82 | 83 | ) : ( 84 | 85 | ); 86 | } 87 | ``` 88 | 89 | But also you can use `react-router` 90 | -------------------------------------------------------------------------------- /docz/src/pages/Player/Player.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useMorphKeys } from 'react-morph'; 3 | import PlayerFull from './PlayerFull'; 4 | import PlayerMini from './PlayerMini'; 5 | 6 | const Player = () => { 7 | const morphs = useMorphKeys(['container', 'title', 'subtitle', 'image']); 8 | 9 | const [toggle, setToggle] = useState(false); 10 | 11 | return ( 12 |
13 |
14 |
15 | {!toggle ? ( 16 | setToggle(!toggle)} /> 17 | ) : ( 18 | setToggle(!toggle)} /> 19 | )} 20 |
21 |
22 | ); 23 | }; 24 | 25 | export default Player; 26 | -------------------------------------------------------------------------------- /docz/src/pages/Player/PlayerFull.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const PlayerFull = ({ morphs = {}, ...props }) => { 4 | return ( 5 |
6 |
7 |
8 |
9 | Album Pic 15 |
16 |
17 |
18 |
19 |

23 | A Sky Full of Stars 24 |

25 |

29 | Ghost Stories 30 |

31 |
32 |
33 | 39 | 40 | 41 |
42 |
43 |
44 |
45 | 51 | 52 | 53 |
54 |
55 | 61 | 62 | 63 |
64 |
65 | 71 | 72 | 73 |
74 |
75 | 81 | 82 | 83 |
84 |
85 | 91 | 92 | 93 |
94 |
95 |
96 |
97 |
98 |
99 |

0:40

100 |

4:20

101 |
102 |
103 |
104 |
105 | 106 |
107 |
108 |
109 |
110 |
111 |
112 | ); 113 | }; 114 | 115 | export default PlayerFull; 116 | -------------------------------------------------------------------------------- /docz/src/pages/Player/PlayerMini.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const PlayerFull = ({ morphs, ...props }) => { 4 | return ( 5 | 10 |
11 |
12 | Album Pic 19 |
20 |
21 |
22 |
23 |

27 | A Sky Full of Stars 28 |

29 |

33 | Ghost Stories 34 |

35 |
36 |
37 |
38 |
39 |
40 | ); 41 | }; 42 | 43 | export default PlayerFull; 44 | -------------------------------------------------------------------------------- /docz/src/pages/Player/player.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Player 3 | route: /demos/player 4 | menu: Demos 5 | --- 6 | 7 | import { useState } from 'react'; 8 | import { useMorph, useMorphs } from 'react-morph'; 9 | import { Playground } from 'docz'; 10 | import '../../utilities.css'; 11 | import '../../../public/tailwind.min.css'; 12 | import PlayerFull from './PlayerFull'; 13 | import PlayerMini from './PlayerMini'; 14 | import Player from './Player.tsx'; 15 | 16 | # Player Demo 17 | 18 | 19 | {() => { 20 | const morphs = useMorphs(['container', 'title', 'subtitle', 'image']); 21 | 22 | const [toggle, setToggle] = useState(false); 23 | 24 | return ( 25 |
26 | {!toggle ? ( 27 | setToggle(!toggle)} /> 28 | ) : ( 29 | setToggle(!toggle)} /> 30 | )} 31 |
32 | 33 | )}} 34 |
35 | 36 | 37 | Design by: @shuvro_008 38 | https://tailwindcomponents.com/component/tailwind-css-audio-player 39 | 40 | -------------------------------------------------------------------------------- /docz/src/pages/Progress/progress.mdx-: -------------------------------------------------------------------------------- 1 | --- 2 | name: Progress 3 | route: /advanced/progress 4 | menu: Advanced 5 | --- 6 | 7 | import { Playground } from 'docz'; 8 | import { useState } from 'react'; 9 | import { useMorph } from 'react-morph/'; 10 | import { butterfly, larva } from '../../images'; 11 | 12 | # Controling progress 13 | 14 | 15 | {() => { 16 | // Handle toggle state as you normally would. 17 | const [toggle, setToggle] = useState(true) 18 | const [progress, setProgress] = useState(0) 19 | 20 | const morph = useMorph({ progress: Number(progress / 100) }); 21 | 22 | return ( 23 |
24 | 25 |
26 | 27 |
28 | 29 | setProgress(value)} 33 | step="0.01" 34 | 35 | /> 36 | {progress}% 37 |
38 | 39 | 40 | {toggle && } 41 | 42 |
43 |
44 |
45 | {!toggle && } 46 |
47 | ) 48 | } 49 | 50 | } 51 | 52 |
53 | -------------------------------------------------------------------------------- /docz/src/pages/Tests/toggle.mdx-: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunnolou/react-morph/f277f639213bc32d46bc918e79deb989d8d52e6b/docz/src/pages/Tests/toggle.mdx- -------------------------------------------------------------------------------- /docz/src/pages/Travel/Travel.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import './travel.css'; 3 | import { useMorph, useMorphs, useFade } from 'react-morph/'; 4 | import { createEaseIn } from 'react-morph/easings'; 5 | import presets from 'react-morph/presets'; 6 | 7 | const strongerEase = createEaseIn(2); 8 | 9 | const faces = [ 10 | { 11 | username: 'brunnolou', 12 | src: 'https://avatars1.githubusercontent.com/u/2729225?s=460&v=4', 13 | }, 14 | { 15 | username: 'lucalanca', 16 | src: 'https://avatars3.githubusercontent.com/u/389459?s=460&v=4', 17 | }, 18 | { 19 | username: 'florianginetta', 20 | src: 'https://avatars3.githubusercontent.com/u/30113109?s=460&v=4', 21 | }, 22 | { 23 | username: 'lejoe', 24 | src: 'https://avatars3.githubusercontent.com/u/1759?s=460&v=4', 25 | }, 26 | ]; 27 | 28 | const spring = presets.noWobble; 29 | 30 | const Travel = () => { 31 | const [toggle, go] = useState(false); 32 | const contentPlaceholderMorph = useMorph({ 33 | spring, 34 | zIndex: 1, 35 | isReversed: !toggle, 36 | easings: strongerEase, 37 | }); 38 | const coverMorph = useMorph({ spring, zIndex: 2 }); 39 | const sepFade = useFade({ spring, isInitial: !toggle, zIndex: 4 }); 40 | const titleMorph = useMorph({ spring, zIndex: 4 }); 41 | const leftMorph = useMorph({ spring, zIndex: 4 }); 42 | const rightMorph = useMorph({ spring, zIndex: 4 }); 43 | const facesMorphs = useMorphs(faces, { spring, zIndex: 3 }); 44 | const facesFades = faces.map(() => 45 | useFade({ spring, isInitial: toggle, zIndex: 4 }), 46 | ); 47 | 48 | return ( 49 |
50 | 51 | {!toggle && ( 52 | go(true)}> 53 |
54 |

55 | Zurich 56 |

57 |
58 |
64 | 65 |
66 | 67 | Grossmünster 68 | 69 | 70 | 47.3769° N, 8.5417° E 71 | 72 |
73 |
74 | )} 75 | 76 | {!toggle && ( 77 |
78 |
82 |
83 |
84 |
85 |

86 | Panorama Grossmünster limmat river 87 |

88 |
89 | 90 |
    91 | {faces.map(({ src, username }, index) => ( 92 |
  • 93 | {username} 99 |
  • 100 | ))} 101 |
102 |
103 |
104 | )} 105 | 106 | {toggle && ( 107 |
go(false)}> 108 |
115 | 116 |
117 |
118 | 119 | Grossmünster 120 | 121 | 122 | 47.3769° N, 8.5417° E 123 | 124 |
125 | 126 |

127 | Zurich 128 |

129 |
130 | 131 |
132 |
136 | 137 |
    138 | {faces.map(({ src, username }, index) => ( 139 |
  • 140 |
    141 | {username} 147 |
    148 | 149 | 150 | {username} 151 | 152 |
  • 153 | ))} 154 |
155 |
156 |
157 | )} 158 |
159 | ); 160 | }; 161 | 162 | export default Travel; 163 | -------------------------------------------------------------------------------- /docz/src/pages/Travel/tavel.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Travel 3 | route: /demos/travel 4 | menu: Demos 5 | --- 6 | 7 | import { Playground } from 'docz'; 8 | import { useState } from 'react'; 9 | import { useMorph } from 'react-morph/'; 10 | import Travel from './Travel'; 11 | 12 | # Travel demo 13 | 14 |
15 | 16 |
17 | -------------------------------------------------------------------------------- /docz/src/pages/Travel/travel.css: -------------------------------------------------------------------------------- 1 | .accelerated { 2 | transform: translate3d(0, 0, 0); 3 | will-change: transform; 4 | } 5 | 6 | * { 7 | box-sizing: border-box; 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | a { 13 | cursor: pointer; 14 | } 15 | 16 | img { 17 | max-width: 100%; 18 | } 19 | 20 | body, 21 | html { 22 | height: 100%; 23 | min-height: 100%; 24 | } 25 | 26 | .body { 27 | background-image: linear-gradient(#ced8dd, #9bb1c1); 28 | color: #666; 29 | width: 100%; 30 | min-height: 700px; 31 | padding: 3rem; 32 | } 33 | 34 | p { 35 | margin: 12px 0; 36 | font-size: 14px; 37 | } 38 | 39 | .container { 40 | display: flex; 41 | justify-content: center; 42 | align-items: center; 43 | flex-direction: column; 44 | } 45 | 46 | .card { 47 | position: relative; 48 | z-index: 2; 49 | display: flex; 50 | flex-direction: column; 51 | justify-content: space-between; 52 | width: 100%; 53 | max-width: 17rem; 54 | height: 18rem; 55 | text-align: center; 56 | color: #fff; 57 | padding: 1rem; 58 | margin: 15rem 1rem 0; 59 | } 60 | 61 | .card-content-placeholder, 62 | .card-image { 63 | position: absolute; 64 | top: 0; 65 | left: 0; 66 | right: 0; 67 | bottom: 0; 68 | z-index: -1; 69 | background-size: auto 100%; 70 | background-position: center; 71 | box-shadow: 0 15px 40px -10px rgba(0, 0, 0, 0.9); 72 | border-radius: 0.5rem; 73 | } 74 | .card-content-placeholder { 75 | background: white; 76 | } 77 | 78 | .details-title, 79 | .card-title { 80 | display: inline-block; 81 | font-weight: 100; 82 | margin: 0; 83 | color: #fff; 84 | } 85 | 86 | .card-content-placeholder { 87 | z-index: -2; 88 | border-radius: 0.25rem; 89 | background: #fff; 90 | box-shadow: 0 15px 40px -10px rgba(0, 0, 0, 0.1); 91 | } 92 | 93 | .card-content { 94 | position: relative; 95 | z-index: 1; 96 | border-radius: 0.25rem; 97 | display: inline-flex; 98 | flex-direction: column; 99 | justify-content: space-between; 100 | width: 100%; 101 | max-width: 19rem; 102 | height: 12rem; 103 | margin: -4rem 1rem 1rem; 104 | padding: 1rem; 105 | font-size: 14px; 106 | text-align: left; 107 | } 108 | 109 | .card-footer { 110 | display: flex; 111 | justify-content: space-between; 112 | } 113 | 114 | .details { 115 | position: relative; 116 | top: 0; 117 | z-index: 1; 118 | display: flex; 119 | flex-direction: column; 120 | justify-content: center; 121 | max-width: 60rem; 122 | width: 100%; 123 | } 124 | 125 | .details-image { 126 | position: relative; 127 | z-index: 2; 128 | background-size: cover; 129 | background-position: center; 130 | 131 | width: 100%; 132 | height: 24rem; 133 | } 134 | 135 | .details-title { 136 | z-index: 3; 137 | display: block; 138 | text-align: center; 139 | font-weight: 100; 140 | margin-top: -6rem; 141 | } 142 | 143 | .title-large { 144 | font-size: 50px; 145 | } 146 | 147 | .details-content { 148 | position: relative; 149 | z-index: 1; 150 | padding: 1rem; 151 | } 152 | 153 | .details-toolbar { 154 | position: absolute; 155 | top: 0; 156 | left: 0; 157 | width: 100%; 158 | padding: 1rem; 159 | z-index: 3; 160 | } 161 | 162 | .details-content-placeholder { 163 | background: #fff; 164 | position: absolute; 165 | top: 0; 166 | left: 0; 167 | right: 0; 168 | bottom: 0; 169 | z-index: -1; 170 | } 171 | 172 | .users { 173 | display: flex; 174 | margin: 1rem -3rem -1rem; 175 | padding: 1rem 2rem 0; 176 | } 177 | 178 | .separator { 179 | padding: 1rem; 180 | border-bottom: 1px solid #eee; 181 | margin: 0 -2rem -1rem; 182 | } 183 | 184 | .users-item { 185 | display: block; 186 | margin-right: -0.5rem; 187 | text-align: left; 188 | display: flex; 189 | justify-content: flex-start; 190 | align-items: center; 191 | } 192 | 193 | .users-image { 194 | display: block; 195 | width: 2rem; 196 | height: 2rem; 197 | border-radius: 2rem; 198 | box-shadow: 0 2px 5px -2px rgba(0, 0, 0, 0.9); 199 | } 200 | 201 | .users-image--lg { 202 | width: 4rem; 203 | height: 4rem; 204 | } 205 | 206 | .p1 { 207 | padding: 1rem; 208 | } 209 | .m1 { 210 | margin: 1rem; 211 | } 212 | 213 | .t-center { 214 | text-align: center; 215 | } 216 | .t-left { 217 | text-align: left; 218 | } 219 | 220 | .c-white { 221 | color: #fff; 222 | } 223 | 224 | .l-inline-block { 225 | position: relative; 226 | display: inline-block; 227 | } 228 | 229 | .l-flex { 230 | display: flex; 231 | justify-content: center; 232 | } 233 | 234 | .text-container { 235 | display: flex; 236 | flex-wrap: wrap; 237 | width: 320px; 238 | margin: 0 auto; 239 | cursor: pointer; 240 | } 241 | 242 | .text-container > span { 243 | margin: 0 0.1em; 244 | } 245 | 246 | .text-container--small { 247 | justify-content: center; 248 | font-size: 30px; 249 | font-weight: bold; 250 | width: 200px; 251 | } 252 | -------------------------------------------------------------------------------- /docz/src/pages/api/useFade.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: useFade 3 | route: /api/fade 4 | menu: Api 5 | --- 6 | 7 | import { Playground } from 'docz'; 8 | import { useState } from 'react'; 9 | import { useFade } from 'react-morph/'; 10 | import { butterfly, larva } from '../../images'; 11 | 12 | # Fade in Fade out 13 | 14 | ## Intro 15 | 16 | When creating complex morph animation some elements can simply fade in or fade out. 17 | This would make more pleasent animations since there will be less cognitive load to 18 | process all the movements. 19 | 20 | Import the `useFade` hook. 21 | 22 | ```js 23 | import { useFade } from 'react-morph'; 24 | ``` 25 | 26 | Call the hook in you function component. 27 | 28 | ```js 29 | const fade = useFade(options); 30 | ``` 31 | 32 | Then spread the props to the elements you want to fade. 33 | 34 | ```jsx 35 | 36 | ``` 37 | 38 | ## Simple fade 39 | 40 | 41 | {() => { 42 | // Handle toggle state as you normally would. 43 | const [toggle, setToggle] = useState(false) 44 | const spring = { damping: 5, stiffness: 10 }; 45 | const fadeIn = useFade({ spring, isInitial: toggle, delaysRatio: 1 }); 46 | const fadeOut = useFade({ spring, isInitial: !toggle }); 47 | 48 | return ( 49 |
50 | 51 | 52 |
53 | 54 | {toggle ? ( 55 | 56 | ) : ( 57 | 58 | )} 59 |
60 | )}} 61 | 62 |
63 | 64 | > **⚠️ Warning: ** 65 | > 66 | > Differentl than `useMorph`: 67 | > 68 | > - Don't spread the same fade into multiple elements. 69 | > - Create one for the initial state and another for the final state 70 | 71 | ## Disable initial fade 72 | 73 | Usually when you create a complex morph with some fades, we just want to fade in/out when toggle. 74 | In order to disable the initial fade in on page load you can toggle the option `isInitial`. 75 | 76 | Normally you will set the `isInitial` option on the initial state and disable it to the final state. 77 | 78 | ```js 79 | useFade({ isInitial: toggle }); 80 | ``` 81 | 82 | ## Delay 83 | 84 | By default `useFade` delays the fade in and speeds up the fade out. 85 | This is expecially useful to use with `useMorph`. 86 | 87 | To update this behaviour you can set the option `delaysRatio`. 88 | The default value is `0.1`. To disable the delay set `delaysRatio` to `1`. 89 | -------------------------------------------------------------------------------- /docz/src/pages/api/useMorphKeys.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: useMorphKeys 3 | route: /api/useMorphKeys 4 | menu: Api 5 | --- 6 | 7 | # `useMorphKeys` 8 | 9 | ```js 10 | import { useMorphKeys } from 'react-morph'; 11 | ``` 12 | 13 | ### `useMorphKeys(string[], options: MorphOptions)` 14 | 15 | Return an **object** with multiple morphs with the given keys. 16 | Also, each morph will receive an incremental `zIndex` option prop. 17 | 18 | ```js 19 | const Index = () => { 20 | const morphs = useMorphKeys(['key1', 'key2']); 21 | // morphs = { key1: …, key2: … } 22 | 23 | return ( 24 |
25 | {toggle && } 26 | {!toggle &&
} 27 |
28 | ); 29 | }; 30 | 31 | const Item = ({ morphs }) => ( 32 |
33 |

Foo

34 |
35 | ); 36 | 37 | const Details = ({ morphs }) => ( 38 |
39 |

Foo

40 |
41 | ); 42 | ``` 43 | -------------------------------------------------------------------------------- /docz/src/pages/api/useMorphList.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: useMorphList 3 | route: /api/useMorphList 4 | menu: Api 5 | --- 6 | 7 | # `useMorphList` 8 | 9 | ```js 10 | import { useMorphList } from 'react-morph'; 11 | ``` 12 | 13 | ### `useMorphList(any[] | number, options: MorphOptions)` 14 | 15 | Return an **array** with multiple morphs with the same length of the given array or the length of the given number. 16 | Also, each morph will receive an incremental `zIndex` option prop. 17 | 18 | ```js 19 | const myItems = [{ name: 'Foo' }, { name: 'bar' }]; 20 | const morphs = useMorphList(myItems); 21 | 22 |
    23 | {myItems.map((user, index) => ( 24 |
  • 25 | {isExpanded && {user.name}} 26 | {isExpanded &&

    {user.name}

    } 27 |
  • 28 | ))} 29 |
; 30 | ``` 31 | -------------------------------------------------------------------------------- /docz/src/pages/demos/docs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Examples 3 | route: /demos/examples 4 | menu: Demos 5 | --- 6 | 7 | import { Playground } from 'docz'; 8 | 9 | # Live Demos 10 | 11 | ## [Hello world](https://codesandbox.io/s/yqyymqn8z1) 12 | 13 |