1.0.9 • Published 2 years ago

@marcuzgabriel/reanimated-animation-library v1.0.9

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

TypeScriptReactJavascript

Reanimated-animation-library with react-native-reanimated, react-native-gesture-handler and react-native-redash

This library provide multiple components but most importantly the BottomSheet. The BottomSheet is highly customizable. Visit the homepage for example: https://marcuzgabriel.github.io/reanimated-bottom-sheet/

BottomSheet

Alt Text Alt Text Alt Text

BottomSheet

PropTypeDescriptionis required
backgroundColorstringSets the background colorfalse
borderTopLeftRadiusnumberSets the border top left radiusfalse
borderTopRightRadiusnumberSets the border top right radiusfalse
closeBottomSheetRequestobject: { isEnabled: boolean; callback: ((cb) => void) => void }Custom trigger function to make the bottom sheet go to bottom onPressfalse
contentComponentnodeContent componenttrue
contentHeightWhenKeyboardIsVisibleobject: { takeUpAllAvailableSpace?: boolean; resizeHeightTrigger?: number; resizeHeight?: number; offset?: number, closeIcon?: { topOffset?: number, rightOffset?: number, icon: () => React.ReactNode }}manipulate the content height when keyboard is visible and add a close iconfalse
extraOffsetnumberIf you need some extra offset when it comes to the panning event hitting the footerfalse
extraSnapPointBottomOffsetnumberMinor differences occours depending on the Platform. This prop helps to get the perfect snap point on all platformsfalse
fadingScrollEdgesobject: { isEnabled: boolean, androidFadingEdgeLength: number, iOSAndWebFadingEdgeHeight: number, nativeBackgroundColor: string, webBackgroundColorTop: { from: string to: string}, webBackgroundColorBottom: { from: string, to: string }This prop ensures that there is a scrolling edge when the content is scrollablefalse
footerComponentnodeFooter componentfalse
getCurrentConfigRequestfunction with callbackThis function will provide the current configurationfalse
headerobject: { height: number }If there is no header component then this object can be used to style the headerfalse
headerComponentnodeHeader componentfalse
hideContentOnCardCollapseobject: { isEnabled: boolean, offset: number }Hides content when gesturing downfalse
hideFooterOnCardCollapseobject: { isEnabled: boolean, offset: number }Hides footer when gesturing downfalse
initializeBottomSheetAsClosedbooleanIn some cases it might be relevant to show the background content before showing the bottomSheetfalse
isBottomSheetInactivebooleanSet the bottom to an inactive state. Can be used for async handling og UX requirementsfalse
keyboardAvoidBottomMargin (currently disabled)numberAn extra margin wrapper is implemented instead. The prop was used to create extra spacings when an input field is focusedfalse
maxHeightRatiofloatmax height of the bottom sheet in 0.1 - 0.9 ratiofalse
morphingArrowobject: { isEnabled: boolean, offset: number, fill: string }As there currently is a bug on web when interpolating SVG's with reanimated, then the morphing arrow can be disabled for specific platforms using this propfalse
offsetAdditionnumberUsed to have extra offset for the snap effectfalse
onLayoutRequestfunction with callbackIn some cases the card height of the BottomSheet might come in handy. This prop returns the heightfalse
openBottomSheetRequestobject: { isEnabled: boolean; callback: ((cb) => void) => void }Custom trigger functions to make the bottom sheet go to topfalse
outerScrollEventobject: { isEnabled?: boolean, scrollY?: Animated.SharedValue, autoScrollTriggerLength?: number }Connect an outer scrolling event that the bottom sheet should react tofalse
pressableSafeAreaToContentnumberStronger handling for controlling safe area to content so a press event on the BottomSheet do not interfer with the content within the BottomSheetfalse
scrollArrowBottomComponent (currently disabled)nodeScroll arrow bottom componentfalse
scrollArrowsobject: { isEnabled: boolean, fill: string, dimensions: number, topArrowOffset: number, bottomArrowOffset: number }When there is no scrollArrowBottom- or top component then this object can be used for styling the scroll arrows.false
scrollArrowTopComponent (currently disabled)nodeScroll arrow top componentfalse
smoothAppearanceobject: { waitForContent: boolean, emptyContentHeight?: number }Ensures a smooth appearance animation of the BottomSheet to eliminate flickeringfalse
snapEffectDirectionAnimated.SharedValueUsed together with SnapEffect component. It tells the BottomSheet how to react to the effect. Please look in examples for more informationfalse
snapPointBottomnumberThis prop is required for the BottomSheet to worktrue
springConfigobject { damping?: number; mass?: number; stiffness?: number; overshootClamping?: boolean; restSpeedThreshold?: number; restDisplacementThreshold?: number; }Overrule default spring config with custom configuration
testIDstringadd testID to the bottomSheetfalse
webBoxShadowobject: { offset: number; opacity: number }Set a box shadow for webfalse

Current progress

  • ScrollViewKeyboardAvoid. Personally I have had troubles using the KeyboardAvoidView from react-native where I am limited to only use one behaviour. This approach uses two behaviours at the same time with reanimated. First it manipulates the translationY position so the content container floats above the keyboard. Secondly it changes the height of the content container so a nice scroll-to-focused-input gets triggered. A minimum requirement for this approach to work is to use this library's <InputField />. Multiple examples can be found in the project Example folder.
  • InputField. This is a component that is connected to the above ScrollViewKeyboardAvoid. When focused and the minimum requirements for ScrollViewKeyboardAvoid is met, then a smooth scroll-to-focused-input field event will trigger.
  • BottomSheet
    • Static event: When background content is not scrollable then the background content should not be snappable
    • Scroll arrows that appear / dissapear
    • Fading scroll edges for alle platforms
    • Drag resistance when using the snap effect
    • InputField component that accepts a unique id so no matter where the component is located then a nice scrollTo animation effect to the input field is achieved
    • If the background content is not scrollable but there is content hiding behind the card, then make the component snappable so the card will collapse if the user tries to do a scroll gesture on the background content
    • Morphing arrow that follows the Y axis animation of the card
    • Card is collapsable by either clicking, gesturing, overlapping from scroll to pan gesture or scrolling the background content
    • The card should be able to handle input fields. When an input field is pressed, then the keyboard should press the card upwards and a scrolling animation should scroll to the input field
    • Add a ScrollView component in a PanGestureHandler component
    • iOS + Android: Overlap from a scrolling gesture to a pan gesture by creating a scroll-to-top snapping effect
    • Basic animation features (scrolling and pan gesture event)
    • Header component
    • Content component
    • Footer component
  • Appear
  • Slider
  • Morphing SVG Graph
  • Unit tests

    Examples

    Different examples can be found at the location src/components/Examples

React integration

import React from 'react';
import { Platform, useWindowDimensions } from 'react-native';
import styled from 'styled-components/native';
import Animated, {
  useSharedValue,
  useAnimatedScrollHandler,
  useAnimatedRef,
} from 'react-native-reanimated';
import { BottomSheet, SnapEffect } from '@marcuzgabriel/reanimated-animation-library';

const HEADER_HEIGHT = 50;
const EXTRA_SNAP_POINT_OFFSET = 30;

const isAndroid = Platform.OS === 'android';

const fakeScrollItem = [
  {
    text: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
  ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
  laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
  voluptate velit esse cillum dolore eu fugiat nulla pariatur.
`,
  },
];

const Wrapper = styled.View<{ windowHeight: number }>`
  position: relative;
  height: ${({ windowHeight }): number => windowHeight}px;
  width: 100%;
`;

const Content = styled.View`
  width: 100%;
  height: 400;
  background-color: purple;
`;

const Header = styled.View`
  width: 100%;
  height: 100px;
  background: black;
  justify
`;

const Text = styled.Text``;

const FakeContentWrapper = styled.View<{ windowHeight: number }>`
  background: white;
  height: ${({ windowHeight }): number => windowHeight}px;
  width: 100%;
  padding: 32px 16px;
`;

const ScrollViewWithSnapEffect: React.FC = () => {
  const scrollViewRef = useAnimatedRef<Animated.ScrollView>();
  const scrollY = useSharedValue(0);
  const cardHeight = useSharedValue(0);
  const snapEffectDirection = useSharedValue('');

  const windowHeight = useWindowDimensions().height;

  const onScrollHandler = useAnimatedScrollHandler({
    onScroll: e => {
      scrollY.value = e.contentOffset.y;
    },
  });

  return (
    <Wrapper windowHeight={windowHeight}>
      <Animated.ScrollView
        ref={scrollViewRef}
        bounces={false}
        alwaysBounceVertical={false}
        onScroll={onScrollHandler}
        scrollEventThrottle={16}
      >
        <SnapEffect cardHeight={cardHeight} snapEffectDirection={snapEffectDirection}>
          {fakeScrollItem.map(({ text }, i) => (
            <FakeContentWrapper windowHeight={windowHeight} key={`${i}_${text}`}>
              <Text>{text}</Text>
            </FakeContentWrapper>
          ))}
        </SnapEffect>
      </Animated.ScrollView>
      <BottomSheet
        scrollY={scrollY}
        fadingScrollEdges={{ isEnabled: false }}
        morphingArrow={{ isEnabled: Platform.OS !=='web', offset: 20 }}
        keyboardAvoidBottomMargin={isAndroid ? 16 : 0}
        snapEffectDirection={snapEffectDirection}
        snapPointBottom={HEADER_HEIGHT + EXTRA_SNAP_POINT_OFFSET}
        onLayoutRequest={(height: number): void => {
          cardHeight.value = height;
        }}
        contentComponent={<Content />}
      />
    </Wrapper>
  );
};

export default ScrollViewWithSnapEffect;

Expo integration

npm install @marcuzgabriel/reanimated-animation-library@1.0.0 https://github.com/marcuzgabriel/reanimated-animation-library/packages/813007

Update app.json accordingly and remember to pod install and build the projects properly.

{
  "name": "MyTSProject",
  "displayName": "MyTSProject",
  "expo": {
    "name": "MyTSProject",
    "slug": "MyTSProject",
    "version": "1.0.0",
    "assetBundlePatterns": [
      "**/*"
    ],
    "web": {
      "build": {
        "babel": {
          "include": [
            "@marcuzgabriel/reanimated-animation-library"
          ]
        }
      }
    }
  }
}

Performance observations

The only time a performance decrease occours is when the native keyboad appears. This type of performance decrease will always happend with or without reanimated. If you experience any other performance decrease, please let me know :)

Observations

Latest react-native-gesture-handler version vs old and latest react-native-reanimated vs old

PackagePlatformObservations / bugs
#react-native-reanimatedwebThe package has a bug on web when it comes to interpolating SVG's. https://github.com/software-mansion/react-native-reanimated/issues/1951
#react-native-gesture-handlerallThere are quite some limitation from previously. Before react-native-gesture-handler handled the touches automatically with no further control to it. Now all pan gestures needs to be controlled with waitFor and simoustanously.
#react-native-gesture-handlerweb & Androidreact-native-gesture-handler and the props waitFor and simultaneously don't work properly for either web or Android. The behaviourial indefferences can be observed when you play around with simultaneously handlers. On iOS simultaneously handlers follow along (works as expected) where on Android and web they don't. Please ask if you need an example. https://github.com/software-mansion/react-native-gesture-handler/issues/420 https://github.com/software-mansion/react-native-gesture-handler/issues/927
#useAnimatedGestureHandlerallthis approach is nice for simple use case but has no gesture state control. The same goes for useAnimatedScrollHander. Mixing, constraining and manipulating gestures directly is no longer achievably.
#useAnimatedReactionallThe oldschool approach with react-native-animated have a global scope for animations also known as the <Animation.Code> scope where values from different events can be mixed together and manipulated in direct time. It is rather difficult to achieve the same flexibility with the new hooks approach. Positively the new approach is probably more effective with the hooks and provides a smoother animation experience. useAnimatedReaction scope is the hook that comes the closest to <Animation.Code>
#react-native-reanimatedallA much better control of animations is now achieveable with HOA's (higher-order animations) as the animations functions as a first-class citizen. A few examples can be found in the library under ./src/hoas
#useWindowDimensionsAndroidA micro difference occours when setting the child height within a Animated.ScrollView component to the window height with the use of useWindowDimensions. When exctracting the child height with (onContentSizeChange) then the height says 683.4285888671875 vs the windowheight 683.4285714285714. An offset constant is therefore needed to determine scrollability.
simulator update behaviourallAs reanimated is using worklets and other functionality that runs on a different thread, then a change in props might first work when the simulator is refreshed
#useAnimatedStyleiOSAvoid attach dependencies to this type of hook. Freezing behaviour is likely to occour. Have multiple examples where iOS crashes without any further information.
iOS simulator / Xcode bugiOS / XcodeInitially the keyboard is NOT toggled on the iOS simulator. This causes an 'in-between' animation to occour when an input field is focused. If the keyboard is toggled while the BottomSheet is in an 'in-between' state and it is the first run on the simulator then the simulator will crash while trying to collapse the BottomSheet. The crash message is cryptic. This bug has something to do with Xcode / simulator and is not reproducable on a live device where the keyboard is always shown / toggled by default. This bug dissapears when restarting the simulator after the crash. Ask for an example.
#react-native-reanimatedallAs a programmer there is little to no information on why a worklet crashes in the console. The troubleshooting with reanimated is therefore (from a personal point of view) quite messy and time consuming.
#react-native-reanimatediOSRarely the simulator can crash when selecting an input field that also have an animation. When the crash occours it is reproducable until the moment the metro bundler and simulator is refreshed. The crash is not reproducable on a real device.
debuggingallDebugging tool has to be flipper: Turbomodules on the native side is not supported with Chrome software-mansion/react-native-reanimated#1663
useAnimatedStylewebIt is not possible to have both a translate and interpolate opacity animation at the same time. Flickering will occour.
1.0.9

2 years ago

1.0.8

2 years ago

1.0.7

2 years ago

1.0.6

3 years ago

1.0.5

3 years ago

1.0.4

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago