├── .buckconfig ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── .vscode ├── launch.json └── settings.json ├── .watchmanconfig ├── LICENSE.md ├── README.md ├── TODO.md ├── android ├── app │ ├── _BUCK │ ├── build.gradle │ ├── build_defs.bzl │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── fluidtransitions │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── res │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios ├── FluidTransitions.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── FluidTransitions-tvOS.xcscheme │ │ └── FluidTransitions.xcscheme ├── FluidTransitions.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── FluidTransitions │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── AppIcon-1024px.png │ │ │ ├── AppIcon-120px-40pt@3x.png │ │ │ ├── AppIcon-120px-60pt@2x.png │ │ │ ├── AppIcon-180px-60pt@3x.png │ │ │ ├── AppIcon-29px-29pt@1x.png │ │ │ ├── AppIcon-40px-20pt@2x.png │ │ │ ├── AppIcon-58px-29pt@2x.png │ │ │ ├── AppIcon-60px-20pt@3x.png │ │ │ ├── AppIcon-80px-40pt@2x.png │ │ │ ├── AppIcon-87px-29pt@3x.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Logo.imageset │ │ │ ├── Contents.json │ │ │ ├── logo-symbol-text@1x.png │ │ │ ├── logo-symbol-text@2x.png │ │ │ └── logo-symbol-text@3x.png │ ├── Info.plist │ └── main.m ├── Podfile └── Podfile.lock ├── metro.config.js ├── package.json ├── src ├── App.tsx ├── examples │ ├── AppStore │ │ └── index.tsx │ ├── Assets │ │ ├── logo-symbol-text@1x.png │ │ ├── logo-symbol-text@2x.png │ │ └── logo-symbol-text@3x.png │ ├── Children │ │ ├── bubble.tsx │ │ └── index.tsx │ ├── Dragging │ │ └── index.tsx │ ├── Driver │ │ ├── MovingButton.tsx │ │ └── index.tsx │ ├── Easings │ │ └── index.tsx │ ├── Flutter │ │ ├── assets │ │ │ ├── earrings.png │ │ │ ├── hat.png │ │ │ ├── icon.png │ │ │ ├── splash.png │ │ │ ├── sunnies.png │ │ │ └── table.png │ │ ├── components │ │ │ ├── Cursor.tsx │ │ │ ├── Header.tsx │ │ │ ├── Headers.tsx │ │ │ ├── Label.tsx │ │ │ ├── MockCard.tsx │ │ │ ├── MockEntry.tsx │ │ │ ├── Model.ts │ │ │ ├── Pages.tsx │ │ │ ├── Sections.tsx │ │ │ └── index.tsx │ │ └── index.tsx │ ├── HomeScreen.tsx │ ├── HomeScreenButton.tsx │ ├── Interactions │ │ ├── AnimatedNumber.tsx │ │ ├── CallButton.tsx │ │ ├── FloatingLabel.tsx │ │ ├── InteractionContainer.tsx │ │ ├── LikeHeart.tsx │ │ ├── LoginButton.tsx │ │ └── index.tsx │ ├── Interpolate │ │ └── index.tsx │ ├── Lists │ │ └── index.tsx │ ├── Maze │ │ ├── MazeItem.tsx │ │ ├── index.tsx │ │ └── styles.ts │ ├── Navigation │ │ ├── box.tsx │ │ ├── bubble.tsx │ │ ├── button.tsx │ │ ├── index.tsx │ │ └── screen.tsx │ ├── Parallax │ │ └── index.tsx │ ├── Repeating │ │ ├── Repeater.tsx │ │ └── index.tsx │ ├── SVG │ │ └── index.tsx │ ├── Stagger │ │ └── index.tsx │ ├── Style │ │ └── index.tsx │ ├── Styles │ │ ├── EMailFolder.tsx │ │ ├── MovingButton.tsx │ │ ├── Pager.tsx │ │ ├── ProgressBar.tsx │ │ └── index.tsx │ ├── Text │ │ ├── AdCard.tsx │ │ ├── Ticker.tsx │ │ └── index.tsx │ ├── colors.ts │ ├── helpers.ts │ ├── index.tsx │ └── scratch.tsx ├── packages │ ├── animated │ │ ├── index.ts │ │ ├── index.web.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Types │ │ │ │ ├── IAnimationNode.ts │ │ │ │ ├── IAnimationProvider.ts │ │ │ │ ├── IAnimationValue.ts │ │ │ │ ├── Interpolation.ts │ │ │ │ └── index.ts │ │ │ ├── react-native-animated │ │ │ │ ├── Implementation │ │ │ │ │ ├── ColorNode.ts │ │ │ │ │ ├── RadNode.ts │ │ │ │ │ ├── bezier.ts │ │ │ │ │ ├── createValue.ts │ │ │ │ │ ├── getColorDisplayValue.ts │ │ │ │ │ ├── getColorDisplayValue.web.ts │ │ │ │ │ ├── getProcessedColor.ts │ │ │ │ │ ├── getProcessedColor.web.ts │ │ │ │ │ ├── getProcessedRotation.ts │ │ │ │ │ ├── normalizeColor.ts │ │ │ │ │ ├── runTiming.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── assign.spec.ts │ │ │ │ │ ├── bezier.spec.ts │ │ │ │ │ ├── block.spec.ts │ │ │ │ │ ├── conditions.spec.ts │ │ │ │ │ ├── operators.spec.ts │ │ │ │ │ └── proc.spec.ts │ │ │ │ └── index.ts │ │ │ └── react-native-reanimated │ │ │ │ ├── Implementation │ │ │ │ ├── createValue.ts │ │ │ │ ├── getColorFromString.ts │ │ │ │ ├── getDegreesInRadian.ts │ │ │ │ ├── getProcessedColor.ts │ │ │ │ ├── getProcessedRotation.ts │ │ │ │ ├── runTiming.ts │ │ │ │ └── utils.ts │ │ │ │ ├── index.native.ts │ │ │ │ └── index.web.ts │ │ └── tsconfig.json │ ├── gestures │ │ ├── index.ts │ │ ├── package.json │ │ ├── src │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── navigation │ │ ├── index.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── Functions │ │ │ │ ├── getNavigationStates.ts │ │ │ │ ├── index.ts │ │ │ │ └── safeGetState.ts │ │ │ ├── Hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useNavigationDirection.ts │ │ │ │ ├── useNavigationStates.ts │ │ │ │ └── useVisibilityStyle.ts │ │ │ ├── Transitions │ │ │ │ ├── createNavigationTransition.ts │ │ │ │ ├── index.ts │ │ │ │ ├── useNavigationTransition.ts │ │ │ │ └── useTranslateTransitions.ts │ │ │ ├── createFluidStackNavigator.tsx │ │ │ ├── hooks │ │ │ │ ├── useCurrentValue.ts │ │ │ │ ├── useDriverContext.ts │ │ │ │ └── useNavigationState.ts │ │ │ ├── index.tsx │ │ │ ├── navigationContainer.tsx │ │ │ └── types.ts │ │ └── tsconfig.json │ ├── svg │ │ ├── index.ts │ │ ├── package.json │ │ ├── src │ │ │ └── Components │ │ │ │ └── index.ts │ │ └── tsconfig.json │ └── transitions │ │ ├── index.ts │ │ ├── package.json │ │ ├── src │ │ ├── Animation │ │ │ ├── Builder │ │ │ │ ├── __tests__ │ │ │ │ │ └── getInterpolationTree.spec.ts │ │ │ │ ├── dumpTree.ts │ │ │ │ ├── getAnimationOnEnd.ts │ │ │ │ ├── getInterpolationTree.ts │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ ├── Functions │ │ │ │ ├── createProc.ts │ │ │ │ ├── easing.ts │ │ │ │ ├── getResolvedAnimation.ts │ │ │ │ ├── index.ts │ │ │ │ └── spring.ts │ │ │ ├── Runner │ │ │ │ ├── Functions │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ ├── createAnimationNode.spec.ts │ │ │ │ │ │ ├── easing.spec.ts │ │ │ │ │ │ ├── interpolate.spec.ts │ │ │ │ │ │ ├── interpolateColor.spec.ts │ │ │ │ │ │ └── normalize.spec.ts │ │ │ │ │ ├── createAnimationNode.ts │ │ │ │ │ ├── createInterpolationNode.ts │ │ │ │ │ ├── getExtrapolationValue.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interpolate.ts │ │ │ │ │ ├── interpolateColor.ts │ │ │ │ │ ├── interpolateValue.ts │ │ │ │ │ ├── lifecycle.ts │ │ │ │ │ ├── normalize.ts │ │ │ │ │ ├── setAnimationValue.ts │ │ │ │ │ └── setInterpolationValue.ts │ │ │ │ ├── addAnimations.ts │ │ │ │ ├── addInterpolation.ts │ │ │ │ ├── index.ts │ │ │ │ └── interpolationStorage.ts │ │ │ ├── commitAnimations.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── Components │ │ │ ├── FluidTransitions │ │ │ │ ├── index.ts │ │ │ │ ├── useAnimationContext.ts │ │ │ │ ├── useConfiguration.tsx │ │ │ │ ├── useInitialStyle.ts │ │ │ │ ├── useInterpolatorConfig.ts │ │ │ │ ├── useInterpolatorContext.tsx │ │ │ │ ├── useLayout.ts │ │ │ │ ├── useMountUpdate.ts │ │ │ │ ├── useOnConfig.ts │ │ │ │ ├── usePropContext.ts │ │ │ │ ├── useSharedInterpolation.tsx │ │ │ │ ├── useStateChanges.ts │ │ │ │ ├── useStyleContext.ts │ │ │ │ ├── useTouchable.tsx │ │ │ │ ├── useTransitionItems.tsx │ │ │ │ ├── useValueContext.ts │ │ │ │ ├── useValueInterpolation.ts │ │ │ │ ├── useWhenConfig.ts │ │ │ │ └── withFluidTransitions.tsx │ │ │ ├── Types │ │ │ │ ├── AnimatedStyleKeys.ts │ │ │ │ ├── AnimationContext.ts │ │ │ │ ├── AnimationInfo.ts │ │ │ │ ├── ConfigurationContext.ts │ │ │ │ ├── DriverContext.ts │ │ │ │ ├── Easing.ts │ │ │ │ ├── InterpolationTypes.ts │ │ │ │ ├── InterpolatorContext.ts │ │ │ │ ├── SharedContext.ts │ │ │ │ ├── StateContext.ts │ │ │ │ ├── StyleTypes.ts │ │ │ │ ├── TransitionItem.ts │ │ │ │ ├── TransitionItemContext.ts │ │ │ │ ├── ValueContextType.ts │ │ │ │ └── index.ts │ │ │ └── index.tsx │ │ ├── Configuration │ │ │ ├── Types.ts │ │ │ ├── __tests__ │ │ │ │ ├── mergeConfig.spec.ts │ │ │ │ └── validateConfig.spec.ts │ │ │ ├── animationType.ts │ │ │ ├── childAnimation.ts │ │ │ ├── createConfig.ts │ │ │ ├── createState.ts │ │ │ ├── index.ts │ │ │ ├── interpolation.ts │ │ │ ├── interpolationValue.ts │ │ │ ├── mergeConfigs.ts │ │ │ ├── onState.ts │ │ │ ├── validateConfig.ts │ │ │ └── whenState.ts │ │ ├── Hooks │ │ │ ├── index.ts │ │ │ ├── useFluidConfig.ts │ │ │ ├── useFluidState.ts │ │ │ ├── useForceUpdate.ts │ │ │ └── useLog.ts │ │ ├── Props │ │ │ ├── getAnimatedProps.ts │ │ │ └── index.ts │ │ ├── Shared │ │ │ ├── createSharedInterpolation.ts │ │ │ ├── getSharedInterpolationMetrics.ts │ │ │ ├── getSharedInterpolationStyles.ts │ │ │ ├── getStates.ts │ │ │ ├── index.ts │ │ │ └── setupSharedInterpolation.ts │ │ ├── Styles │ │ │ ├── __tests__ │ │ │ │ ├── getStyleValueFromKey.spec.ts │ │ │ │ └── setStyleValueForKey.spec.ts │ │ │ ├── getCalculatedStyle.ts │ │ │ ├── getMergedStyles.ts │ │ │ ├── getResolvedStyle.ts │ │ │ ├── getStyleInfo.ts │ │ │ ├── getStyleKeys.ts │ │ │ ├── getStyleValueFromKey.ts │ │ │ └── setStyleValueForKey.ts │ │ ├── Types │ │ │ ├── AnimatedValueType.ts │ │ │ ├── ComponentProps.ts │ │ │ ├── Constants.ts │ │ │ ├── FluidException.ts │ │ │ ├── LogTypes.ts │ │ │ ├── MetricTypes.ts │ │ │ └── index.ts │ │ ├── Utilities │ │ │ ├── Springs.ts │ │ │ ├── Timings.ts │ │ │ ├── index.ts │ │ │ └── measureItem.ts │ │ ├── Values │ │ │ ├── __tests__ │ │ │ │ ├── getInputRange.spec.ts │ │ │ │ └── getOutputRange.spec.ts │ │ │ ├── addValue.ts │ │ │ ├── createValue.ts │ │ │ ├── getChangedKeys.ts │ │ │ ├── getInputRange.ts │ │ │ ├── getKeys.ts │ │ │ ├── getOutputRange.ts │ │ │ ├── getValueFromKey.ts │ │ │ ├── index.ts │ │ │ └── setValueForKey.ts │ │ └── config.ts │ │ └── tsconfig.json └── web │ ├── .gitignore │ ├── config-overrides.js │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ ├── src │ ├── App.tsx │ ├── Easings │ │ └── index.tsx │ ├── Interpolate │ │ └── index.tsx │ ├── Maze │ │ ├── MazeItem.tsx │ │ ├── index.tsx │ │ └── styles.ts │ ├── Stagger │ │ └── index.tsx │ ├── Styles │ │ ├── MovingButton.tsx │ │ ├── Pager.tsx │ │ ├── ProgressBar.tsx │ │ └── index.tsx │ ├── Text │ │ ├── AdCard.tsx │ │ ├── Ticker.tsx │ │ └── index.tsx │ ├── colors.ts │ ├── helpers.ts │ ├── index.tsx │ ├── react-app-env.d.ts │ └── serviceWorker.ts │ ├── tsconfig.json │ └── yarn.lock ├── tsconfig.json └── yarn.lock /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true, 4 | }, 5 | parser: "@typescript-eslint/parser", // Specifies the ESLint parser 6 | extends: [ 7 | "@react-native-community", // Uses the recommended rules from the @typescript-eslint/eslint-plugin 8 | "prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier 9 | "plugin:prettier/recommended", 10 | ], 11 | plugins: ["react-hooks"], 12 | parserOptions: { 13 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 14 | sourceType: "module", // Allows for the use of imports 15 | }, 16 | rules: { 17 | "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks 18 | "react-hooks/exhaustive-deps": "warn", // Checks effect dependencies 19 | "no-dupe-class-members": "off", 20 | }, 21 | overrides: [ 22 | { 23 | files: ["**/*.ts", "**/*.tsx"], 24 | rules: { 25 | "no-unused-vars": ["off"], 26 | "no-undef": ["off"], 27 | }, 28 | }, 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # Visual Studio Code 34 | # 35 | .vscode/.react 36 | 37 | # node.js 38 | # 39 | node_modules/ 40 | npm-debug.log 41 | yarn-error.log 42 | 43 | # BUCK 44 | buck-out/ 45 | \.buckd/ 46 | *.keystore 47 | 48 | # fastlane 49 | # 50 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 51 | # screenshots whenever they are needed. 52 | # For more information about the recommended setup visit: 53 | # https://docs.fastlane.tools/best-practices/source-control/ 54 | 55 | */fastlane/report.xml 56 | */fastlane/Preview.html 57 | */fastlane/screenshots 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # CocoaPods 63 | /ios/Pods/ 64 | /ios/budbuddy.xcworkspace/xcshareddata/ 65 | 66 | # Build 67 | /**/dist/** -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: true, 3 | jsxBracketSameLine: true, 4 | singleQuote: false, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug iOS", 9 | "cwd": "${workspaceFolder}", 10 | "type": "reactnative", 11 | "request": "launch", 12 | "platform": "ios" 13 | }, 14 | { 15 | "name": "Debug Android", 16 | "cwd": "${workspaceFolder}", 17 | "type": "reactnative", 18 | "request": "launch", 19 | "platform": "android" 20 | }, 21 | { 22 | "name": "Debug Android (Hermes) - Experimental", 23 | "cwd": "${workspaceFolder}", 24 | "type": "reactnativedirect", 25 | "request": "launch", 26 | "platform": "android" 27 | }, 28 | { 29 | "type": "chrome", 30 | "request": "launch", 31 | "name": "Launch Chrome", 32 | "url": "http://localhost:3000", 33 | "webRoot": "${workspaceFolder}" 34 | }, 35 | 36 | { 37 | "name": "Attach to packager", 38 | "cwd": "${workspaceFolder}", 39 | "type": "reactnative", 40 | "request": "attach" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "eslint.validate": [ 4 | "javascript", 5 | "javascriptreact", 6 | {"language": "typescript", "autoFix": true}, 7 | {"language": "typescriptreact", "autoFix": true} 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Fram X AS / Christian Falch 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 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | ## Bugs 4 | 5 | * [x] Fix issue with removing a when interpolation. See Styles example and press on/off 6 | * [x] Style-flash in Styles example - press button and while it is animating, press inbox-button 7 | * [x] Create central function for removing interpolations and animations 8 | * [x] Fix createAnimationNode impl so that tests only needs to run once 9 | * [ ] Shared transitions (WIP) 10 | * [ ] Fix running second shared transition while one is running 11 | Should we skip providing shared transitions in first version? 12 | * [ ] Fix issue with when.interpolation.value not using animation (is it possible?) 13 | 14 | ### Web 15 | 16 | * [ ] Fix measure of rotated elements on Web 17 | * [ ] Styles example: Button is not moving 18 | 19 | ### Android 20 | 21 | * [ ] Parallax example, not interpolating change of header color correctly 22 | * [x] SVG - ValueNode cannot be cast to a number 23 | * [x] Interpolation 24 | * [x] "Error while updating transform" 25 | * [x] Rotated image is not displayed correctly 26 | * [x] Interactions - some alignment issue 27 | 28 | ## TODO 29 | 30 | * [x] ConfigStateType should be accepted as state 31 | * [x] StaggerMs & StaggerFunc => stagger 32 | * [x] Test/implement stagger custom function 33 | * [x] Create dragging example 34 | * [ ] Navigation with new Stack navigator 35 | 36 | ## Later 37 | 38 | * [ ] Prop animation - support nested props and arrays WIP 39 | * [ ] Fix Spotify example, find out why changing to static styles fixes initial pos 40 | * [ ] Add velocity calculation to spring functions, find a way to keep velocity on 41 | animations and pass them to the spring function 42 | * [ ] Optimize running multiple animation updates when nodes are equal (duration, offset, easing) -------------------------------------------------------------------------------- /android/app/_BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.fluidtransitions", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.fluidtransitions", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/fluidtransitions/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.fluidtransitions; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import com.facebook.react.ReactActivityDelegate; 5 | import com.facebook.react.ReactRootView; 6 | import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; 7 | 8 | public class MainActivity extends ReactActivity { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. 12 | * This is used to schedule rendering of the component. 13 | */ 14 | @Override 15 | protected String getMainComponentName() { 16 | return "FluidTransitions"; 17 | } 18 | 19 | @Override 20 | protected ReactActivityDelegate createReactActivityDelegate() { 21 | return new ReactActivityDelegate(this, getMainComponentName()) { 22 | @Override 23 | protected ReactRootView createRootView() { 24 | return new RNGestureHandlerEnabledRootView(MainActivity.this); 25 | } 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/fluidtransitions/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.fluidtransitions; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.facebook.react.PackageList; 7 | import com.facebook.hermes.reactexecutor.HermesExecutorFactory; 8 | import com.facebook.react.bridge.JavaScriptExecutorFactory; 9 | import com.facebook.react.ReactApplication; 10 | import com.facebook.react.ReactNativeHost; 11 | import com.facebook.react.ReactPackage; 12 | import com.facebook.soloader.SoLoader; 13 | 14 | import java.util.List; 15 | 16 | public class MainApplication extends Application implements ReactApplication { 17 | 18 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 19 | @Override 20 | public boolean getUseDeveloperSupport() { 21 | return BuildConfig.DEBUG; 22 | } 23 | 24 | @Override 25 | protected List getPackages() { 26 | @SuppressWarnings("UnnecessaryLocalVariable") 27 | List packages = new PackageList(this).getPackages(); 28 | // Packages that cannot be autolinked yet can be added manually here, for example: 29 | // packages.add(new MyReactNativePackage()); 30 | return packages; 31 | } 32 | 33 | @Override 34 | protected String getJSMainModuleName() { 35 | return "index"; 36 | } 37 | }; 38 | 39 | @Override 40 | public ReactNativeHost getReactNativeHost() { 41 | return mReactNativeHost; 42 | } 43 | 44 | @Override 45 | public void onCreate() { 46 | super.onCreate(); 47 | SoLoader.init(this, /* native exopackage */ false); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Fluid Transitions 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "28.0.3" 6 | minSdkVersion = 16 7 | compileSdkVersion = 28 8 | targetSdkVersion = 28 9 | supportLibVersion = "28.0.0" 10 | } 11 | repositories { 12 | google() 13 | jcenter() 14 | } 15 | dependencies { 16 | classpath("com.android.tools.build:gradle:3.4.1") 17 | 18 | // NOTE: Do not place your application dependencies here; they belong 19 | // in the individual module build.gradle files 20 | } 21 | } 22 | 23 | allprojects { 24 | repositories { 25 | mavenLocal() 26 | maven { 27 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 28 | url("$rootDir/../node_modules/react-native/android") 29 | } 30 | maven { 31 | // Android JSC is installed from npm 32 | url("$rootDir/../node_modules/jsc-android/dist") 33 | } 34 | 35 | google() 36 | jcenter() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'FluidTransitions' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FluidTransitions", 3 | "displayName": "FluidTransitions" 4 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = api => { 2 | api.cache(false); 3 | return { 4 | presets: ["module:metro-react-native-babel-preset"], 5 | plugins: [ 6 | [ 7 | "module-resolver", 8 | { 9 | alias: { 10 | "react-native-fluid-transitions": "./src/packages/transitions", 11 | "react-native-fluid-animations": "./src/packages/animated", 12 | "react-native-fluid-svg": "./src/packages/svg", 13 | "react-native-fluid-gestures": "./src/packages/gestures", 14 | "react-native-fluid-navigation": "./src/packages/navigation", 15 | }, 16 | }, 17 | ], 18 | ], 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import {AppRegistry} from 'react-native'; 2 | import App from './src/App'; 3 | import {name as appName} from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | -------------------------------------------------------------------------------- /ios/FluidTransitions.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/FluidTransitions.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/FluidTransitions/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-1024px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-1024px.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-120px-40pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-120px-40pt@3x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-120px-60pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-120px-60pt@2x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-180px-60pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-180px-60pt@3x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-29px-29pt@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-29px-29pt@1x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-40px-20pt@2x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-58px-29pt@2x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-60px-20pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-60px-20pt@3x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-80px-40pt@2x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-87px-29pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/AppIcon-87px-29pt@3x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "AppIcon-40px-20pt@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "AppIcon-60px-20pt@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "AppIcon-29px-29pt@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "AppIcon-58px-29pt@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "AppIcon-87px-29pt@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "AppIcon-80px-40pt@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "AppIcon-120px-40pt@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "AppIcon-120px-60pt@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "AppIcon-180px-60pt@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "1024x1024", 59 | "idiom" : "ios-marketing", 60 | "filename" : "AppIcon-1024px.png", 61 | "scale" : "1x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/Logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo-symbol-text@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "logo-symbol-text@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "logo-symbol-text@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/Logo.imageset/logo-symbol-text@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/Logo.imageset/logo-symbol-text@1x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/Logo.imageset/logo-symbol-text@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/Logo.imageset/logo-symbol-text@2x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/Images.xcassets/Logo.imageset/logo-symbol-text@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/ios/FluidTransitions/Images.xcassets/Logo.imageset/logo-symbol-text@3x.png -------------------------------------------------------------------------------- /ios/FluidTransitions/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | const blacklist = require("metro-config/src/defaults/blacklist"); 8 | 9 | module.exports = { 10 | resolver: { blacklistRE: blacklist([/src\/web\/.*/]) }, 11 | transformer: { 12 | getTransformOptions: async () => ({ 13 | transform: { 14 | experimentalImportSupport: false, 15 | inlineRequires: false, 16 | }, 17 | }), 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import App from './examples'; 2 | export default App; 3 | -------------------------------------------------------------------------------- /src/examples/Assets/logo-symbol-text@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Assets/logo-symbol-text@1x.png -------------------------------------------------------------------------------- /src/examples/Assets/logo-symbol-text@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Assets/logo-symbol-text@2x.png -------------------------------------------------------------------------------- /src/examples/Assets/logo-symbol-text@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Assets/logo-symbol-text@3x.png -------------------------------------------------------------------------------- /src/examples/Children/bubble.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet } from "react-native"; 3 | import Fluid, { 4 | useFluidConfig, 5 | AnimationType, 6 | OnEnterState, 7 | WhenState, 8 | } from "react-native-fluid-transitions"; 9 | 10 | type Props = { 11 | label: string; 12 | color: string; 13 | active: boolean; 14 | }; 15 | 16 | const Bubble: React.FunctionComponent = ({ label, color, active }) => { 17 | const activeState = { name: "active", active }; 18 | const config = useFluidConfig( 19 | AnimationType(Fluid.Animations.Springs.Gentle), 20 | WhenState(activeState, { 21 | opacity: 0, 22 | transform: [{ scale: 0.009 }, { translateY: -64 }], 23 | }), 24 | OnEnterState(Fluid.States.Mounted, { 25 | inputRange: [0, 0.5, 1], 26 | outputRange: [1, 0.5, 1], 27 | styleKey: "transform.scaleY", 28 | }), 29 | ); 30 | 31 | return ( 32 | 40 | ); 41 | }; 42 | 43 | const styles = StyleSheet.create({ 44 | container: { 45 | opacity: 1, 46 | width: 20, 47 | height: 20, 48 | borderRadius: 10, 49 | margin: 10, 50 | }, 51 | containerInitialStyle: { 52 | opacity: 0, 53 | transform: [{ scale: 0.009 }, { translateY: -64 }], 54 | }, 55 | }); 56 | 57 | export { Bubble }; 58 | -------------------------------------------------------------------------------- /src/examples/Driver/MovingButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet, Text } from "react-native"; 3 | import Fluid, { useFluidConfig } from "react-native-fluid-transitions"; 4 | import * as Colors from "../colors"; 5 | 6 | type Props = { 7 | toggled: boolean; 8 | onToggle: () => void; 9 | }; 10 | const MovingButton: React.FunctionComponent = ({ 11 | toggled, 12 | onToggle, 13 | }) => { 14 | const toggledState = { name: "toggled", active: toggled }; 15 | const notToggledState = { name: "nottoggled", active: !toggled }; 16 | 17 | const config = useFluidConfig({ 18 | animation: Fluid.Animations.Springs.WobblySlow, 19 | when: [ 20 | { state: toggledState, style: styles.activeButton }, 21 | { state: notToggledState, style: styles.inactiveButton }, 22 | ], 23 | }); 24 | 25 | return ( 26 | 33 | Tap me! 34 | 35 | ); 36 | }; 37 | 38 | const styles = StyleSheet.create({ 39 | button: { 40 | borderColor: Colors.ColorA, 41 | borderWidth: 4, 42 | alignItems: "center", 43 | justifyContent: "center", 44 | borderRadius: 8, 45 | padding: 12, 46 | marginTop: 20, 47 | marginBottom: 15, 48 | backgroundColor: "white", 49 | }, 50 | initialStyle: { 51 | transform: [{ scale: 0.009 }], 52 | }, 53 | activeButton: { 54 | transform: [{ rotate: "-15deg" }, { translateX: 100 }], 55 | }, 56 | inactiveButton: { 57 | transform: [{ rotate: "15deg" }, { translateX: -100 }], 58 | }, 59 | pressed: { 60 | backgroundColor: "#CCC", 61 | }, 62 | }); 63 | 64 | export default MovingButton; 65 | -------------------------------------------------------------------------------- /src/examples/Flutter/assets/earrings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Flutter/assets/earrings.png -------------------------------------------------------------------------------- /src/examples/Flutter/assets/hat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Flutter/assets/hat.png -------------------------------------------------------------------------------- /src/examples/Flutter/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Flutter/assets/icon.png -------------------------------------------------------------------------------- /src/examples/Flutter/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Flutter/assets/splash.png -------------------------------------------------------------------------------- /src/examples/Flutter/assets/sunnies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Flutter/assets/sunnies.png -------------------------------------------------------------------------------- /src/examples/Flutter/assets/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fram-x/react-native-fluid/22f39543059f7c38709f55b9f2eb928ae42f8ab8/src/examples/Flutter/assets/table.png -------------------------------------------------------------------------------- /src/examples/Flutter/components/Cursor.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet, Dimensions } from "react-native"; 3 | 4 | import { CURSOR_WIDTH } from "./Model"; 5 | 6 | type CursorProps = {}; 7 | 8 | const { width } = Dimensions.get("window"); 9 | 10 | export default class Cursor extends React.PureComponent { 11 | render() { 12 | return ( 13 | 14 | 15 | 16 | ); 17 | } 18 | } 19 | 20 | const styles = StyleSheet.create({ 21 | container: { 22 | flex: 1, 23 | width, 24 | justifyContent: "center", 25 | alignItems: "center", 26 | }, 27 | cursor: { 28 | top: 30, 29 | width: CURSOR_WIDTH, 30 | height: 5, 31 | backgroundColor: "white", 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /src/examples/Flutter/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet, Image, Dimensions } from "react-native"; 3 | import { Section } from "./Model"; 4 | import Svg, { LinearGradient, Stop, Rect } from "react-native-svg"; 5 | 6 | type HeaderProps = { 7 | section: Section; 8 | }; 9 | 10 | const { width } = Dimensions.get("window"); 11 | 12 | export default class Header extends React.PureComponent { 13 | render() { 14 | const { section } = this.props; 15 | const colors = [section.leftColor, section.rightColor]; 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | } 30 | 31 | const styles = StyleSheet.create({ 32 | container: { 33 | flex: 1, 34 | width, 35 | }, 36 | image: { 37 | ...StyleSheet.absoluteFillObject, 38 | width: null, 39 | height: null, 40 | }, 41 | gradient: { 42 | ...StyleSheet.absoluteFillObject, 43 | opacity: 0.9, 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /src/examples/Flutter/components/MockCard.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet, Image } from "react-native"; 3 | 4 | type MockCardProps = { 5 | image: string; 6 | }; 7 | 8 | export default class MockCard extends React.PureComponent { 9 | render() { 10 | const { image: source } = this.props; 11 | return ( 12 | 13 | 14 | 15 | ); 16 | } 17 | } 18 | 19 | const styles = StyleSheet.create({ 20 | container: {}, 21 | image: { 22 | borderRadius: 5, 23 | height: 200, 24 | width: null, 25 | resizeMode: "cover", 26 | margin: 8, 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /src/examples/Flutter/components/MockEntry.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, Image, Text, StyleSheet } from "react-native"; 3 | 4 | type MockEntryProps = { 5 | image: string; 6 | }; 7 | 8 | export default class MockEntry extends React.PureComponent { 9 | render() { 10 | const { image: source } = this.props; 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | React Native enables interactive animation 18 | 3K views - 5 days 19 | 20 | 21 | ); 22 | } 23 | } 24 | 25 | const styles = StyleSheet.create({ 26 | container: { 27 | flexDirection: "row", 28 | }, 29 | leftCell: { 30 | padding: 8, 31 | }, 32 | rightCell: { 33 | paddingVertical: 8, 34 | paddingRight: 8, 35 | justifyContent: "center", 36 | }, 37 | image: { 38 | width: 45, 39 | height: 45, 40 | borderRadius: 5, 41 | }, 42 | subtitle: { 43 | color: "gray", 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /src/examples/Flutter/components/Model.ts: -------------------------------------------------------------------------------- 1 | export const PADDING = 8; 2 | export const CURSOR_WIDTH = 50; 3 | export const SMALL_HEADER_HEIGHT = 109; 4 | export const MEDIUM_HEADER_HEIGHT = 300; 5 | 6 | export type Section = { 7 | title: string; 8 | leftColor: string; 9 | rightColor: string; 10 | image: string; 11 | }; 12 | -------------------------------------------------------------------------------- /src/examples/Flutter/components/Pages.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet, View, Dimensions } from "react-native"; 3 | import Animated from "react-native-reanimated"; 4 | 5 | import { Section, SMALL_HEADER_HEIGHT } from "./Model"; 6 | import MockEntry from "./MockEntry"; 7 | import MockCard from "./MockCard"; 8 | 9 | type PagesProps = { 10 | sections: Section[]; 11 | x: Animated.Value; 12 | y: Animated.Value; 13 | }; 14 | 15 | const { height, width } = Dimensions.get("window"); 16 | const { multiply } = Animated; 17 | export default class Pages extends React.PureComponent { 18 | render() { 19 | const { sections, y, x } = this.props; 20 | const translateX = multiply(x, -1); 21 | const translateY = multiply(y, -1); 22 | return ( 23 | 24 | {sections.map(({ image }, key) => ( 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ))} 39 | 40 | ); 41 | } 42 | } 43 | 44 | const styles = StyleSheet.create({ 45 | container: { 46 | flexDirection: "row", 47 | }, 48 | page: { 49 | backgroundColor: "white", 50 | width, 51 | height: height - SMALL_HEADER_HEIGHT, 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /src/examples/Flutter/components/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Sections } from "./Sections"; 2 | -------------------------------------------------------------------------------- /src/examples/Flutter/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Sections, Section } from "./components"; 3 | 4 | const mariner = "#3B5F8F"; 5 | const mediumPurple = "#8266D4"; 6 | const tomato = "#F95B57"; 7 | const mySin = "#F3A646"; 8 | 9 | const sections: Section[] = [ 10 | { 11 | title: "SUNGLASSES", 12 | leftColor: mediumPurple, 13 | rightColor: mariner, 14 | image: require("./assets/sunnies.png"), 15 | }, 16 | { 17 | title: "FURNITURE", 18 | leftColor: tomato, 19 | rightColor: mediumPurple, 20 | image: require("./assets/table.png"), 21 | }, 22 | { 23 | title: "JEWELRY", 24 | leftColor: mySin, 25 | rightColor: tomato, 26 | image: require("./assets/earrings.png"), 27 | }, 28 | { 29 | title: "HEADWEAR", 30 | leftColor: "white", 31 | rightColor: tomato, 32 | image: require("./assets/hat.png"), 33 | }, 34 | ]; 35 | 36 | export default () => { 37 | return ; 38 | }; 39 | -------------------------------------------------------------------------------- /src/examples/HomeScreenButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Text, TouchableOpacity, StyleSheet, Dimensions } from "react-native"; 3 | import Icon from "react-native-vector-icons/MaterialCommunityIcons"; 4 | import * as Colors from "./colors"; 5 | 6 | interface CardProps { 7 | text: string; 8 | icon: string; 9 | onPress: () => void; 10 | } 11 | 12 | const HomeScreenButton = (props: CardProps) => { 13 | const { text, icon, onPress } = props; 14 | return ( 15 | 16 | 17 | {text} 18 | 19 | ); 20 | }; 21 | 22 | const { width } = Dimensions.get("window"); 23 | 24 | const styles = StyleSheet.create({ 25 | card: { 26 | alignItems: "center", 27 | justifyContent: "center", 28 | width: width / 3 - 30, 29 | height: width / 3 - 30, 30 | borderColor: "#444", 31 | borderWidth: StyleSheet.hairlineWidth, 32 | borderRadius: 4, 33 | margin: 10, 34 | }, 35 | cardIcon: { 36 | marginBottom: 10, 37 | color: Colors.ColorA, 38 | }, 39 | cardText: { 40 | textAlign: "center", 41 | }, 42 | }); 43 | 44 | export default HomeScreenButton; 45 | -------------------------------------------------------------------------------- /src/examples/Interactions/AnimatedNumber.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet, Text, Platform } from "react-native"; 3 | import Fluid from "react-native-fluid-transitions"; 4 | 5 | type AnimatedNumberProps = { 6 | value: number; 7 | }; 8 | 9 | const AnimatedNumber: React.FC = ({ value }) => { 10 | const digits = value.toString().split(""); 11 | return ( 12 | 13 | {digits.map((s, index) => ( 14 | 15 | ))} 16 | 17 | ); 18 | }; 19 | 20 | type NumberProps = { 21 | value: number; 22 | }; 23 | const Number: React.FC = ({ value }) => { 24 | const style = { 25 | transform: [{ translateY: -(value * boxHeight) }], 26 | }; 27 | return ( 28 | 31 | {[...Array(10).keys()].map((_, index) => ( 32 | 33 | {index} 34 | 35 | ))} 36 | 37 | ); 38 | }; 39 | 40 | const boxHeight = Platform.select({ default: 24, android: 28 }); 41 | const styles = StyleSheet.create({ 42 | container: { 43 | flexDirection: "row", 44 | height: boxHeight, 45 | overflow: "hidden", 46 | }, 47 | numberContainer: { 48 | height: boxHeight, 49 | }, 50 | digit: { 51 | height: boxHeight, 52 | fontFamily: "Arial", 53 | fontSize: 24, 54 | }, 55 | }); 56 | 57 | export { AnimatedNumber }; 58 | -------------------------------------------------------------------------------- /src/examples/Interactions/FloatingLabel.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Platform, TextInput, StyleSheet, View } from "react-native"; 3 | import Fluid from "react-native-fluid-transitions"; 4 | import { ColorA } from "../colors"; 5 | 6 | type Props = { 7 | placeholder?: string; 8 | }; 9 | const FloatingLabel: React.FC = ({ placeholder }) => { 10 | const [value, setValue] = useState(""); 11 | const onChangeText = (t: string) => setValue(t); 12 | return ( 13 | 14 | {/* Placeholder */} 15 | 16 | {placeholder} 17 | 18 | {/* Input */} 19 | 25 | 26 | ); 27 | }; 28 | 29 | const styles = StyleSheet.create({ 30 | container: { 31 | paddingTop: 8, 32 | justifyContent: "center", 33 | borderBottomColor: "#333", 34 | borderBottomWidth: StyleSheet.hairlineWidth, 35 | minWidth: 130, 36 | }, 37 | placeholderEmpty: { 38 | ...StyleSheet.absoluteFillObject, 39 | marginVertical: 3, 40 | transform: [{ translateY: Platform.select({ default: 4, android: 8 }) }], 41 | color: "#555", 42 | fontSize: 14, 43 | }, 44 | placeholder: { 45 | ...StyleSheet.absoluteFillObject, 46 | marginVertical: 3, 47 | transform: [{ translateY: -10 }], 48 | color: ColorA, 49 | fontSize: 10, 50 | }, 51 | input: { 52 | alignSelf: "stretch", 53 | padding: 0, 54 | margin: 0, 55 | }, 56 | }); 57 | 58 | export { FloatingLabel }; 59 | -------------------------------------------------------------------------------- /src/examples/Interactions/InteractionContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet, Dimensions, Text } from "react-native"; 3 | import Fluid from "react-native-fluid-transitions"; 4 | 5 | type Props = { 6 | text: string; 7 | onPress?: () => void; 8 | }; 9 | const InteractionContainer: React.FC = ({ text, onPress, children }) => { 10 | return ( 11 | 12 | {children} 13 | 14 | {text} 15 | 16 | 17 | ); 18 | }; 19 | 20 | const { width } = Dimensions.get("window"); 21 | 22 | const styles = StyleSheet.create({ 23 | container: { 24 | width: width / 2 - 30, 25 | height: width / 2 - 30, 26 | borderColor: "#444", 27 | borderWidth: StyleSheet.hairlineWidth, 28 | borderRadius: 4, 29 | margin: 10, 30 | overflow: "hidden" 31 | }, 32 | controlContainer: { 33 | alignItems: "center", 34 | justifyContent: "center", 35 | alignSelf: "stretch", 36 | flex: 1 37 | }, 38 | textContainer: { 39 | alignItems: "center", 40 | justifyContent: "center", 41 | padding: 4 42 | }, 43 | text: { 44 | textAlign: "center", 45 | fontSize: 11 46 | } 47 | }); 48 | 49 | export { InteractionContainer }; 50 | -------------------------------------------------------------------------------- /src/examples/Interactions/LikeHeart.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { StyleSheet, View, TextStyle } from "react-native"; 3 | import Fluid, { createFluidComponent } from "react-native-fluid-transitions"; 4 | import Icon from "react-native-vector-icons/MaterialCommunityIcons"; 5 | import { IconProps } from "react-native-vector-icons/Icon"; 6 | import { ColorA } from "../colors"; 7 | 8 | const FluidIcon = createFluidComponent(Icon, false); 9 | 10 | const LikeHeart = () => { 11 | const [likes, setLikes] = useState(0); 12 | const state = { name: "likes", active: true, value: likes }; 13 | const onLike = () => setLikes(l => l + 1); 14 | const config = Fluid.createConfig({ 15 | onEnter: { 16 | state: "likes", 17 | interpolation: { 18 | styleKey: "transform.scale", 19 | outputRange: [1, 1.5, 1], 20 | }, 21 | animation: Fluid.Animations.Springs.Gentle, 22 | }, 23 | }); 24 | return ( 25 | 26 | 34 | 35 | ); 36 | }; 37 | 38 | const styles = StyleSheet.create({ 39 | container: { 40 | flexDirection: "row", 41 | alignItems: "center", 42 | justifyContent: "center", 43 | width: 44, 44 | height: 44, 45 | }, 46 | }); 47 | 48 | export { LikeHeart }; 49 | -------------------------------------------------------------------------------- /src/examples/Maze/MazeItem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View } from "react-native"; 3 | import { styles } from "./styles"; 4 | import Fluid from "react-native-fluid-transitions"; 5 | import { Easings } from "react-native-fluid-transitions"; 6 | 7 | interface MazeItemProps { 8 | size: number; 9 | index: number; 10 | isSet: boolean; 11 | } 12 | 13 | export const MazeItem: React.FC = ({ isSet, index, size }) => { 14 | return ( 15 | 16 | 22 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /src/examples/Maze/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef } from "react"; 2 | import { Dimensions } from "react-native"; 3 | import Fluid from "react-native-fluid-transitions"; 4 | import { styles } from "./styles"; 5 | import { MazeItem } from "./MazeItem"; 6 | 7 | const columns = 6; 8 | 9 | const { width, height } = Dimensions.get("window"); 10 | const boxSize = width / columns; 11 | 12 | const createMaze = () => { 13 | const nextMaze: boolean[] = []; 14 | for (let y = 0; y < height / boxSize; y++) { 15 | for (let x = 0; x < columns; x++) { 16 | nextMaze.push(Math.random() < 0.5); 17 | } 18 | } 19 | return nextMaze; 20 | }; 21 | 22 | const swapMaze = (maze: Array) => { 23 | const nextMaze: Array = []; 24 | let i = 0; 25 | for (let y = 0; y < height / boxSize; y++) { 26 | for (let x = 0; x < columns; x++) { 27 | nextMaze.push(!maze[i++]); 28 | } 29 | } 30 | return nextMaze; 31 | }; 32 | 33 | const MazeExampleScreen = () => { 34 | const [maze, setMaze] = useState(() => createMaze()); 35 | const cur = useRef(1); 36 | if (cur.current === 0) { 37 | cur.current = 1; 38 | } else { 39 | cur.current = 0; 40 | } 41 | const toggleLabyrinth = () => { 42 | setMaze(swapMaze(maze)); 43 | }; 44 | 45 | return ( 46 | 51 | {/* */} 52 | {maze.map((b, index) => ( 53 | 54 | ))} 55 | 56 | ); 57 | }; 58 | 59 | MazeExampleScreen.navigationOptions = { 60 | title: "Maze", 61 | }; 62 | 63 | export default MazeExampleScreen; 64 | -------------------------------------------------------------------------------- /src/examples/Maze/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from "react-native"; 2 | import { ColorC } from "../colors"; 3 | 4 | export const styles = StyleSheet.create({ 5 | container: { 6 | flex: 1, 7 | flexDirection: "row", 8 | flexWrap: "wrap" 9 | }, 10 | box: { 11 | alignItems: "center", 12 | justifyContent: "center", 13 | backgroundColor: ColorC, 14 | overflow: "hidden" 15 | }, 16 | line: { 17 | width: 2, 18 | backgroundColor: "#000" 19 | }, 20 | setBox: { 21 | transform: [{ rotate: "-45deg" }] 22 | }, 23 | offsetBox: { 24 | transform: [{ rotate: "45deg" }] 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /src/examples/Navigation/box.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Fluid from "react-native-fluid-transitions"; 3 | import { useHorizontalTransition } from "react-native-fluid-navigation"; 4 | import { Dimensions, StyleSheet } from "react-native"; 5 | 6 | type Props = { 7 | color: string; 8 | }; 9 | 10 | export const Box: React.FC = ({ color }) => { 11 | const horizontalTransition = useHorizontalTransition( 12 | Dimensions.get("screen").width, 13 | ); 14 | 15 | return ( 16 | 20 | ); 21 | }; 22 | 23 | const styles = StyleSheet.create({ 24 | container: { 25 | width: 40, 26 | height: 40, 27 | margin: 20, 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /src/examples/Navigation/bubble.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Fluid from "react-native-fluid-transitions"; 3 | import { useHorizontalTransition } from "react-native-fluid-navigation"; 4 | import { Dimensions, StyleSheet } from "react-native"; 5 | 6 | type Props = { 7 | color: string; 8 | }; 9 | 10 | export const Bubble: React.FC = ({ color }) => { 11 | const horizontalTransition = useHorizontalTransition( 12 | 30 + Dimensions.get("screen").width / 2, 13 | ); 14 | 15 | return ( 16 | 20 | ); 21 | }; 22 | 23 | const styles = StyleSheet.create({ 24 | container: { 25 | width: 40, 26 | height: 40, 27 | borderRadius: 20, 28 | margin: 20, 29 | }, 30 | }); 31 | -------------------------------------------------------------------------------- /src/examples/Navigation/button.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { StyleSheet, Dimensions } from "react-native"; 3 | import Fluid from "react-native-fluid-transitions"; 4 | import { ColorC, ColorE } from "../colors"; 5 | import { useHorizontalTransition } from "react-native-fluid-navigation"; 6 | 7 | const styles = StyleSheet.create({ 8 | container: { 9 | alignItems: "center", 10 | justifyContent: "center", 11 | padding: 14, 12 | }, 13 | active: { 14 | backgroundColor: ColorE, 15 | width: 200, 16 | height: 50, 17 | borderRadius: 25, 18 | }, 19 | inactive: { 20 | backgroundColor: ColorC, 21 | width: 300, 22 | height: 50, 23 | borderRadius: 0, 24 | }, 25 | }); 26 | 27 | const AnimatedButton: React.FC = ({ children }) => { 28 | const [active, setActive] = useState(true); 29 | const toggle = () => setActive(p => !p); 30 | const horizontalTransition = useHorizontalTransition( 31 | Dimensions.get("window").width, 32 | ); 33 | return ( 34 | 39 | {children} 40 | 41 | ); 42 | }; 43 | 44 | export { AnimatedButton }; 45 | -------------------------------------------------------------------------------- /src/examples/Navigation/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FluidNavigationContainer } from "react-native-fluid-navigation"; 3 | import { createFluidStackNavigator } from "react-native-fluid-navigation"; 4 | 5 | import { Screen } from "./screen"; 6 | import { ColorA, ColorB, ColorC, ColorE } from "../colors"; 7 | 8 | const NavigationExampleScreen = createFluidStackNavigator({ 9 | screen1: () => ( 10 | 11 | 18 | 19 | ), 20 | 21 | screen2: () => ( 22 | 23 | 32 | 33 | ), 34 | 35 | screen3: () => ( 36 | 37 | 45 | 46 | ), 47 | 48 | screen4: () => ( 49 | 50 | 58 | 59 | ), 60 | }); 61 | 62 | export default NavigationExampleScreen; 63 | -------------------------------------------------------------------------------- /src/examples/Repeating/Repeater.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Fluid, { Easings, useFluidConfig } from "react-native-fluid-transitions"; 3 | import { StyleSheet } from "react-native"; 4 | 5 | type Props = { 6 | loop: number; 7 | flip: number; 8 | yoyo: number; 9 | color: string; 10 | isRepeating: boolean; 11 | }; 12 | const styles = StyleSheet.create({ 13 | container: { 14 | width: 30, 15 | height: 30, 16 | borderRadius: 15, 17 | marginBottom: 3, 18 | }, 19 | }); 20 | 21 | export const Repeater: React.FC = ({ 22 | loop, 23 | flip, 24 | yoyo, 25 | color, 26 | isRepeating, 27 | }) => { 28 | const state = { name: "repeat", active: isRepeating }; 29 | const config = useFluidConfig({ 30 | when: { 31 | animation: { 32 | type: "timing", 33 | duration: 1200, 34 | easing: Easings.back(), 35 | }, 36 | state: state, 37 | flip, 38 | yoyo, 39 | loop, 40 | interpolation: { 41 | outputRange: [-50, 0, 50], 42 | styleKey: "transform.translateX", 43 | }, 44 | }, 45 | }); 46 | return ( 47 | 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /src/examples/SVG/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { StyleSheet, View, Dimensions } from "react-native"; 3 | import Fluid from "react-native-fluid-svg"; 4 | import { ColorC } from "../colors"; 5 | import Svg, { Defs, Stop } from "react-native-svg"; 6 | 7 | const SvgExampleScreen = () => { 8 | const [isToggled, setIsToggled] = useState(false); 9 | const toggle = () => setIsToggled(p => !p); 10 | const { width, height } = Dimensions.get("window"); 11 | return ( 12 | 13 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | SvgExampleScreen.navigationOptions = { 41 | title: "SVG", 42 | }; 43 | 44 | const styles = StyleSheet.create({ 45 | container: { 46 | justifyContent: "center", 47 | alignItems: "center", 48 | padding: 40, 49 | flex: 1, 50 | }, 51 | }); 52 | 53 | export default SvgExampleScreen; 54 | -------------------------------------------------------------------------------- /src/examples/Styles/Pager.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet } from "react-native"; 3 | import Fluid from "react-native-fluid-transitions"; 4 | import * as Colors from "../colors"; 5 | 6 | const PagerItem: React.FunctionComponent<{ 7 | active: boolean; 8 | label: string; 9 | }> = ({ active, label }) => { 10 | const style = [styles.item, active ? styles.itemActive : styles.itemInactive]; 11 | return ; 12 | }; 13 | 14 | const Pager: React.FunctionComponent<{ 15 | activeIndex: number; 16 | count: number; 17 | }> = ({ activeIndex, count }) => { 18 | const items = Array.from({ length: count }, (_, k) => k); 19 | return ( 20 | 21 | {items.map(i => ( 22 | 27 | ))} 28 | 29 | ); 30 | }; 31 | 32 | const styles = StyleSheet.create({ 33 | container: { 34 | flexDirection: "row", 35 | alignItems: "center", 36 | justifyContent: "center", 37 | marginBottom: 24 38 | }, 39 | item: { 40 | borderRadius: 5, 41 | height: 10, 42 | marginHorizontal: 10 43 | }, 44 | itemActive: { 45 | backgroundColor: Colors.ColorA, 46 | width: 30 47 | }, 48 | itemInactive: { 49 | backgroundColor: "#CCC", 50 | width: 10 51 | } 52 | }); 53 | 54 | export { Pager, PagerItem }; 55 | -------------------------------------------------------------------------------- /src/examples/Text/Ticker.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Fluid, { Staggered } from "react-native-fluid-transitions"; 3 | import { Text, StyleSheet } from "react-native"; 4 | 5 | const Ticker: React.FunctionComponent<{ 6 | text: string; 7 | appear: any; 8 | }> = ({ text, appear }) => { 9 | return ( 10 | 14 | {text.split("").map((s: string, index: number) => 15 | s === " " ? ( 16 | 17 | ) : ( 18 | 22 | {s} 23 | 24 | ), 25 | )} 26 | 27 | ); 28 | }; 29 | 30 | const styles = StyleSheet.create({ 31 | container: { 32 | borderColor: "#CCC", 33 | borderWidth: StyleSheet.hairlineWidth, 34 | overflow: "hidden", 35 | justifyContent: "center", 36 | alignItems: "center", 37 | flexDirection: "row", 38 | marginTop: 20, 39 | padding: 20, 40 | }, 41 | }); 42 | 43 | export default Ticker; 44 | -------------------------------------------------------------------------------- /src/examples/colors.ts: -------------------------------------------------------------------------------- 1 | export const ColorA = "#B52B38"; 2 | export const ColorB = "#E17140"; 3 | export const ColorC = "#FBB958"; 4 | export const ColorD = "#C8CB55"; 5 | export const ColorE = "#6B7449"; 6 | -------------------------------------------------------------------------------- /src/examples/helpers.ts: -------------------------------------------------------------------------------- 1 | import { Dimensions } from "react-native"; 2 | 3 | export const generateRandomImageUri = (width?: number, height?: number) => { 4 | const randomNumber = Math.floor(Math.random() * 100 + 1); 5 | return generateImageUri(randomNumber, width, height); 6 | }; 7 | 8 | export const generateImageUri = ( 9 | imageId: number, 10 | width?: number, 11 | height?: number 12 | ) => { 13 | const size = Dimensions.get("window"); 14 | const imageHeight = Math.round(height || size.width * 0.4); 15 | const imageWidth = Math.round(width || size.width); 16 | return `https://picsum.photos/${imageWidth}/${imageHeight}?image=${imageId}`; 17 | }; 18 | -------------------------------------------------------------------------------- /src/packages/animated/index.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationProvider } from "./src/Types/IAnimationProvider"; 2 | import { NativeModules } from "react-native"; 3 | 4 | const _renimatedAvailable = 5 | // false && 6 | NativeModules !== undefined && NativeModules.ReanimatedModule !== undefined; 7 | 8 | console.log( 9 | `**** Render engine ${ 10 | _renimatedAvailable ? "react-native-reanimated" : "React Native Animated" 11 | }`, 12 | ); 13 | export const AnimationProvider: IAnimationProvider = _renimatedAvailable 14 | ? require("./src/react-native-reanimated").ReanimatedAnimationProvider 15 | : require("./src/react-native-animated").ReactNativeAnimationProvider; 16 | 17 | export * from "./src/Types"; 18 | -------------------------------------------------------------------------------- /src/packages/animated/index.web.ts: -------------------------------------------------------------------------------- 1 | import { ReactNativeAnimationProvider } from "./src/react-native-animated"; 2 | 3 | export default ReactNativeAnimationProvider; 4 | 5 | export * from "./src/Types"; 6 | -------------------------------------------------------------------------------- /src/packages/animated/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-fluid-animations", 3 | "version": "0.1.1", 4 | "description": "Animations for animated / reanimated", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/fram-x/react-native-fluid.git" 8 | }, 9 | "main": "dist/index.js", 10 | "types": "dist/index.d.ts", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "author": "Fram X AS / Christian Falch", 15 | "license": "MIT" 16 | } 17 | -------------------------------------------------------------------------------- /src/packages/animated/src/Types/IAnimationNode.ts: -------------------------------------------------------------------------------- 1 | export interface IAnimationNode {} 2 | -------------------------------------------------------------------------------- /src/packages/animated/src/Types/IAnimationValue.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "./IAnimationNode"; 2 | 3 | export interface IAnimationValue extends IAnimationNode { 4 | setValue: (value: any) => void; 5 | } 6 | -------------------------------------------------------------------------------- /src/packages/animated/src/Types/Interpolation.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "./IAnimationNode"; 2 | 3 | export type ExtrapolateType = "extend" | "identity" | "clamp"; 4 | 5 | export type InterpolationConfig = { 6 | inputRange?: number[]; 7 | outputRange: Array; 8 | extrapolate?: ExtrapolateType; 9 | extrapolateLeft?: ExtrapolateType; 10 | extrapolateRight?: ExtrapolateType; 11 | }; 12 | -------------------------------------------------------------------------------- /src/packages/animated/src/Types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./IAnimationNode"; 2 | export * from "./IAnimationProvider"; 3 | export * from "./IAnimationValue"; 4 | export * from "./Interpolation"; 5 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/ColorNode.ts: -------------------------------------------------------------------------------- 1 | import { Animated } from "react-native"; 2 | 3 | export class ColorNode extends Animated.Value { 4 | constructor(parent: Animated.Value) { 5 | // @ts-ignore 6 | super(parent.__getValue()); 7 | this._parent = parent; 8 | } 9 | 10 | _parent: Animated.Value; 11 | 12 | __attach(): void { 13 | // @ts-ignore 14 | this._parent.__addChild(this); 15 | } 16 | 17 | __detach(): void { 18 | // @ts-ignore 19 | this._parent.__removeChild(this); 20 | // @ts-ignore 21 | super.__detach(); 22 | } 23 | 24 | setValue = (value: number) => { 25 | if (Number.isNaN(value)) { 26 | throw new Error("Value is not a number"); 27 | } 28 | this._parent.setValue(value); 29 | }; 30 | 31 | __getValue = () => { 32 | // @ts-ignore 33 | const l = this._parent.__getValue(); 34 | // const r = (l >> 24) & 0xff; 35 | // const g = (l >> 16) & 0xff; 36 | // const b = (l >> 8) & 0xff; 37 | // const a = l & 0xff; 38 | const clr = `rgba(${(l >> 24) & 0xff},${(l >> 16) & 0xff},${(l >> 8) & 39 | 0xff},${l & 0xff})`; 40 | return clr; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/RadNode.ts: -------------------------------------------------------------------------------- 1 | import { Animated } from "react-native"; 2 | 3 | export class RadNode extends Animated.Value { 4 | constructor(parent: Animated.Value) { 5 | // @ts-ignore 6 | super(parent.__getValue()); 7 | this._parent = parent; 8 | } 9 | 10 | _parent: Animated.Value; 11 | 12 | __attach(): void { 13 | // @ts-ignore 14 | this._parent.__addChild(this); 15 | } 16 | 17 | __detach(): void { 18 | // @ts-ignore 19 | this._parent.__removeChild(this); 20 | // @ts-ignore 21 | super.__detach(); 22 | } 23 | 24 | setValue = (value: number) => { 25 | if (Number.isNaN(value)) { 26 | throw new Error("Value is not a number"); 27 | } 28 | this._parent.setValue(value); 29 | }; 30 | 31 | __getValue = () => { 32 | // @ts-ignore 33 | return this._parent.__getValue() + "rad"; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/bezier.ts: -------------------------------------------------------------------------------- 1 | export function bezier(mX1: number, mY1: number, mX2: number, mY2: number) { 2 | // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). 3 | const cx = 3.0 * mX1; 4 | const bx = 3.0 * (mX2 - mX1) - cx; 5 | const ax = 1.0 - cx - bx; 6 | 7 | const cy = 3.0 * mY1; 8 | const by = 3.0 * (mY2 - mY1) - cy; 9 | const ay = 1.0 - cy - by; 10 | 11 | const sampleCurveX = (t: number) => { 12 | // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. 13 | return ((ax * t + bx) * t + cx) * t; 14 | }; 15 | 16 | const sampleCurveY = (t: number) => { 17 | return ((ay * t + by) * t + cy) * t; 18 | }; 19 | 20 | const sampleCurveDerivativeX = (t: number) => { 21 | return (3.0 * ax * t + 2.0 * bx) * t + cx; 22 | }; 23 | 24 | const solveCurveX = (x: number, epsilon: number) => { 25 | let t0, t1, t2, x2, d2: number; 26 | let i: number; 27 | 28 | // First try a few iterations of Newton's method -- normally very fast. 29 | for (t2 = x, i = 0; i < 8; i++) { 30 | x2 = sampleCurveX(t2) - x; 31 | if (Math.abs(x2) < epsilon) return t2; 32 | d2 = sampleCurveDerivativeX(t2); 33 | if (Math.abs(d2) < 1e-6) break; 34 | t2 = t2 - x2 / d2; 35 | } 36 | 37 | // Fall back to the bisection method for reliability. 38 | t0 = 0.0; 39 | t1 = 1.0; 40 | t2 = x; 41 | 42 | if (t2 < t0) return t0; 43 | if (t2 > t1) return t1; 44 | 45 | while (t0 < t1) { 46 | x2 = sampleCurveX(t2); 47 | if (Math.abs(x2 - x) < epsilon) return t2; 48 | if (x > x2) t0 = t2; 49 | else t1 = t2; 50 | t2 = (t1 - t0) * 0.5 + t0; 51 | } 52 | 53 | // Failure. 54 | return t2; 55 | }; 56 | 57 | return function BezierEasing(x: number) { 58 | return sampleCurveY(solveCurveX(x, Number.EPSILON)); 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/createValue.ts: -------------------------------------------------------------------------------- 1 | import { Animated } from "react-native"; 2 | import { isAnimatedNode } from "./utils"; 3 | import { IAnimationNode } from "../../Types"; 4 | 5 | export const createValue = (v: number | IAnimationNode) => { 6 | if (isAnimatedNode(v)) { 7 | // @ts-ignore 8 | return new Animated.Value(v.__getValue()); 9 | } 10 | return new Animated.Value(v as number); 11 | }; 12 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/getColorDisplayValue.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "../../Types/IAnimationNode"; 2 | 3 | export const getColorDisplayValue = (input: IAnimationNode) => input; 4 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/getColorDisplayValue.web.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "../../Types/IAnimationNode"; 2 | import { ColorNode } from "./ColorNode"; 3 | import { Animated } from "react-native"; 4 | 5 | export const getColorDisplayValue = (input: IAnimationNode) => { 6 | return new ColorNode(input as Animated.Value); 7 | }; 8 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/getProcessedColor.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "../../Types"; 2 | import { isAnimatedNode } from "./utils"; 3 | import { Animated } from "react-native"; 4 | 5 | import { normalizeColor } from "./normalizeColor"; 6 | 7 | export const getProcessedColor = ( 8 | value: string | number | IAnimationNode, 9 | ): number => { 10 | if (isAnimatedNode(value)) { 11 | // @ts-ignore 12 | return normalizeColor((value as Animated.Value).__getValue()); 13 | } 14 | // @ts-ignore 15 | return normalizeColor(value as number); 16 | }; 17 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/getProcessedColor.web.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "../../Types"; 2 | import { isAnimatedNode } from "./utils"; 3 | import { Animated } from "react-native"; 4 | 5 | const normalizeColor = require("normalize-css-color"); 6 | 7 | export const getProcessedColor = ( 8 | value: string | number | IAnimationNode 9 | ): number => { 10 | if (isAnimatedNode(value)) { 11 | // @ts-ignore 12 | return normalizeColor((value as Animated.Value).__getValue()); 13 | } 14 | return normalizeColor(value); 15 | }; 16 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/getProcessedRotation.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "../../Types"; 2 | import { isAnimatedNode } from "./utils"; 3 | import { Animated } from "react-native"; 4 | 5 | export const getProcessedRotation = ( 6 | value: string | IAnimationNode 7 | ): number | IAnimationNode => { 8 | let resolvedValue: string; 9 | if (isAnimatedNode(value)) { 10 | // @ts-ignore 11 | resolvedValue = (value as Animated.Node).__getValue(); 12 | } else { 13 | resolvedValue = value as string; 14 | } 15 | 16 | if (/deg$/.test(resolvedValue)) { 17 | const degrees = parseFloat(resolvedValue) || 0; 18 | const radians = (degrees * Math.PI) / 180.0; 19 | return radians; 20 | } else { 21 | // Assume radians 22 | return resolvedValue; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/runTiming.ts: -------------------------------------------------------------------------------- 1 | import { Animated, Easing } from "react-native"; 2 | import { IAnimationValue } from "../../Types"; 3 | 4 | export const runTiming = (master: IAnimationValue, duration: number): void => { 5 | Animated.timing(master as Animated.Value, { 6 | toValue: duration, 7 | duration, 8 | easing: Easing.linear, 9 | isInteraction: false, 10 | }).start(() => { 11 | (master as Animated.Value).removeAllListeners(); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/Implementation/utils.ts: -------------------------------------------------------------------------------- 1 | export function isAnimatedNode(value: Object): boolean { 2 | // @ts-ignore 3 | return value && value.__getValue !== undefined; 4 | } 5 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/__tests__/assign.spec.ts: -------------------------------------------------------------------------------- 1 | import { ReactNativeAnimationProvider as P, AnimatedNode } from ".."; 2 | const { Animated } = P; 3 | 4 | describe("assign", () => { 5 | it("should assign numberic value", () => { 6 | const a = P.createValue(1); 7 | const statement = Animated.set(a, 10); 8 | const valueToTest = (statement as AnimatedNode).evaluate(); 9 | expect(valueToTest).toBe(10); 10 | }); 11 | 12 | it("should assign animated value", () => { 13 | const a = P.createValue(1); 14 | const b = P.createValue(2); 15 | const statement = Animated.set(a, b); 16 | const valueToTest = (statement as AnimatedNode).evaluate(); 17 | expect(valueToTest).toEqual(2); 18 | }); 19 | 20 | it("should assign expression", () => { 21 | const a = P.createValue(1); 22 | const b = P.createValue(2); 23 | const expression = Animated.add(a, b); 24 | const statement = Animated.set(a, expression); 25 | const valueToTest = (statement as AnimatedNode).evaluate(); 26 | expect(valueToTest).toEqual(3); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/__tests__/bezier.spec.ts: -------------------------------------------------------------------------------- 1 | import { AnimationProvider } from "../../.."; 2 | import { AnimatedNode } from ".."; 3 | const { Animated } = AnimationProvider; 4 | 5 | describe("bezier / linear", () => { 6 | it("should return 0", () => { 7 | const val = AnimationProvider.createValue(0); 8 | const retVal = Animated.bezier(0, 0, 1, 1)(val); 9 | expect((retVal as AnimatedNode).evaluate()).toBe(0); 10 | }); 11 | 12 | it("should return 1", () => { 13 | const val = AnimationProvider.createValue(1); 14 | const retVal = Animated.bezier(0, 0, 1, 1)(val); 15 | expect((retVal as AnimatedNode).evaluate()).toBe(1); 16 | }); 17 | 18 | it("should return 0.5", () => { 19 | const val = AnimationProvider.createValue(0.5); 20 | const retVal = Animated.bezier(0, 0, 1, 1)(val); 21 | expect((retVal as AnimatedNode).evaluate()).toBe(0.5); 22 | }); 23 | }); 24 | 25 | describe("bezier / curves", () => { 26 | it("should return 0.627", () => { 27 | const val = AnimationProvider.createValue(0.5); 28 | const retVal = Animated.bezier(0.17, 0.67, 0.83, 0.67)(val); 29 | expect((retVal as AnimatedNode).evaluate()).toBe(0.6275000000000001); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/__tests__/block.spec.ts: -------------------------------------------------------------------------------- 1 | import { ReactNativeAnimationProvider as P, AnimatedNode } from ".."; 2 | const { Animated } = P; 3 | 4 | describe("assign", () => { 5 | it("should execute all nodes in a block", () => { 6 | const a = P.createValue(1); 7 | const statement = Animated.block([ 8 | Animated.set(a, Animated.add(a, 10)), 9 | Animated.set(a, Animated.add(a, 10)), 10 | Animated.set(a, Animated.add(a, 10)), 11 | Animated.set(a, Animated.add(a, 10)), 12 | Animated.set(a, Animated.add(a, 10)) 13 | ]); 14 | const valueToTest = (statement as AnimatedNode).evaluate(); 15 | expect(valueToTest).toBe(51); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-animated/__tests__/proc.spec.ts: -------------------------------------------------------------------------------- 1 | import { ReactNativeAnimationProvider as P, AnimatedNode } from ".."; 2 | const { Animated } = P; 3 | 4 | describe("proc", () => { 5 | it("should call proc", () => { 6 | const addNumbers = Animated.proc("", (a, b) => Animated.add(a, b)); 7 | const valueToTest = addNumbers(10, 10); 8 | expect((valueToTest as AnimatedNode).evaluate()).toEqual(20); 9 | }); 10 | 11 | it("should call nested procs", () => { 12 | const addNumbers = Animated.proc("", (a, b) => Animated.add(a, b)); 13 | const valueToTest = addNumbers(10, addNumbers(10, 10)); 14 | expect((valueToTest as AnimatedNode).evaluate()).toEqual(30); 15 | }); 16 | 17 | it("should call composite procs", () => { 18 | const addNumbers = Animated.proc("", (a, b) => Animated.add(a, b)); 19 | const multiplyNumber = Animated.proc("", (a, b) => Animated.multiply(a, b)); 20 | const valueToTest = multiplyNumber(addNumbers(10, 10), addNumbers(10, 10)); 21 | expect((valueToTest as AnimatedNode).evaluate()).toEqual(400); 22 | }); 23 | 24 | it("should call with nested args", () => { 25 | const subcalc = Animated.proc("", (a, b) => Animated.add(a, b)); 26 | const calculate = Animated.proc("", (a, b) => 27 | Animated.multiply(subcalc(a, b), subcalc(a, b)), 28 | ); 29 | const valueToTest = calculate(10, 10); 30 | expect((valueToTest as AnimatedNode).evaluate()).toEqual(400); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-reanimated/Implementation/createValue.ts: -------------------------------------------------------------------------------- 1 | import Animated from "react-native-reanimated"; 2 | import { IAnimationNode, IAnimationValue } from "../../Types"; 3 | import { isAnimatedNode } from "./utils"; 4 | // @ts-ignore 5 | import { always } from "react-native-reanimated/src/base"; 6 | 7 | const { set, eq, cond, call } = Animated; 8 | 9 | export const createValue = (v: number | IAnimationNode): IAnimationValue => { 10 | if (isAnimatedNode(v)) { 11 | const retVal = new Animated.Value(0); 12 | const flag = new Animated.Value(0); 13 | 14 | const evaluateOnce = cond(eq(flag, 0), [ 15 | set(flag, 1), 16 | set(retVal, v as Animated.Node), 17 | call([], () => { 18 | // @ts-ignore 19 | alwaysNode.__detach(); 20 | }), 21 | ]); 22 | 23 | const alwaysNode = always(evaluateOnce); 24 | // @ts-ignore 25 | alwaysNode.__attach(); 26 | return retVal; 27 | } 28 | return new Animated.Value(v as number); 29 | }; 30 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-reanimated/Implementation/getColorFromString.ts: -------------------------------------------------------------------------------- 1 | import { processColor } from "react-native"; 2 | 3 | const getColorFromStringOrNumber = (color: string | number) => { 4 | // 0xaarrggbb 5 | const l = processColor(color); 6 | return { 7 | rgba: [ 8 | (l >> 16) & 0x0000ff, 9 | (l >> 8) & 0x00ff, 10 | l & 0xff, 11 | (l >> 24) & 0x000000ff 12 | ] 13 | }; 14 | }; 15 | 16 | export { getColorFromStringOrNumber }; 17 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-reanimated/Implementation/getDegreesInRadian.ts: -------------------------------------------------------------------------------- 1 | export const getDegreesInRadian = (rotation: string): number => { 2 | if (!rotation) return 0; 3 | if (rotation.endsWith("deg")) { 4 | return degToRad(parseFloat(rotation.substring(0, rotation.length - 3))); 5 | } else if (rotation.endsWith("rad")) { 6 | return parseFloat(rotation.substring(0, rotation.length - 3)); 7 | } 8 | return 0; 9 | }; 10 | 11 | const degToRad = (deg: number): number => (deg * Math.PI) / 180; 12 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-reanimated/Implementation/getProcessedColor.ts: -------------------------------------------------------------------------------- 1 | import { processColor } from "react-native"; 2 | import { IAnimationNode } from "../../Types"; 3 | import { isAnimatedNode } from "./utils"; 4 | 5 | export const getProcessedColor = ( 6 | value: IAnimationNode | string | number 7 | ): IAnimationNode | number => { 8 | const isAnimatedValue = isAnimatedNode(value); 9 | if (isAnimatedValue) return value; 10 | return processColor(value); 11 | }; 12 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-reanimated/Implementation/getProcessedRotation.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "../../Types"; 2 | import { isAnimatedNode } from "./utils"; 3 | 4 | export const getProcessedRotation = ( 5 | value: IAnimationNode | string 6 | ): IAnimationNode | number => { 7 | if (isAnimatedNode(value)) return value; 8 | if (/deg$/.test(value as string)) { 9 | const degrees = parseFloat(value as string) || 0; 10 | const radians = (degrees * Math.PI) / 180.0; 11 | return radians; 12 | } else { 13 | // Assume radians 14 | return parseFloat(value as string); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-reanimated/Implementation/runTiming.ts: -------------------------------------------------------------------------------- 1 | import Animated from "react-native-reanimated"; 2 | import { IAnimationValue } from "../../Types"; 3 | // @ts-ignore 4 | import { always } from "react-native-reanimated/src/base"; 5 | 6 | const { 7 | Clock, 8 | Value, 9 | sub, 10 | cond, 11 | greaterOrEq, 12 | call, 13 | stopClock, 14 | set, 15 | startClock, 16 | clockRunning, 17 | } = Animated; 18 | 19 | export const runTiming = (master: IAnimationValue, duration: number) => { 20 | const clock = new Clock(); 21 | let timing: Animated.Node; 22 | 23 | const startTime = new Value(0); 24 | const frameTime = sub(clock, startTime); 25 | const value = master as Animated.Value; 26 | 27 | timing = always( 28 | cond( 29 | clockRunning(clock), 30 | [ 31 | cond(greaterOrEq(frameTime, duration), [ 32 | // Stop animation (duration has been reached) 33 | stopClock(clock), 34 | set(value, duration), 35 | call([], () => 36 | // @ts-ignore 37 | timing.__detach(), 38 | ), 39 | ]), 40 | // Update animation value 41 | set(value, frameTime), 42 | ], 43 | [ 44 | // Start (clock is not running) 45 | set(startTime, clock), 46 | set(value, frameTime), 47 | startClock(clock), 48 | ], 49 | ), 50 | ); 51 | 52 | // @ts-ignore 53 | timing.__attach(); 54 | }; 55 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-reanimated/Implementation/utils.ts: -------------------------------------------------------------------------------- 1 | export function isAnimatedNode(value: Object): boolean { 2 | return value && value.hasOwnProperty("__nodeID"); 3 | } 4 | -------------------------------------------------------------------------------- /src/packages/animated/src/react-native-reanimated/index.web.ts: -------------------------------------------------------------------------------- 1 | export const dummy = 10; 2 | -------------------------------------------------------------------------------- /src/packages/animated/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "module": "commonjs", 11 | "outDir": "dist", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "declaration": true, 15 | "sourceMap": true, 16 | "jsx": "react" 17 | }, 18 | "include": ["src", "index.web.ts", "index.ts"], 19 | "exclude": ["node_modules", "**/*.spec.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /src/packages/gestures/index.ts: -------------------------------------------------------------------------------- 1 | import { GestureContainer } from "./src"; 2 | export { GestureContainer }; 3 | -------------------------------------------------------------------------------- /src/packages/gestures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-fluid-gestures", 3 | "version": "0.1.1", 4 | "description": "Gestures for react-native-fluid-transitions", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/fram-x/react-native-fluid.git" 8 | }, 9 | "main": "dist/index.js", 10 | "types": "dist/index.d.ts", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "author": "Fram X AS / Christian Falch", 15 | "license": "MIT", 16 | "peerDependencies": { 17 | "react-native-fluid-animations": "*", 18 | "react-native-fluid-transitions": "*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/packages/gestures/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "module": "commonjs", 11 | "outDir": "dist", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "declaration": true, 15 | "sourceMap": true, 16 | "jsx": "react", 17 | "baseUrl": ".", 18 | "paths": { 19 | "react-native-fluid-animations": ["../../packages/animated"], 20 | "react-native-fluid-transitions": ["../../packages/transitions"] 21 | } 22 | }, 23 | "include": ["src", "index.ts"], 24 | "exclude": ["node_modules", "**/*.spec.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /src/packages/navigation/index.ts: -------------------------------------------------------------------------------- 1 | export { createFluidStackNavigator, FluidNavigationContainer } from "./src"; 2 | export { NavigationState } from "./src/types"; 3 | export * from "./src/Transitions"; 4 | export { useNavigationDirection, useNavigationStates } from "./src/Hooks"; 5 | -------------------------------------------------------------------------------- /src/packages/navigation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-fluid-navigation", 3 | "version": "0.1.1", 4 | "description": "Navigation transitions for React Navigation and react-nativve-fluid-transitions", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/fram-x/react-native-fluid.git" 8 | }, 9 | "main": "dist/index.js", 10 | "types": "dist/index.d.ts", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "author": "Fram X AS", 15 | "license": "MIT", 16 | "peerDependencies": { 17 | "react-navigation-stack": "*", 18 | "react-native-reanimated": "*", 19 | "react-navigation-hooks": "*", 20 | "react-native-fluid-animations": "*", 21 | "react-native-fluid-transitions": "*" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Functions/getNavigationStates.ts: -------------------------------------------------------------------------------- 1 | import { NavigationState } from "../types"; 2 | 3 | export const getNavigationStates = ( 4 | index: number, 5 | navigationState: NavigationState, 6 | ) => { 7 | const states = [ 8 | { 9 | name: NavigationState.None, 10 | active: navigationState === NavigationState.None, 11 | }, 12 | { 13 | name: NavigationState.ForwardTo, 14 | active: navigationState === NavigationState.ForwardTo, 15 | }, 16 | { 17 | name: NavigationState.ForwardFrom, 18 | active: navigationState === NavigationState.ForwardFrom, 19 | }, 20 | { 21 | name: NavigationState.BackTo, 22 | active: navigationState === NavigationState.BackTo, 23 | }, 24 | { 25 | name: NavigationState.BackFrom, 26 | active: navigationState === NavigationState.BackFrom, 27 | }, 28 | { 29 | name: NavigationState.InTransition, 30 | active: navigationState !== NavigationState.None, 31 | }, 32 | { 33 | name: NavigationState.Forward, 34 | active: 35 | navigationState === NavigationState.ForwardFrom || 36 | navigationState === NavigationState.ForwardTo, 37 | }, 38 | { 39 | name: NavigationState.Index, 40 | active: true, 41 | value: index, 42 | }, 43 | ]; 44 | return states; 45 | }; 46 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Functions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./getNavigationStates"; 2 | export * from "./safeGetState"; 3 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Functions/safeGetState.ts: -------------------------------------------------------------------------------- 1 | import { ConfigStateType } from "react-native-fluid-transitions"; 2 | 3 | export const safeGetState = ( 4 | name: string, 5 | states: ConfigStateType[], 6 | ): ConfigStateType => { 7 | const state = states.find(s => s.name === name); 8 | if (!state) throw Error("State " + name + " not found."); 9 | return state; 10 | }; 11 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useDriverContext } from "./useDriverContext"; 2 | export { useCurrentValue } from "./useCurrentValue"; 3 | export { useNavigationState } from "./useNavigationState"; 4 | export { useVisibilityStyle } from "./useVisibilityStyle"; 5 | export { useNavigationDirection } from "./useNavigationDirection"; 6 | export { useNavigationStates } from "./useNavigationStates"; 7 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Hooks/useNavigationDirection.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { StateContext } from "react-native-fluid-transitions"; 3 | import { safeGetState } from "../Functions"; 4 | import { NavigationState } from "../types"; 5 | import { DirectionConfig } from "react-native-fluid-transitions"; 6 | 7 | export const useNavigationDirection = (): DirectionConfig => { 8 | const stateContext = useContext(StateContext); 9 | if (!stateContext) { 10 | throw Error("State context is missing"); 11 | } 12 | 13 | const forwardState = safeGetState( 14 | NavigationState.Forward, 15 | stateContext.states, 16 | ); 17 | 18 | return forwardState.active ? "forward" : "backward"; 19 | }; 20 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Hooks/useNavigationStates.ts: -------------------------------------------------------------------------------- 1 | import { safeGetState } from "../Functions"; 2 | import { NavigationState } from "../types"; 3 | import { useContext } from "react"; 4 | import { StateContext } from "react-native-fluid-transitions"; 5 | 6 | export const useNavigationStates = () => { 7 | const stateContext = useContext(StateContext); 8 | if (!stateContext) throw Error("State context is missing."); 9 | 10 | const forwardFrom = safeGetState( 11 | NavigationState.ForwardFrom, 12 | stateContext.states, 13 | ); 14 | const forwardTo = safeGetState( 15 | NavigationState.ForwardTo, 16 | stateContext.states, 17 | ); 18 | const backFrom = safeGetState(NavigationState.BackFrom, stateContext.states); 19 | const backTo = safeGetState(NavigationState.BackTo, stateContext.states); 20 | return { forwardFrom, forwardTo, backFrom, backTo }; 21 | }; 22 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Hooks/useVisibilityStyle.ts: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from "react"; 2 | import { StyleProp, ViewStyle } from "react-native"; 3 | import Animated from "react-native-reanimated"; 4 | 5 | export const useVisibilityStyle = ( 6 | index: number, 7 | _normalizedProgress: Animated.Node, 8 | ) => { 9 | // Set opacity to 0 for all screens except the first one 10 | const styleRef = useRef>({ 11 | opacity: index > 0 ? 0 : 1, 12 | }); 13 | 14 | useEffect(() => { 15 | styleRef.current = {}; 16 | }, []); 17 | 18 | return styleRef.current; 19 | }; 20 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Transitions/createNavigationTransition.ts: -------------------------------------------------------------------------------- 1 | import Fluid, { 2 | // @ts-ignore 3 | ConfigWhenType, 4 | // @ts-ignore 5 | ConfigStateType, 6 | } from "react-native-fluid-transitions"; 7 | import { NavigationState } from "../types"; 8 | import { safeGetState } from "../Functions"; 9 | 10 | export const createWhenConfiguration = ( 11 | states: ConfigStateType[], 12 | styleKey: string, 13 | inputRange: Array, 14 | outputRangeForwardFrom: Array, 15 | outputRangeForwardTo: Array, 16 | outputRangeBackFrom: Array, 17 | outputRangeBackTo: Array, 18 | ): ConfigWhenType[] => { 19 | const { forwardTo, forwardFrom, backTo, backFrom } = getNavigationStates( 20 | states, 21 | ); 22 | 23 | const interpolationForState = ( 24 | state: ConfigStateType, 25 | _isForward: boolean, 26 | outputRange: Array, 27 | ) => ({ 28 | state: state.name, 29 | animation: Fluid.Animations.Springs.Gentle, 30 | interpolation: [ 31 | { 32 | styleKey, 33 | inputRange, 34 | outputRange, 35 | }, 36 | ], 37 | }); 38 | 39 | return [ 40 | interpolationForState(forwardTo, true, outputRangeForwardTo), 41 | interpolationForState(forwardFrom, false, outputRangeForwardFrom), 42 | interpolationForState(backTo, true, outputRangeBackTo), 43 | interpolationForState(backFrom, false, outputRangeBackFrom), 44 | ]; 45 | }; 46 | 47 | const getNavigationStates = (states: ConfigStateType[]) => { 48 | const forwardTo = safeGetState(NavigationState.ForwardTo, states); 49 | const forwardFrom = safeGetState(NavigationState.ForwardFrom, states); 50 | const backTo = safeGetState(NavigationState.BackTo, states); 51 | const backFrom = safeGetState(NavigationState.BackFrom, states); 52 | return { 53 | forwardTo, 54 | forwardFrom, 55 | backTo, 56 | backFrom, 57 | }; 58 | }; 59 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Transitions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./createNavigationTransition"; 2 | export * from "./useNavigationTransition"; 3 | export * from "./useTranslateTransitions"; 4 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Transitions/useNavigationTransition.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { StateContext, useFluidConfig } from "react-native-fluid-transitions"; 3 | import { createWhenConfiguration } from "./createNavigationTransition"; 4 | import { ConfigType } from "react-native-fluid-transitions"; 5 | 6 | export const useNavigationTransition = ( 7 | styleKey: string, 8 | inputRange: Array, 9 | outputRangeForwardFrom: Array, 10 | outputRangeForwardTo: Array, 11 | outputRangeBackFrom: Array, 12 | outputRangeBackTo: Array, 13 | ): ConfigType => { 14 | const stateContext = useContext(StateContext); 15 | if (!stateContext) { 16 | throw Error("State context is missing"); 17 | } 18 | 19 | return useFluidConfig({ 20 | when: createWhenConfiguration( 21 | stateContext.states, 22 | styleKey, 23 | inputRange, 24 | outputRangeForwardFrom, 25 | outputRangeForwardTo, 26 | outputRangeBackFrom, 27 | outputRangeBackTo, 28 | ), 29 | }); 30 | }; 31 | 32 | export const useAllDirectionTransition = (styleKey: string, value: number) => { 33 | return useNavigationTransition( 34 | styleKey, 35 | [0, 1], 36 | [0, -value], // Forward from 37 | [value, 0], // Forward to 38 | [0, value], // Back from 39 | [-value, 0], // Back to 40 | ); 41 | }; 42 | 43 | export const useDirectionTransition = (styleKey: string, value: number) => { 44 | return useNavigationTransition( 45 | styleKey, 46 | [0, 1], 47 | [0, value], // Forward from 48 | [value, 0], // Forward to 49 | [0, value], // Back from 50 | [value, 0], // Back to 51 | ); 52 | }; 53 | -------------------------------------------------------------------------------- /src/packages/navigation/src/Transitions/useTranslateTransitions.ts: -------------------------------------------------------------------------------- 1 | import { 2 | useAllDirectionTransition, 3 | useDirectionTransition, 4 | } from "./useNavigationTransition"; 5 | 6 | export const useHorizontalTransition = (width: number) => { 7 | return useAllDirectionTransition("transform.translateX", width); 8 | }; 9 | 10 | export const useVerticalTransition = (height: number) => { 11 | return useAllDirectionTransition("transform.translateY", height); 12 | }; 13 | 14 | export const useTopTransition = (height: number) => { 15 | return useDirectionTransition("transform.translateY", -height); 16 | }; 17 | 18 | export const useBottomTransition = (height: number) => { 19 | return useDirectionTransition("transform.translateY", height); 20 | }; 21 | 22 | export const useLeftTransition = (width: number) => { 23 | return useDirectionTransition("transform.translateY", -width); 24 | }; 25 | 26 | export const useRightTransition = (width: number) => { 27 | return useDirectionTransition("transform.translateY", width); 28 | }; 29 | -------------------------------------------------------------------------------- /src/packages/navigation/src/hooks/useDriverContext.ts: -------------------------------------------------------------------------------- 1 | import Animated from "react-native-reanimated"; 2 | import { useMemo } from "react"; 3 | import { DriverContextType } from "react-native-fluid-transitions"; 4 | import { NavigationState } from "../types"; 5 | 6 | export const useDriverContext = ( 7 | _screenName: string, 8 | navigationState: NavigationState, 9 | durationValue: Animated.Value, 10 | current: Animated.Node, 11 | ): DriverContextType => { 12 | return useMemo( 13 | () => ({ 14 | isActive: () => navigationState !== NavigationState.None, 15 | driver: current, 16 | requestDuration: (duration: number) => { 17 | durationValue.setValue(duration); 18 | // console.log("---", screenName, "got duration", duration); 19 | }, 20 | }), 21 | [current, durationValue, navigationState], 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /src/packages/navigation/src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./navigationContainer"; 2 | export * from "./createFluidStackNavigator"; 3 | -------------------------------------------------------------------------------- /src/packages/navigation/src/navigationContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet } from "react-native"; 3 | import Fluid, { 4 | StateContext, 5 | DriverContext, 6 | } from "react-native-fluid-transitions"; 7 | import { useNavigationState, useDriverContext, useCurrentValue } from "./Hooks"; 8 | import { getNavigationStates } from "./Functions"; 9 | 10 | type Props = { 11 | name: string; 12 | }; 13 | 14 | export const FluidNavigationContainer: React.FC = ({ 15 | name, 16 | ...props 17 | }) => { 18 | const { navigationState, index } = useNavigationState(name); 19 | 20 | // Current 21 | const { current, duration } = useCurrentValue(navigationState); 22 | 23 | // Driver context 24 | const driverContextValue = useDriverContext( 25 | name, 26 | navigationState, 27 | duration, 28 | current, 29 | ); 30 | 31 | const states = getNavigationStates(index, navigationState); 32 | // console.log(name, "NavState", navigationState); 33 | 34 | // Render 35 | return ( 36 | 37 | 38 | 43 | 44 | 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /src/packages/navigation/src/types.ts: -------------------------------------------------------------------------------- 1 | export enum NavigationState { 2 | None = "None", 3 | ForwardTo = "ForwardTo", 4 | ForwardFrom = "ForwardFrom", 5 | BackTo = "BackTo", 6 | BackFrom = "BackFrom", 7 | Index = "Index", 8 | Current = "Current", 9 | Focused = "Focused", 10 | Forward = "Forward", 11 | InTransition = "InTransition", 12 | } 13 | 14 | export const NavigationTiming: number = 1250; 15 | -------------------------------------------------------------------------------- /src/packages/navigation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "module": "commonjs", 11 | "outDir": "dist", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "declaration": true, 15 | "sourceMap": true, 16 | "jsx": "react", 17 | "baseUrl": ".", 18 | "paths": { 19 | "react-native-fluid-animations": ["../../packages/animated"], 20 | "react-native-fluid-transitions": ["../../packages/transitions"] 21 | } 22 | }, 23 | "include": ["src", "index.ts"], 24 | "exclude": ["node_modules", "**/*.spec.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /src/packages/svg/index.ts: -------------------------------------------------------------------------------- 1 | import Fluid from "react-native-fluid-transitions"; 2 | import Svg, { Defs, Stop } from "react-native-svg"; 3 | import { 4 | FluidEllipse, 5 | FluidCircle, 6 | FluidRect, 7 | FluidLine, 8 | FluidLinearGradient 9 | } from "./src/Components"; 10 | 11 | const SvgViews = { 12 | Ellipse: FluidEllipse, 13 | Circle: FluidCircle, 14 | Rect: FluidRect, 15 | Line: FluidLine, 16 | LinearGradient: FluidLinearGradient, 17 | Defs, 18 | Stop, 19 | Svg 20 | }; 21 | 22 | const SvgFluid = { 23 | ...Fluid, 24 | Svg: SvgViews 25 | }; 26 | 27 | export default SvgFluid; 28 | -------------------------------------------------------------------------------- /src/packages/svg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-fluid-svg", 3 | "version": "0.1.1", 4 | "description": "", 5 | "main": "index.ts", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Fram X AS / Christian Falch", 10 | "license": "MIT", 11 | "peerDependencies": { 12 | "react-native-fluid-animations": "*", 13 | "react-native-fluid-transitions": "*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/packages/svg/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "module": "commonjs", 11 | "outDir": "dist", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "declaration": true, 15 | "sourceMap": true, 16 | "jsx": "react", 17 | "baseUrl": ".", 18 | "paths": { 19 | "react-native-fluid-animations": ["../../packages/animated"], 20 | "react-native-fluid-transitions": ["../../packages/transitions"] 21 | } 22 | }, 23 | "include": ["src", "index.ts"], 24 | "exclude": ["node_modules", "**/*.spec.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /src/packages/transitions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-fluid-transitions", 3 | "version": "0.1.1", 4 | "description": "Fluid transition component for React Native and React Native Web", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/fram-x/react-native-fluid.git" 8 | }, 9 | "main": "dist/index.js", 10 | "types": "dist/index.d.ts", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "author": "Fram X AS / Christian Falch", 15 | "license": "MIT", 16 | "peerDependencies": { 17 | "react-native-fluid-animations": "*" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Builder/dumpTree.ts: -------------------------------------------------------------------------------- 1 | import { AnimationNode } from "./types"; 2 | 3 | export const dumpTree = ( 4 | node: AnimationNode, 5 | output: (message?: any, ...optionalParams: any[]) => void, 6 | indent: number = 0, 7 | ) => { 8 | const indentStr = new Array(indent).map(_ => "").join(" "); 9 | // @ts-ignore 10 | if (node.childDirection === "-") { 11 | output( 12 | indentStr, 13 | indentStr, 14 | getNodeName(node), 15 | "subduration:", 16 | // @ts-ignore 17 | node.subtreeDuration.toFixed(2), 18 | ); 19 | } else { 20 | output( 21 | indentStr, 22 | getNodeName(node), 23 | "dur:", 24 | node.duration.toFixed(2), 25 | "anim:", 26 | node.animation ? node.animation.type : "unknown", 27 | "subdur:", 28 | // @ts-ignore 29 | node.subtreeDuration.toFixed(2), 30 | "delay:", 31 | node.delay, 32 | "offset:", 33 | node.offset, 34 | "stgr:", 35 | node.stagger, 36 | node.childAnimation, 37 | node.childDirection, 38 | ); 39 | } 40 | node.children.forEach(c => dumpTree(c, output, indent + 2)); 41 | }; 42 | 43 | const getNodeName = (node: AnimationNode) => { 44 | return "[" + node.label || "id: " + node.id + "]: "; 45 | }; 46 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Builder/getAnimationOnEnd.ts: -------------------------------------------------------------------------------- 1 | import { OnAnimationFunction } from "../../Components/Types/InterpolationTypes"; 2 | 3 | export const getAnimationOnEnd = ( 4 | length: number, 5 | onEnd: OnAnimationFunction | undefined 6 | ) => { 7 | if (onEnd === undefined) return undefined; 8 | let animationCount = length; 9 | return () => { 10 | animationCount--; 11 | if (animationCount === 0) { 12 | onEnd && onEnd(); 13 | } 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Builder/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./getInterpolationTree"; 2 | export * from "./dumpTree"; 3 | export * from "./types"; 4 | export * from "./getAnimationOnEnd"; 5 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Builder/types.ts: -------------------------------------------------------------------------------- 1 | import { Metrics } from "../../Types"; 2 | import { 3 | ChildAnimationDirection, 4 | ConfigAnimationType, 5 | ConfigStaggerFunction, 6 | } from "../../Configuration"; 7 | 8 | export type AnimationNode = { 9 | id: number; 10 | interpolationId: number; 11 | childAnimation: "staggered" | "parallel" | "sequential"; 12 | childDirection: ChildAnimationDirection; 13 | children: Array; 14 | metrics: Metrics; 15 | parent?: AnimationNode; 16 | offset: number; 17 | duration: number; 18 | subtreeDuration?: number; 19 | delay: number; 20 | stagger: number; 21 | staggerFunction?: ConfigStaggerFunction; 22 | waitForMetrics?: () => Promise; 23 | animation?: ConfigAnimationType; 24 | label?: string; 25 | isHidden: boolean; 26 | }; 27 | 28 | export type Animations = { [key: string]: boolean }; 29 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Functions/createProc.ts: -------------------------------------------------------------------------------- 1 | import { IAnimationNode } from "react-native-fluid-animations"; 2 | 3 | let _functionCache: { 4 | [key: string]: (...args: IAnimationNode[]) => IAnimationNode; 5 | } = {}; 6 | 7 | export const createProc = ( 8 | key: string, 9 | cb: () => (...args: IAnimationNode[]) => IAnimationNode, 10 | ): ((...args: IAnimationNode[]) => IAnimationNode) => { 11 | if (!_functionCache[key]) { 12 | _functionCache[key] = cb(); 13 | Object.defineProperty(_functionCache[key], "name", { value: key }); 14 | } 15 | return _functionCache[key]; 16 | }; 17 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Functions/getResolvedAnimation.ts: -------------------------------------------------------------------------------- 1 | import { createSpring } from "./spring"; 2 | import { 3 | ConfigTimingAnimationType, 4 | ConfigAnimationType 5 | } from "../../Configuration"; 6 | 7 | export const getResolvedAnimation = ( 8 | animation: ConfigAnimationType 9 | ): ConfigTimingAnimationType => { 10 | if (animation.type === "spring") { 11 | // We need to change to a timing animation with easing 12 | const springInfo = createSpring( 13 | 0, 14 | 1, 15 | animation.mass, 16 | animation.stiffness, 17 | animation.damping 18 | ); 19 | return { 20 | ...animation, 21 | type: "timing", 22 | duration: springInfo.duration, 23 | easing: springInfo.easing 24 | }; 25 | } 26 | return animation; 27 | }; 28 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Functions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./easing"; 2 | export * from "./spring"; 3 | export * from "./createProc"; 4 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/Functions/__tests__/interpolateColor.spec.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import { AnimationNode } from "react-native-fluid-animations"; 3 | import { interpolateColor } from "../interpolateColor"; 4 | 5 | describe("interpolateColor", () => { 6 | it("should return 0xff000000", () => { 7 | const valueToTest = interpolateColor(1, 0, 1, 0x00000000, 0xff000000); 8 | expect((valueToTest as AnimationNode).evaluate()).toBe(0xff000000); 9 | }); 10 | it("should return 0x00ff0000", () => { 11 | const valueToTest = interpolateColor(1, 0, 1, 0xff000000, 0x00ff0000); 12 | expect((valueToTest as AnimationNode).evaluate()).toBe(0x00ff0000); 13 | }); 14 | it("should return 0x00aa0000", () => { 15 | const valueToTest = interpolateColor(1, 0, 1, 0xff000000, 0x00aa0000); 16 | expect((valueToTest as AnimationNode).evaluate()).toBe(0x00aa0000); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/Functions/getExtrapolationValue.ts: -------------------------------------------------------------------------------- 1 | import { Extrapolate } from "./interpolate"; 2 | import { ExtrapolateType } from "react-native-fluid-animations"; 3 | 4 | export const getExtrapolationValue = ( 5 | extrapolate: ExtrapolateType 6 | ): Extrapolate => { 7 | switch (extrapolate) { 8 | case "clamp": 9 | return Extrapolate.Clamp; 10 | case "extend": 11 | return Extrapolate.Extend; 12 | case "identity": 13 | return Extrapolate.Identity; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/Functions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./interpolate"; 2 | export * from "./lifecycle"; 3 | export * from "./createAnimationNode"; 4 | export * from "./normalize"; 5 | export * from "./setAnimationValue"; 6 | export * from "./interpolateColor"; 7 | export * from "./interpolateValue"; 8 | export * from "./getExtrapolationValue"; 9 | export * from "./createInterpolationNode"; 10 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/Functions/interpolateValue.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IAnimationNode, 3 | AnimationProvider 4 | } from "react-native-fluid-animations"; 5 | import { createProc } from "../../Functions/createProc"; 6 | 7 | const { proc, add, multiply, divide, sub } = AnimationProvider.Animated; 8 | 9 | const interpolateValueProc = createProc("interpolateValue", () => 10 | proc( 11 | "interpolateValue", 12 | (inputValue, inputMin, inputMax, outputMin, outputMax) => 13 | add( 14 | outputMin, 15 | multiply( 16 | divide(sub(inputValue, inputMin), sub(inputMax, inputMin)), 17 | sub(outputMax, outputMin) 18 | ) 19 | ) 20 | ) 21 | ); 22 | 23 | export const interpolateValue = ( 24 | input: IAnimationNode, 25 | inputMin: any, 26 | inputMax: any, 27 | outputMin: any, 28 | outputMax: any 29 | ) => { 30 | return interpolateValueProc(input, inputMin, inputMax, outputMin, outputMax); 31 | }; 32 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/Functions/normalize.ts: -------------------------------------------------------------------------------- 1 | import { createProc } from "../../Functions/createProc"; 2 | import { AnimationProvider } from "react-native-fluid-animations"; 3 | 4 | const { proc, greaterThan, divide, sub, cond } = AnimationProvider.Animated; 5 | 6 | export const normalize = createProc("normalize", () => 7 | proc("normalize", (source, offset, duration) => 8 | cond( 9 | greaterThan(divide(sub(source, offset), duration), 1.0), 10 | // Greater than 1.0 - return 1.0 11 | 1.0, 12 | // Return value 13 | divide(sub(source, offset), duration) 14 | ) 15 | ) 16 | ); 17 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/Functions/setAnimationValue.ts: -------------------------------------------------------------------------------- 1 | import { getInterpolatorFunction } from "./interpolate"; 2 | import { normalize } from "./normalize"; 3 | import { 4 | InterpolateFunction, 5 | AnimationProvider, 6 | IAnimationValue, 7 | } from "react-native-fluid-animations"; 8 | import { createProc } from "../../Functions/createProc"; 9 | import { EasingFunction } from "../../../Components/Types"; 10 | const { cond, set, eq, proc } = AnimationProvider.Animated; 11 | 12 | export const getSetAnimationValue = ( 13 | interpolateInternal: InterpolateFunction, 14 | key: string, 15 | easingFunction: EasingFunction, 16 | easingKey: string, 17 | ) => { 18 | const interpolate = getInterpolatorFunction( 19 | interpolateInternal, 20 | key, 21 | easingFunction, 22 | easingKey, 23 | ); 24 | 25 | return createProc(`setAnimationValue_${easingKey}-${key}`, () => 26 | proc( 27 | `setAnimationValue_${easingKey}-${key}`, 28 | ( 29 | source, 30 | offset, 31 | duration, 32 | target, 33 | inputMin, 34 | inputMax, 35 | outputMin, 36 | outputMax, 37 | extrapolateLeft, 38 | extrapolateRight, 39 | outputStart, 40 | ) => 41 | // Copy start output range - this is needed since it might be 42 | // the tracker itself 43 | cond( 44 | eq(outputStart, Number.MIN_VALUE), 45 | // if outputStart is Minvalue then 46 | set(outputStart as IAnimationValue, outputMin), 47 | // else Interpolate 48 | interpolate( 49 | normalize(source, offset, duration), 50 | inputMin, 51 | inputMax, 52 | outputStart, 53 | outputMax, 54 | extrapolateLeft, 55 | extrapolateRight, 56 | target, 57 | ), 58 | ), 59 | ), 60 | ); 61 | }; 62 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/Functions/setInterpolationValue.ts: -------------------------------------------------------------------------------- 1 | import { getInterpolatorFunction } from "./interpolate"; 2 | import { 3 | InterpolateFunction, 4 | AnimationProvider, 5 | IAnimationValue, 6 | } from "react-native-fluid-animations"; 7 | import { createProc } from "../../Functions/createProc"; 8 | const { proc, block, cond, eq, set } = AnimationProvider.Animated; 9 | 10 | export const getSetInterpolationValue = ( 11 | interpolateInternal: InterpolateFunction, 12 | key: string, 13 | ) => { 14 | const interpolate = getInterpolatorFunction(interpolateInternal, key); 15 | 16 | return createProc(`setInterpolationValue_${key}`, () => 17 | proc( 18 | `setInterpolationValue_${key}`, 19 | ( 20 | source, 21 | target, 22 | inputMin, 23 | inputMax, 24 | outputMin, 25 | outputMax, 26 | extrapolateLeft, 27 | extrapolateRight, 28 | isStarted, 29 | removePreviousStatement, 30 | ) => 31 | block([ 32 | cond(eq(isStarted, 0), [ 33 | set(isStarted as IAnimationValue, 1), 34 | removePreviousStatement, 35 | ]), 36 | interpolate( 37 | source, 38 | inputMin, 39 | inputMax, 40 | outputMin, 41 | outputMax, 42 | extrapolateLeft, 43 | extrapolateRight, 44 | target, 45 | ), 46 | ]), 47 | ), 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/addAnimations.ts: -------------------------------------------------------------------------------- 1 | import { createAnimationNode } from "./Functions"; 2 | import { AnimationInfo } from "../../Components/Types/AnimationInfo"; 3 | import { Easings, DriverContextType } from "../../Components/Types"; 4 | import { IAnimationNode } from "react-native-fluid-animations"; 5 | 6 | export const addAnimations = ( 7 | source: IAnimationNode, 8 | driverContext: DriverContextType | undefined, 9 | animations: AnimationInfo[], 10 | ) => { 11 | // Skip tracking? 12 | if (animations.length === 0) return; 13 | 14 | // Populate with all interpolations from the tracker list 15 | animations.forEach(animation => { 16 | const { 17 | animationId, 18 | key, 19 | ownerId, 20 | inputRange, 21 | easing, 22 | easingKey, 23 | extrapolate, 24 | extrapolateLeft, 25 | extrapolateRight, 26 | outputRange, 27 | duration, 28 | target, 29 | offset, 30 | onBegin, 31 | onEnd, 32 | interpolate, 33 | } = animation; 34 | // Get easing 35 | const easingFunction = easing || Easings.linear; 36 | // Create node 37 | createAnimationNode( 38 | source, 39 | target, 40 | animationId, 41 | key, 42 | ownerId, 43 | offset, 44 | duration, 45 | easingFunction, 46 | easingKey || "linear", 47 | inputRange, 48 | outputRange, 49 | extrapolate, 50 | extrapolateLeft, 51 | extrapolateRight, 52 | onBegin, 53 | onEnd, 54 | interpolate, 55 | driverContext ? driverContext.isActive : () => false, 56 | ); 57 | }); 58 | }; 59 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/Runner/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./addAnimations"; 2 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./commitAnimations"; 2 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Animation/types.ts: -------------------------------------------------------------------------------- 1 | import { InterpolationInfo } from "../Components/Types"; 2 | import { IAnimationNode } from "react-native-fluid-animations"; 3 | 4 | export type Interpolations = Array<{ 5 | interpolator: IAnimationNode; 6 | interpolationInfo: InterpolationInfo; 7 | }>; 8 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Components/FluidTransitions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./useConfiguration"; 2 | export * from "./useTransitionItems"; 3 | export * from "./useInterpolatorContext"; 4 | export * from "./useAnimationContext"; 5 | export * from "./useTouchable"; 6 | export * from "./useSharedInterpolation"; 7 | export * from "./useMountUpdate"; 8 | export * from "./useLayout"; 9 | export * from "./useValueInterpolation"; 10 | export * from "./useStyleContext"; 11 | export * from "./useStateChanges"; 12 | export * from "./useOnConfig"; 13 | export * from "./useWhenConfig"; 14 | export * from "./useInitialStyle"; 15 | export * from "./useInterpolatorConfig"; 16 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Components/FluidTransitions/useInterpolatorConfig.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TransitionItem, 3 | ValueContextType, 4 | InterpolatorContextType, 5 | } from "../Types"; 6 | import { fluidException } from "../../Types"; 7 | import { SafeStateConfigType } from "../../Configuration"; 8 | 9 | export const useInterpolatorConfig = ( 10 | _transitionItem: TransitionItem, 11 | styleContext: ValueContextType, 12 | _propContext: ValueContextType, 13 | interpolatorContext: InterpolatorContextType, 14 | configuration: SafeStateConfigType, 15 | isMounted: boolean, 16 | ) => { 17 | const interpolations = configuration.interpolation; 18 | 19 | interpolations.forEach(interpolation => { 20 | if (!interpolation.value) { 21 | throw fluidException( 22 | "A configuration interpolation needs an interpolator.", 23 | ); 24 | } 25 | const { ownerLabel, valueName } = interpolation.value; 26 | const interpolator = interpolatorContext.getInterpolator( 27 | ownerLabel, 28 | valueName, 29 | ); 30 | if (!interpolator) { 31 | if (isMounted) { 32 | // TODO: Better explanation? 33 | throw fluidException( 34 | "Could not find interpolator " + 35 | valueName + 36 | " in item with label " + 37 | ownerLabel + 38 | ".", 39 | ); 40 | } else { 41 | return; 42 | } 43 | } 44 | 45 | const { 46 | styleKey, 47 | inputRange, 48 | outputRange, 49 | extrapolate, 50 | extrapolateLeft, 51 | extrapolateRight, 52 | } = interpolation; 53 | 54 | // Set up styles with interpolations 55 | styleContext.addInterpolation( 56 | interpolator, 57 | styleKey, 58 | inputRange, 59 | outputRange, 60 | extrapolate, 61 | extrapolateLeft, 62 | extrapolateRight, 63 | ); 64 | }); 65 | }; 66 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Components/FluidTransitions/usePropContext.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { TransitionItem, AnimationContextType, Values } from "../Types"; 3 | import { ValueDescriptorsType } from "../../Types"; 4 | import { useValueContext } from "./useValueContext"; 5 | import { getAnimatedProps } from "../../Props"; 6 | import { IAnimationNode } from "react-native-fluid-animations"; 7 | 8 | export const usePropContext = ( 9 | transitionItem: TransitionItem, 10 | animationContext: AnimationContextType, 11 | props: T, 12 | valueDescriptors: ValueDescriptorsType 13 | ) => { 14 | // Contains previous prop values for descriptors 15 | const previousPropsRef = useRef(); 16 | 17 | // Current animated props 18 | const animatedPropsRef = useRef<{ [key: string]: IAnimationNode }>(); 19 | 20 | if (previousPropsRef.current !== props) { 21 | // Update 22 | previousPropsRef.current = props; 23 | } 24 | 25 | const nextKeys = Object.keys(props); 26 | const nextValues: Values = {}; 27 | nextKeys.forEach(key => (nextValues[key] = props[key])); 28 | 29 | // Create value context 30 | const valueContext = useValueContext( 31 | transitionItem, 32 | animationContext, 33 | valueDescriptors, 34 | nextKeys, 35 | nextValues 36 | ); 37 | 38 | if (valueContext.isChanged) { 39 | // Reset props 40 | animatedPropsRef.current = undefined; 41 | } 42 | 43 | const getAnimatedPropsInternal = () => { 44 | if (!animatedPropsRef.current) { 45 | animatedPropsRef.current = getAnimatedProps(valueContext.current()); 46 | } 47 | return animatedPropsRef.current; 48 | }; 49 | 50 | return { 51 | getAnimatedProps: getAnimatedPropsInternal, 52 | propContext: valueContext 53 | }; 54 | }; 55 | -------------------------------------------------------------------------------- /src/packages/transitions/src/Components/FluidTransitions/useStyleContext.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { StyleSheet } from "react-native"; 3 | import { 4 | Style, 5 | TransitionItem, 6 | AnimationContextType, 7 | AnimatedStyleKeys 8 | } from "../Types"; 9 | import { getCalulatedStyles } from "../../Styles/getCalculatedStyle"; 10 | import { useValueContext } from "./useValueContext"; 11 | import { getStyleInfo } from "../../Styles/getStyleInfo"; 12 | 13 | /** 14 | * 15 | * @description Builds a hash table of styles that is when interpolating 16 | */ 17 | export const useStyleContext = ( 18 | transitionItem: TransitionItem, 19 | animationContext: AnimationContextType, 20 | currentStyle: Style[] | Style | number | undefined 21 | ) => { 22 | // Contains the last calculated style 23 | const calculatedStyleRef = useRef