react-native-reanimated#interpolate TypeScript Examples

The following examples show how to use react-native-reanimated#interpolate. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From react-native-wagmi-charts with MIT License 7 votes vote down vote up
export function getY({
  value,
  domain,
  maxHeight,
}: {
  value: number;
  domain: TDomain;
  maxHeight: number;
}) {
  'worklet';
  return interpolate(value, domain, [maxHeight, 0], Extrapolate.CLAMP);
}
Example #2
Source File: utils.ts    From react-native-wagmi-charts with MIT License 7 votes vote down vote up
export function getHeight({
  value,
  domain,
  maxHeight,
}: {
  value: number;
  domain: TDomain;
  maxHeight: number;
}) {
  'worklet';
  return interpolate(
    value,
    [0, Math.max(...domain) - Math.min(...domain)],
    [0, maxHeight],
    Extrapolate.CLAMP
  );
}
Example #3
Source File: utils.ts    From react-native-wagmi-charts with MIT License 7 votes vote down vote up
export function getPrice({
  y,
  domain,
  maxHeight,
}: {
  y: number;
  domain: TDomain;
  maxHeight: number;
}) {
  'worklet';
  if (y === -1) return -1;
  return interpolate(y, [0, maxHeight], domain.reverse(), Extrapolate.CLAMP);
}
Example #4
Source File: useInit.ts    From react-native-gallery-toolkit with MIT License 6 votes vote down vote up
usedWorklets = {
  withTiming,
  withSpring,
  bezier: Easing.bezier,
  interpolate,
  withDecay,
  useAnimatedGestureHandler,
  ...usedVectors,
} as { [key: string]: any }
Example #5
Source File: CircularProgress.tsx    From tic-tac-toe-app with MIT License 6 votes vote down vote up
CircularProgress: React.FC<PropTypes> = ({ progress, size = 64 }) => {
    const strokeWidth = size * (1 / 2);
    const radius = (size - strokeWidth) / 2;
    const circumference = radius * 2 * Math.PI;

    const theme = useSelector(selectTheme);
    const α = interpolate(progress, {
        inputRange: [0, 1],
        outputRange: [0, -Math.PI * 2],
    });

    const strokeDashoffset = multiply(α, radius);

    return (
        <View style={{ transform: [{ rotate: '-90deg' }] }}>
            <Svg width={size} height={size}>
                <AnimatedCircle
                    stroke={colors[theme].main}
                    fill="none"
                    cx={size / 2}
                    cy={size / 2}
                    r={radius}
                    {...{ strokeWidth, strokeDashoffset }}
                    strokeDasharray={`${circumference} ${circumference}`}
                />
            </Svg>
        </View>
    );
}
Example #6
Source File: getHeight.ts    From react-native-wagmi-charts with MIT License 6 votes vote down vote up
export function getHeight({
  value,
  domain,
  maxHeight,
}: {
  value: number;
  domain: TDomain;
  maxHeight: number;
}) {
  'worklet';
  return interpolate(
    value,
    [0, Math.max(...domain) - Math.min(...domain)],
    [0, maxHeight],
    Extrapolate.CLAMP
  );
}
Example #7
Source File: getPrice.ts    From react-native-wagmi-charts with MIT License 6 votes vote down vote up
export function getPrice({
  y,
  domain,
  maxHeight,
}: {
  y: number;
  domain: TDomain;
  maxHeight: number;
}) {
  'worklet';
  if (y === -1) return -1;
  return interpolate(y, [0, maxHeight], domain.reverse(), Extrapolate.CLAMP);
}
Example #8
Source File: getY.ts    From react-native-wagmi-charts with MIT License 6 votes vote down vote up
export function getY({
  value,
  domain,
  maxHeight,
}: {
  value: number;
  domain: TDomain;
  maxHeight: number;
}) {
  'worklet';
  return interpolate(value, domain, [maxHeight, 0], Extrapolate.CLAMP);
}
Example #9
Source File: BasicSticky.tsx    From react-native-sticky-item with MIT License 5 votes vote down vote up
BasicSticky = ({
  x,
  threshold,
  itemWidth,
  itemHeight,
  stickyItemWidth,
  stickyItemHeight,
  separatorSize,
  isRTL,
}: StickyItemContentProps) => {
  //#region plus
  const animatedPlusScale = interpolate(x, {
    inputRange: [0, threshold],
    outputRange: [0, 1],
    extrapolate: Extrapolate.CLAMP,
  });
  const plusStyle = [
    styles.plus,
    {
      width: stickyItemWidth,
      height: stickyItemHeight,
      [isRTL ? 'right' : 'left']: '50%',
      transform: transformOrigin(
        { x: 0, y: 0 },
        {
          translateX: interpolate(x, {
            inputRange: [separatorSize, threshold],
            outputRange: [
              (stickyItemWidth / 2) * (isRTL ? 1 : -1),
              (itemWidth / 2 - stickyItemWidth) * (isRTL ? -1 : 1),
            ],
            extrapolate: Extrapolate.CLAMP,
          }),
          translateY: itemHeight / 2 - stickyItemHeight / 2,
          scale: animatedPlusScale,
        }
      ) as Animated.AnimatedTransform,
    },
  ];
  //#endregion

  //#region text
  const animatedTextOpacity = interpolate(x, {
    inputRange: [0, threshold * 0.6],
    outputRange: [1, 0],
    extrapolate: Extrapolate.CLAMP,
  });
  const textStyle = [
    styles.text,
    {
      opacity: animatedTextOpacity,
      paddingHorizontal: separatorSize,
      lineHeight: itemHeight,
      transform: [
        {
          translateY: 0,
        },
      ] as Animated.AnimatedTransform,
    },
  ];
  //#endregion

  return (
    <>
      <Animated.View style={plusStyle}>
        <PlusSVG />
      </Animated.View>
      <Animated.Text style={textStyle}>
        {isRTL ? 'להוסיף' : 'Add'}
      </Animated.Text>
    </>
  );
}
Example #10
Source File: ScalableImageExample.tsx    From react-native-gallery-toolkit with MIT License 5 votes vote down vote up
export default function StandaloneGalleryBasicScreen() {
  const { controlsStyles, setControlsHidden } = useControls();

  const opacity = useSharedValue(0);

  const overlayStyles = useAnimatedStyle(() => {
    return {
      opacity: opacity.value,
      backgroundColor: 'black',
    };
  });

  const onScale = useWorkletCallback((scale: number) => {
    opacity.value = interpolate(
      scale,
      [1, 2],
      [0, 0.3],
      Extrapolate.CLAMP,
    );
  }, []);

  const onGestureStartCallback = () => {
    StatusBar.setHidden(true);
  };
  const onGestureReleaseCallback = () => {
    StatusBar.setHidden(false);
  };

  const onGestureStart = useWorkletCallback(() => {
    setControlsHidden(true);
    runOnJS(onGestureStartCallback)();
  });

  const onGestureRelease = useWorkletCallback(() => {
    setControlsHidden(false);
    runOnJS(onGestureReleaseCallback)();
  });

  return (
    <View style={{ flex: 1, backgroundColor: 'transparent' }}>
      <Animated.View
        pointerEvents="none"
        style={[StyleSheet.absoluteFill, overlayStyles]}
      />

      <View
        style={{
          zIndex: 0,
          flex: 1,
          justifyContent: 'center',
        }}
      >
        <ScalableImage
          width={image.width}
          height={image.height}
          source={image.uri}
          onScale={onScale}
          onGestureStart={onGestureStart}
          onGestureRelease={onGestureRelease}
        />
      </View>

      <Animated.View style={controlsStyles}>
        <DetachedHeader.Container>
          <DetachedHeader />
        </DetachedHeader.Container>
      </Animated.View>
    </View>
  );
}
Example #11
Source File: HorizontalFlatListExample.tsx    From react-native-scroll-bottom-sheet with MIT License 5 votes vote down vote up
HorizontalFlatListExample: React.FC<Props> = ({ navigation }) => {
  const bottomSheetRef = React.useRef<ScrollBottomSheet<any> | null>(null);

  const animatedPosition = React.useRef(new Value(0));
  const opacity = interpolate(animatedPosition.current, {
    inputRange: [0, 1],
    outputRange: [0, 0.75],
    extrapolate: Extrapolate.CLAMP,
  });

  const renderRow = React.useCallback(
    ({ index }) => <Carousel index={index} />,
    []
  );

  return (
    <View style={styles.container}>
      <MapView
        style={StyleSheet.absoluteFillObject}
        initialRegion={initialRegion}
      />
      <Animated.View
        pointerEvents="box-none"
        style={[
          StyleSheet.absoluteFillObject,
          { backgroundColor: 'black', opacity },
        ]}
      />
      <View style={StyleSheet.absoluteFillObject} pointerEvents="box-none">
        <TouchableRipple
          style={[styles.iconContainer, { right: 16 }]}
          onPress={() => {
            bottomSheetRef.current?.snapTo(2);
          }}
          borderless
        >
          <MaterialCommunityIcons
            name="close"
            size={32}
            color="white"
            style={styles.icon}
          />
        </TouchableRipple>
        {Platform.OS === 'ios' && (
          <TouchableRipple
            style={[styles.iconContainer, { left: 16 }]}
            onPress={() => {
              navigation.goBack();
            }}
            borderless
          >
            <Ionicons
              name="ios-arrow-back"
              size={32}
              color="white"
              style={styles.icon}
            />
          </TouchableRipple>
        )}
      </View>
      <ScrollBottomSheet<string>
        ref={bottomSheetRef}
        componentType="FlatList"
        topInset={24}
        animatedPosition={animatedPosition.current}
        snapPoints={snapPointsFromTop}
        initialSnapIndex={2}
        renderHandle={() => <Handle />}
        keyExtractor={i => `row-${i}`}
        initialNumToRender={5}
        contentContainerStyle={styles.contentContainerStyle}
        data={Array.from({ length: 100 }).map((_, i) => String(i))}
        renderItem={renderRow}
      />
    </View>
  );
}
Example #12
Source File: FlatListPageIndicator.tsx    From nyxo-app with GNU General Public License v3.0 5 votes vote down vote up
ExpandingDot: FC<Props> = ({
  scrollX,
  data,
  dotStyle,
  style,
  inActiveDotOpacity,
  expandingDotWidth,
  width = WIDTH
}) => {
  const defaultProps = {
    inActiveDotOpacity: inActiveDotOpacity || 0.5,
    expandingDotWidth: expandingDotWidth || 20,
    dotWidth: (dotStyle?.width as number) || 10
  }

  return (
    <Container style={style}>
      {data.map((_, index: number) => {
        const inputRange = [
          (index - 1) * width,
          index * width,
          (index + 1) * width
        ]

        const opacity = interpolate(scrollX, {
          inputRange,
          outputRange: [
            defaultProps.inActiveDotOpacity,
            1,
            defaultProps.inActiveDotOpacity
          ],
          extrapolateRight: Animated.Extrapolate.CLAMP,
          extrapolateLeft: Animated.Extrapolate.CLAMP
        })
        const expand = interpolate(scrollX, {
          inputRange,
          outputRange: [
            defaultProps.dotWidth,
            defaultProps.expandingDotWidth,
            defaultProps.dotWidth
          ],
          extrapolateRight: Animated.Extrapolate.CLAMP,
          extrapolateLeft: Animated.Extrapolate.CLAMP
        })

        return (
          <Dot
            // eslint-disable-next-line react/no-array-index-key
            key={`dot-${index}`}
            style={[dotStyle, { width: expand }, { opacity }]}
          />
        )
      })}
    </Container>
  )
}
Example #13
Source File: WeekIntro.tsx    From nyxo-app with GNU General Public License v3.0 5 votes vote down vote up
WeekIntro: FC<Props> = ({
  intro,
  description,
  habitCount,
  lessonCount,
  loading
}) => {
  const animationValue = useValue(0)
  const isLoading = useValue(loading ? 1 : 0)

  useCode(
    () =>
      cond(eq(isLoading, 1), [
        set(
          animationValue,
          loop({
            duration: DEFAULT_DURATION,
            easing: DEFAULT_EASING,
            boomerang: true
          })
        )
      ]),
    [isLoading, animationValue]
  )

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const animatedStyle: any = {
    transform: [
      {
        translateX: interpolate(animationValue, {
          inputRange: [0, 1],
          outputRange: ['-100%', '100%']
        })
      }
    ]
  }

  return (
    <Container>
      <Card>
        {loading ? (
          <IntroSkeleton animatedStyle={animatedStyle} />
        ) : (
          <Intro>{intro}</Intro>
        )}
        <Information>
          {habitCount > 0 && (
            <>
              <HabitIcon />
              <Habits variables={{ count: habitCount }}>
                WEEK_VIEW.HABIT_COUNT
              </Habits>
            </>
          )}
          {lessonCount > 0 && (
            <LessonIcon>
              <LessonIcon />
              <Habits variables={{ count: lessonCount }}>
                WEEK_VIEW.LESSON_COUNT
              </Habits>
            </LessonIcon>
          )}
        </Information>
        <Description secondary>
          {description && <RichText content={description} />}
        </Description>
      </Card>
    </Container>
  )
}
Example #14
Source File: AnimatedHelper.ts    From curved-bottom-navigation-bar with MIT License 5 votes vote down vote up
useInterpolate = (
  progress: Animated.SharedValue<number>,
  input: number[],
  output: number[],
  type?: Animated.Extrapolate
) => useDerivedValue(() => interpolate(progress.value, input, output, type))
Example #15
Source File: HabitCard.tsx    From nyxo-app with GNU General Public License v3.0 4 votes vote down vote up
HabitCard: FC<Props> = (props) => {
  const ref = useRef<Swipeable>(null)
  const dispatch = useAppDispatch()
  const { habit } = props
  const {
    habit: { period, dayStreak = 0 }
  } = props
  const { color } = getIcon(period)

  const completed = isCompletedToday(habit)

  const toggleCompleted = () => {
    dispatch(markTodayHabitAsCompleted(habit))
    close()
  }

  const editHabit = () => {
    dispatch(draftEditHabit(habit))
    dispatch(toggleEditHabitModal())
  }

  const deleteHabit = () => {
    dispatch(deleteHabitById(habit))
    close()
  }
  const archiveHabit = () => {
    dispatch(archiveHabitThunk(habit))
    close()
  }

  const close = () => {
    // eslint-disable-next-line no-unused-expressions
    ref?.current?.close()
  }

  const renderLeftActions = (
    progress: Animated.Value<number>,
    dragX: Animated.Value<number>
  ) => {
    const animation = interpolate(dragX, {
      inputRange: [0, 100],
      outputRange: [0, 1]
    })
    return (
      <>
        <ActionComplete
          animation={animation}
          direction="LEFT"
          icon="archive"
          buttonText="HABIT.ARCHIVE"
          action={archiveHabit}
        />
        <ActionComplete
          animation={animation}
          direction="LEFT"
          icon="bin"
          buttonText="HABIT.DELETE"
          action={deleteHabit}
        />
      </>
    )
  }

  const renderRightActions = (
    progress: Animated.Value<number>,
    dragX: Animated.Value<number>
  ) => {
    const animation = interpolate(dragX, {
      inputRange: [-150, 0],
      outputRange: [1, 0]
    })

    return (
      <>
        <ActionComplete
          animation={animation}
          direction="RIGHT"
          icon="checkMark"
          buttonText="HABIT.COMPLETE"
          action={toggleCompleted}
        />
      </>
    )
  }

  return (
    <Swipeable
      ref={ref}
      renderLeftActions={renderLeftActions}
      renderRightActions={renderRightActions}>
      <BorderlessButton onPress={editHabit}>
        <Card>
          <PeriodBarIndicator backgroundColor={color} />

          <MiddleSector>
            <PeriodIndicator accent={color}>
              {`HABIT.EVERY_${period.toUpperCase()}`}
            </PeriodIndicator>
            <Separator />
            <TitleHolder completedToday={completed}>{habit.title}</TitleHolder>
            <Separator />
            <DayStreakContainer>
              <IconBold width={12} height={12} name="flame" fill="#adadad" />
              <DayStreak>{dayStreak}</DayStreak>
            </DayStreakContainer>
          </MiddleSector>

          <CheckIconHolder>
            {completed && (
              <IconBold
                width={15}
                height={15}
                name="checkMark"
                fill={colors.darkBlue}
              />
            )}
          </CheckIconHolder>
        </Card>
      </BorderlessButton>
    </Swipeable>
  )
}
Example #16
Source File: FacebookStickyStory.tsx    From react-native-sticky-item with MIT License 4 votes vote down vote up
FacebookStickyStory = ({
  x,
  threshold,
  itemWidth,
  itemHeight,
  stickyItemWidth,
  separatorSize,
  borderRadius,
  isRTL,
  theme = 'light',
}: StickyItemContentProps & FacebookStickyStoryProps) => {
  const stickyItemX = itemWidth / 2 + (itemWidth / 2 - stickyItemWidth);
  const stickyItemY = itemHeight / 2 - stickyItemWidth / 2;
  const stickyItemWidthWithoutPadding = stickyItemWidth - separatorSize * 2;
  const separatorSizeToStickyWidthScale = Math.min(
    separatorSize / stickyItemWidth,
    0.2
  );

  //#region thumbnail
  const thumbnailWidth = itemWidth;
  const thumbnailHeight = itemWidth;

  const thumbnailTranslateX =
    Math.abs(thumbnailWidth / 2 - (stickyItemX + stickyItemWidth / 2)) *
    (isRTL ? -1 : 1);
  const thumbnailTranslateY = Math.abs(
    thumbnailHeight / 2 - (stickyItemY + stickyItemWidth / 2)
  );

  const thumbnailScale =
    stickyItemWidth / itemWidth - separatorSizeToStickyWidthScale;
  const animatedThumbnailScale = interpolate(x, {
    inputRange: [separatorSize, threshold],
    outputRange: [1, thumbnailScale],
    extrapolate: Extrapolate.CLAMP,
  });
  const animatedThumbnailTranslateX = interpolate(x, {
    inputRange: [separatorSize, threshold],
    outputRange: [0, thumbnailTranslateX],
    extrapolate: Extrapolate.CLAMP,
  });
  const animatedThumbnailTranslateY = interpolate(x, {
    inputRange: [separatorSize, threshold],
    outputRange: [0, thumbnailTranslateY],
    extrapolate: Extrapolate.CLAMP,
  });
  const animatedThumbnailBorderRadius = interpolate(x, {
    inputRange: [separatorSize, threshold],
    outputRange: [
      borderRadius,
      stickyItemWidth * (separatorSizeToStickyWidthScale + 1),
    ],
    extrapolate: Extrapolate.CLAMP,
  });

  const thumbnailStyle = [
    {
      backgroundColor: theme === 'light' ? 'black' : 'white',
      width: thumbnailWidth,
      height: thumbnailHeight,
      borderRadius: animatedThumbnailBorderRadius,
      transform: [
        { translateX: (thumbnailWidth / 2) * -1 },
        { translateY: (thumbnailHeight / 2) * -1 },
        { translateX: animatedThumbnailTranslateX },
        { translateY: animatedThumbnailTranslateY },
        { translateX: thumbnailWidth / 2 },
        { translateY: thumbnailHeight / 2 },
        { scale: animatedThumbnailScale },
      ],
    },
  ];
  //#endregion

  //#region add icon
  const addIconWidth = 30;
  const addIconHeight = 30;

  const addIconPosition = findPointOnCircle({
    radius: stickyItemWidthWithoutPadding / 2,
    degrees: isRTL ? 135 : 45,
  });
  const animatedAddIconTranslateX = interpolate(x, {
    inputRange: [separatorSize, threshold],
    outputRange: [0, addIconPosition.x],
    extrapolate: Extrapolate.CLAMP,
  });
  const animatedAddIconTranslateY = interpolate(x, {
    inputRange: [separatorSize, threshold],
    outputRange: [thumbnailHeight / 2, addIconPosition.y],
    extrapolate: Extrapolate.CLAMP,
  });
  const animatedAddIconScale = interpolate(x, {
    inputRange: [separatorSize, threshold],
    outputRange: [1, 0.33],
    extrapolate: Extrapolate.CLAMP,
  });
  const animatedAddIconBorderWidth = interpolate(x, {
    inputRange: [separatorSize, threshold],
    outputRange: [3, 2],
    extrapolate: Extrapolate.CLAMP,
  });
  const addIconStyle = [
    styles.addIcon,
    {
      width: addIconWidth,
      height: addIconHeight,
      borderRadius: addIconWidth,
      borderWidth: animatedAddIconBorderWidth,
      transform: [
        { translateX: (addIconWidth / 2) * -1 },
        { translateY: (addIconHeight / 2) * -1 },
        { translateX: thumbnailWidth / 2 },
        { translateY: thumbnailHeight / 2 },
        { translateX: animatedThumbnailTranslateX },
        { translateY: animatedThumbnailTranslateY },
        { translateX: animatedAddIconTranslateX },
        { translateY: animatedAddIconTranslateY },
        { scale: animatedAddIconScale },
      ],
    },
  ];
  //#endregion

  //#region text
  const animatedTextOpacity = interpolate(x, {
    inputRange: [separatorSize, threshold * 0.6],
    outputRange: [1, 0],
    extrapolate: Extrapolate.CLAMP,
  });
  const animatedTextTranslateY = interpolate(x, {
    inputRange: [separatorSize, threshold * 0.6],
    outputRange: [itemHeight / 2 + itemHeight / 4, itemHeight / 2],
    extrapolate: Extrapolate.CLAMP,
  });
  const textStyle = [
    styles.text,
    {
      color: theme === 'light' ? 'black' : 'white',
      opacity: animatedTextOpacity,
      paddingHorizontal: separatorSize * 2,
      transform: [
        {
          translateY: animatedTextTranslateY,
        },
      ] as Animated.AnimatedTransform,
    },
  ];
  //#endregion

  return (
    <>
      <Animated.View style={thumbnailStyle} />
      <Animated.Text style={textStyle}>
        {isRTL ? `إضافة إلى قصتك` : `Create a story`}
      </Animated.Text>
      <Animated.View style={addIconStyle} />
    </>
  );
}
Example #17
Source File: SectionListExample.tsx    From react-native-scroll-bottom-sheet with MIT License 4 votes vote down vote up
SectionListExample: React.FC<Props> = () => {
  const snapPointsFromTop = [96, '45%', windowHeight - 264];
  const animatedPosition = React.useRef(new Value(0.5));
  const handleLeftRotate = concat(
    interpolate(animatedPosition.current, {
      inputRange: [0, 0.4, 1],
      outputRange: [25, 0, 0],
      extrapolate: Extrapolate.CLAMP,
    }),
    'deg'
  );
  const handleRightRotate = concat(
    interpolate(animatedPosition.current, {
      inputRange: [0, 0.4, 1],
      outputRange: [-25, 0, 0],
      extrapolate: Extrapolate.CLAMP,
    }),
    'deg'
  );
  const cardScale = interpolate(animatedPosition.current, {
    inputRange: [0, 0.6, 1],
    outputRange: [1, 1, 0.9],
    extrapolate: Extrapolate.CLAMP,
  });

  const renderSectionHeader = React.useCallback(
    ({ section }) => (
      <View style={styles.section}>
        <Text>{section.title}</Text>
      </View>
    ),
    []
  );

  const renderItem = React.useCallback(
    ({ item }) => <Transaction {...item} />,
    []
  );

  return (
    <View style={styles.container}>
      <View style={styles.balanceContainer}>
        <Text style={styles.poundSign}>£</Text>
        <Text style={styles.balance}>4,345</Text>
      </View>
      <ProgressBar
        style={styles.progressBar}
        progress={0.8}
        color={Colors.green600}
      />
      <Animated.Image
        source={require('../assets/card-front.png')}
        style={[styles.card, { transform: [{ scale: cardScale }] }]}
      />
      <View style={styles.row}>
        <View>
          <View style={styles.action}>
            <FontAwesome5 name="credit-card" size={24} color="black" />
          </View>
          <Text style={{ textAlign: 'center' }}>Account</Text>
        </View>
        <View>
          <View style={styles.action}>
            <FontAwesome5 name="eye" size={24} color="black" />
          </View>
          <Text style={{ textAlign: 'center' }}>Pin</Text>
        </View>
        <View>
          <View style={styles.action}>
            <Ionicons name="md-snow" size={24} color="black" />
          </View>
          <Text style={{ textAlign: 'center' }}>Freeze</Text>
        </View>
        <View>
          <View style={styles.action}>
            <FontAwesome5 name="plus" size={24} color="black" />
          </View>
          <Text style={{ textAlign: 'center' }}>Top up</Text>
        </View>
      </View>
      <ScrollBottomSheet<ListItemData>
        enableOverScroll
        removeClippedSubviews={Platform.OS === 'android' && sections.length > 0}
        componentType="SectionList"
        topInset={statusBarHeight + navBarHeight}
        animatedPosition={animatedPosition.current}
        snapPoints={snapPointsFromTop}
        initialSnapIndex={1}
        animationConfig={{
          easing: Easing.inOut(Easing.linear),
        }}
        renderHandle={() => (
          <Handle style={{ paddingVertical: 20, backgroundColor: '#F3F4F9' }}>
            <Animated.View
              style={[
                styles.handle,
                {
                  left: windowWidth / 2 - 20,
                  transform: [{ rotate: handleLeftRotate }],
                },
              ]}
            />
            <Animated.View
              style={[
                styles.handle,
                {
                  right: windowWidth / 2 - 20,
                  transform: [{ rotate: handleRightRotate }],
                },
              ]}
            />
          </Handle>
        )}
        contentContainerStyle={styles.contentContainerStyle}
        stickySectionHeadersEnabled
        sections={sections}
        keyExtractor={i => i.id}
        renderSectionHeader={renderSectionHeader}
        renderItem={renderItem}
      />
    </View>
  );
}
Example #18
Source File: InstagramFeed.tsx    From react-native-gallery-toolkit with MIT License 4 votes vote down vote up
function RenderItem({
  index: _index,
  activeItemIndex,
  item: { images, name },
  setControlsHidden,
  scrollViewRef,
}: RenderItemProps) {
  const opacity = useSharedValue(0);
  const backgroundScale = useSharedValue(0);
  const activeIndexInPager = useSharedValue(0);

  const normalizedImages = useMemo(
    () =>
      images.map((item) => {
        const { targetWidth, targetHeight } = normalizeDimensions(
          item,
        );

        return {
          ...item,
          width: targetWidth,
          height: targetHeight,
        };
      }),
    [images],
  );

  const onScale = useWorkletCallback((scale: number) => {
    opacity.value = interpolate(
      scale,
      [1, 2],
      [0, 0.7],
      Extrapolate.CLAMP,
    );

    backgroundScale.value = interpolate(
      scale,
      [1, 1.01, 2],
      [0, 4, 5],
      Extrapolate.CLAMP,
    );
  }, []);

  const onGestureStart = useWorkletCallback(() => {
    setControlsHidden(true);
    runOnJS(StatusBar.setHidden)(true);
    activeItemIndex.value = _index;
  }, []);

  const onGestureRelease = useWorkletCallback(() => {
    //delay for smooth hiding background opacity
    activeItemIndex.value = withDelay(200, withTiming(-1));
    setControlsHidden(false);
    runOnJS(StatusBar.setHidden)(false);
  }, []);

  const overlayStyles = useAnimatedStyle(() => {
    return {
      opacity: opacity.value,
      transform: [
        {
          scale: backgroundScale.value,
        },
      ],
    };
  });

  const keyExtractor = useCallback(
    ({ id }: { id: string }) => id,
    [],
  );

  const canvasHeight = useMemo(
    () => Math.max(...normalizedImages.map((item) => item.height)),
    [normalizedImages],
  );

  const renderPage = useCallback(({ item, pagerRefs }: RenderPageProps<SimpleGalleryItemType>) => {
    return (
      <ScalableImage
        outerGestureHandlerRefs={[...pagerRefs, scrollViewRef]}
        source={item.uri}
        width={item.width}
        height={item.height}
        onScale={onScale}
        onGestureStart={onGestureStart}
        onGestureRelease={onGestureRelease}
      />
    );
  }, []);

  const onIndexChangeWorklet = useWorkletCallback((nextIndex: number) => {
    activeIndexInPager.value = nextIndex;
  }, []);

  const content = (() => {
    if (images.length === 1) {
      return (
        <ScalableImage
          source={images[0].uri}
          width={images[0].width}
          height={images[0].height}
          onScale={onScale}
          outerGestureHandlerRefs={[scrollViewRef]}
          onGestureStart={onGestureStart}
          onGestureRelease={onGestureRelease}
        />
      );
    } else {
      return (
        <>
          <Pager
            pages={images}
            totalCount={images.length}
            keyExtractor={keyExtractor}
            initialIndex={0}
            width={width}
            gutterWidth={0}
            outerGestureHandlerRefs={[scrollViewRef]}
            verticallyEnabled={false}
            // @ts-expect-error
            renderPage={renderPage}
            onIndexChange={onIndexChangeWorklet}
          />

          <Pagination
            length={images.length}
            activeIndexInPager={activeIndexInPager}
          />
        </>
      );
    }
  })();

  return (
    <Animated.View style={s.itemContainer}>
      <Header uri={images[0].uri} name={name} />

      <Animated.View
        pointerEvents="none"
        style={[s.overlay, overlayStyles]}
      />

      <View style={[s.itemPager, { height: canvasHeight }]}>
        {content}
      </View>

      <Footer />
    </Animated.View>
  );
}
Example #19
Source File: Bar.tsx    From expo-progress with MIT License 4 votes vote down vote up
function ProgressBar({
  isIndeterminate = false,
  duration = isIndeterminate ? 1000 : 500,
  isAnimated = false,
  progress = isIndeterminate ? 0.5 : 0,
  height = 7,
  borderRadius = height * 0.5,
  // Default iOS blue
  color = '#007aff',
  trackColor = 'transparent',
  style,
  trackImage,
  progressImage,
}: ProgressBarProps) {
  const [width, setWidth] = React.useState(0);
  const progressValue = useValue(isAnimated ? 0 : progress);
  const indeterminateValue = useValue(0);
  const animatedWidth = interpolate(
    clamp(progressValue, minProgress, maxProgress),
    {
      inputRange: [minProgress, maxProgress],
      outputRange: [0, width],
      extrapolate: Extrapolate.CLAMP,
    }
  );

  useCode(() => {
    if (isAnimated) {
      return set(
        progressValue,
        timing({
          from: progressValue,
          to: progress,
          duration,
        })
      );
    } else {
      return set(progressValue, progress);
    }
  }, [progress]);

  useCode(() => {
    if (isIndeterminate) {
      const loopingIndeterminateValue = loop({
        autoStart: true,
        boomerang: false,
        duration,
      });
      return set(indeterminateValue, loopingIndeterminateValue);
    }
    const animatedIndeterminateValue = timing({
      from: indeterminateValue,
      to: 0,
    });
    return set(indeterminateValue, animatedIndeterminateValue);
  }, [isIndeterminate]);

  // todo: web has a bug where the reanimated Animated.View style is not updating unless this is an animated value.
  let translateX: Node<number> | number = useValue(0);

  if (isIndeterminate) {
    translateX = interpolate(indeterminateValue, {
      inputRange: [0, 1],
      outputRange: [multiply(-1, animatedWidth), width],
    });
  }

  return (
    <ImageBackground
      onLayout={(e: LayoutChangeEvent) => {
        setWidth(e.nativeEvent.layout.width);
      }}
      resizeMode={'stretch'}
      style={[
        styles.container,
        {
          height,
          borderRadius,
          backgroundColor: trackColor,
        },
        style,
      ]}
      // @ts-ignore
      source={trackImage}
    >
      <Animated.Image
        style={[
          styles.bar,
          {
            width: animatedWidth,
            transform: [
              {
                translateX,
              },
            ],
            backgroundColor: color,
            borderRadius,
          },
        ]}
        // @ts-ignore
        source={progressImage}
      />
    </ImageBackground>
  );
}