RIPPLE.TSX import React from 'react'; import {Alert, Dimensions, StyleSheet, Text, View} from 'react-native';import {Feather as Icon} from '@expo/vector-icons';import RippleButton from './RippleButton';import {StyleGuide} from '../components';const {width} = Dimensions.get('window');const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',backgroundColor: '#ecf0f1',padding: 8,},touchable: {marginVertical: 16,width: width - 63,height: 45,justifyContent: 'center',alignItems: 'center',flexDirection: 'row',},text: {alignSelf: 'center',fontSize: 24,fontWeight: '400',marginLeft: 16,},});const App = () => {return (<View style={styles.container}><RippleButton color={StyleGuide.palette.primary}><View style={styles.touchable}><Icon name="trash" color="white" size={24} /><Text style={[styles.text, {color: 'white'}]}>Delete</Text></View></RippleButton><RippleButton color="#DC034E"><View style={styles.touchable}><Icon name="send" color="white" size={24} /><Text style={[styles.text, {color: 'white'}]}>Send</Text></View></RippleButton><RippleButton color="#E0E0E0"><View style={styles.touchable}><Icon name="share" size={24} /><Text style={styles.text}>Upload</Text></View></RippleButton></View>);};export default App;
RippleButton.tsx
import React, {Children, ReactElement, ReactNode, useState} from 'react';
import {State, TapGestureHandler} from 'react-native-gesture-handler';
import Animated, {
  and,
  call,
  cond,
  diff,
  eq,
  greaterOrEq,
  greaterThan,
  neq,
  onChange,
  or,
  useCode,
} from 'react-native-reanimated';
import {StyleSheet, View, ViewProps} from 'react-native';
import {
  mix,
  translate,
  useDebug,
  useTapGestureHandler,
  vec,
  withTransition,
} from 'react-native-redash';
import Color from 'color';
interface RippleButtonProps {
  children: ReactElement<ViewProps>;
  color: string;
  onPress?: () => void;
}
const RippleButton = ({children, color, onPress}: RippleButtonProps) => {
  const [radius, setRadius] = useState(-1);
  const {gestureHandler, position, state} = useTapGestureHandler();
  const child = Children.only(children);
  const progress = withTransition(eq(state, State.BEGAN));
  const isGoingUp = or(greaterThan(diff(progress), 0), eq(progress, 1));
  const scale = mix(progress, 0.001, 1);
  const opacity = isGoingUp;
  const backgroundColor = Color(color).lighten(0.1);
  useCode(
    () => [
      onChange(
        state,
        cond(eq(state, State.END), [call([], onPress || (() => null))]),
      ),
    ],
    [onPress],
  );
  return (
    <TapGestureHandler {...gestureHandler}>
      <Animated.View {...child.props} style={[child.props.style]}>
        <View
          style={{
            ...StyleSheet.absoluteFillObject,
            borderRadius: 14,
            backgroundColor: color,
            overflow: 'hidden',
          }}
          onLayout={({
            nativeEvent: {
              layout: {height, width},
            },
          }) => setRadius(Math.sqrt(width ** 2 + height ** 2))}>
          {radius !== -1 && (
            <Animated.View
              style={{
                opacity,
                backgroundColor,
                borderRadius: radius,
                width: radius * 2,
                height: radius * 2,
                transform: [
                  ...translate(vec.create(-radius)),
                  ...translate(position),
                  {scale},
                ],
              }}
            />
          )}
        </View>
        {child.props.children}
      </Animated.View>
    </TapGestureHandler>
  );
};
export default RippleButton;

No comments:
Post a Comment