lottie-react-native动画库github以及官网

可以用于页面加载时的动画

import React, {FC} from "react";
import {SafeAreaView, ScrollView, Text} from "react-native";
import Lottie from 'lottie-react-native';


interface Props {

}

const LottieReactNative: FC<Props> = () => {
    const animationRef = React.useRef<Lottie | null>(null);

    React.useEffect(() => {
       
    }, []);

    return <SafeAreaView>
        <ScrollView>
            <Lottie source={require('./animation.json')} autoPlay loop style={{width: 100}}/>
        </ScrollView>
    </SafeAreaView>;
};

export default LottieReactNative;

192B37FF-0A1A-4A48-8A79-87D44B0FD655-85254-000044162DF453EB.GIF

react-icomoon轻量图标

import React from "react";
import {SafeAreaView, ScrollView} from "react-native";
import IcoMoon, {iconList} from "react-icomoon";
const iconSet = require('./selection.json');
import { Svg, Path } from 'react-native-svg';

const ReactIcoMoon = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView>
        <ScrollView>
            <IcoMoon
                native
                iconSet={iconSet}
                SvgComponent={Svg}
                PathComponent={Path}
                icon="heart"
                size={30}
                style={{ margin: 50, color: '#f40' }}
            />
        </ScrollView>
    </SafeAreaView>;
};

export default ReactIcoMoon;

WechatIMG85.jpeg

react-native-calendars日历

import React from "react";
import {SafeAreaView, ScrollView} from "react-native";
import {Calendar, LocaleConfig} from 'react-native-calendars';

LocaleConfig['locales'][''] = {
    monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
    monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
    dayNames: ['周一', '周二', '周三', '周四', '周五', '周六', '周天'],
    dayNamesShort: ['周一', '周二', '周三', '周四', '周五', '周六', '周天'],
    amDesignator: '上午',
    pmDesignator: '下午'
}
const ReactNativeCalendars = () => {
    const [selected, setSelected] = React.useState('');
    React.useEffect(() => {

    }, []);
    return <SafeAreaView>
        <ScrollView>
            <Calendar
                onDayPress={(day) => {
                    setSelected(day.dateString);
                }}
                style={{height: 350}}
                markedDates={{
                    [selected]: {selected: true, disableTouchEvent: true, selectedDotColor: 'orange'}
                }}
            />
        </ScrollView>
    </SafeAreaView>;
};

export default ReactNativeCalendars;

E9674479-3137-411D-B674-EEDEBBE27A61-85254-0000443FCD644D9E.GIF

react-native-drawer抽屉

<Drawer
     type="displace"
     ref={drawerRef}
     acceptDoubleTap={true}
     captureGestures={false}
     tweenDuration={100}
     panThreshold={0.08}
     onOpen={() => setOpen(true)}
     onClose={() => setOpen(false)}
     openDrawerOffset={() => 90}
     panOpenMask={0.2}
     open={open}
     negotiatePan={true}
     content={<ScrollView style={styles.container}>
         <ReactNativeCalendars onSelect={(date) => {
             drawerRef.current!.close();
             setDate(date);
         }}/>
     </ScrollView>}
     styles={{drawer: styles.drawer, main: styles.main}}
     tweenHandler={(ratio) => ({
         main: {opacity: (2 - ratio) / 2}
     })}
 >
     <ScrollView style={[styles.container, {backgroundColor: '#FFF',}]}>
         <Text onPress={() => drawerRef.current!.open()}>{date || '选择日期'}</Text>
     </ScrollView>
</Drawer>

D8A133C5-4CB8-43C5-BEB2-435F39B112A0-94224-00004579DD804CF7.GIF

react-native-textinput-effects漂亮的输入框

import React from "react";
import {SafeAreaView, ScrollView} from "react-native";
import {Fumi} from 'react-native-textinput-effects';
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome';


const ReactNativeTextInputEffects = () => {
    const [value, setValue] = React.useState('');
    React.useEffect(() => {

    }, []);
    return <SafeAreaView>
        <ScrollView>
            <Fumi
                style={{backgroundColor: '#f9f5ed'}}
                label="账号"
                iconClass={FontAwesomeIcon}
                iconName="university"
                iconColor="#f95a25"
                iconSize={20}
                iconWidth={40}
                inputPadding={16}
                onChangeText={setValue}
                value={value}
                keyboardType="number-pad"
                blurOnSubmit={true}
            />
        </ScrollView>
    </SafeAreaView>;
};

export default ReactNativeTextInputEffects;

IMG_9131.GIF

react-native-lightbox查看图片(这个库有点老复制下来重写)

import React, {FC} from "react";
import {
    View,
    ViewStyle,
    Animated,
    Easing,
    Dimensions,
    Platform,
    StyleSheet,
    StatusBar,
    Modal,
    TouchableOpacity,
    Text, TouchableWithoutFeedback, Image
} from "react-native";


const WINDOW_HEIGHT = Dimensions.get('window').height;
const WINDOW_WIDTH = Dimensions.get('window').width;
const isIOS = Platform.OS === 'ios';

export interface ImageLightBoxProps {
    style?: ViewStyle;
    uri: string;
}

const styles = StyleSheet.create({
    background: {
        position: 'absolute',
        top: 0,
        left: 0,
        width: WINDOW_WIDTH,
        height: WINDOW_HEIGHT,
        backgroundColor: '#000'
    },
    open: {
        position: 'absolute',
        flex: 1,
        justifyContent: 'center',
        backgroundColor: 'transparent',
    },
    header: {
        position: 'absolute',
        top: 0,
        left: 0,
        width: WINDOW_WIDTH,
        backgroundColor: 'transparent',
    },
    closeButton: {
        fontSize: 35,
        color: 'white',
        lineHeight: 40,
        width: 40,
        textAlign: 'center',
        shadowOffset: {
            width: 0,
            height: 0,
        },
        shadowRadius: 1.5,
        shadowColor: 'black',
        shadowOpacity: 0.8,
    },
});

const springConfig = {
    tension: 1,
    friction: 100,
    useNativeDriver: false
}
const ImageLightBox: FC<ImageLightBoxProps> = (props) => {
    const rootRef = React.useRef<Image>();
    const animatedLayoutOpacity = React.useRef(new Animated.Value(1));
    const animatedOpenValue = React.useRef(new Animated.Value(0));
    const [origin, setOrigin] = React.useState({x: 0, y: 0, width: 0, height: 0});
    const [isOpen, setIsOpen] = React.useState(false);
    const [target, setTarget] = React.useState({x: 0, y: 0, opacity: 1});
    const [imageStyle, setImageStyle] = React.useState<{ height?: number | string; width?: number | string }>({});

    React.useEffect(() => {
        Image.getSize(props.uri, (w, h) => {
            if (props.style) {
                const {height, width} = props.style;
                if (height && !width) {
                    setImageStyle({width: height * w / h, height});
                } else if (!height && width) {
                    setImageStyle({width, height: width * h / w});
                } else if (height && width) {
                    setImageStyle({width, height});
                } else {
                    setImageStyle({height: h, width: w})
                }
            }
        });
    }, [props.uri]);

    const open = () => {
        rootRef.current!.measure((ox, oy, width, height, px, py) => {
            if (isIOS) {
                StatusBar.setHidden(true, 'fade');
            }
            setIsOpen(true);
            setTarget({x: 0, y: 0, opacity: 1});
            // 获取图片的位置并隐身
            setOrigin({width, height, x: px, y: py});
            Animated.parallel([
                setOpacity(0),
                setOpenValue(1)
            ]).start();
        });
    }

    const close = () => {
        if (isIOS) {
            StatusBar.setHidden(false, 'fade');
        }
        setOpenValue(0).start(() => {
            setOpacity(1).start(() => {
                setIsOpen(false);
            });
        });
    }

    const setOpenValue = (value) => {
        return Animated.spring(animatedOpenValue.current, {
            toValue: value,
            ...springConfig
        });
    }
    const setOpacity = (value: number) => {
        return Animated.timing(animatedLayoutOpacity.current, {
            useNativeDriver: true,
            easing: Easing.linear,
            duration: 100,
            toValue: value
        });
    }

    const opacityStyle = {
        opacity: animatedOpenValue.current.interpolate({inputRange: [0, 1], outputRange: [0, target.opacity]})
    }
    const openStyle = [styles.open, {
        left: animatedOpenValue.current.interpolate({
            inputRange: [0, 1],
            outputRange: [origin.x || 0, target.x || 0],
        }),
        top: animatedOpenValue.current.interpolate({
            inputRange: [0, 1],
            outputRange: [origin.y || 0, target.y || 0],
        }),
        width: animatedOpenValue.current.interpolate({
            inputRange: [0, 1],
            outputRange: [origin.width || 0, WINDOW_WIDTH],
        }),
        height: animatedOpenValue.current.interpolate({
            inputRange: [0, 1],
            outputRange: [origin.height || 0, WINDOW_HEIGHT],
        })
    }];

    return <React.Fragment>
        <TouchableWithoutFeedback onPress={open}>
            <View ref={rootRef} style={imageStyle}>
                <Animated.Image
                    style={[props.style, imageStyle, {opacity: animatedLayoutOpacity.current}]}
                    resizeMode="contain"
                    source={{uri: props.uri}}
                />
            </View>
        </TouchableWithoutFeedback>
        <Modal visible={isOpen} transparent={true} onRequestClose={close}>
            <Animated.View style={[styles.background, opacityStyle]}/>
            <Animated.View style={[openStyle]}>
                <Image
                    source={{uri: props.uri}}
                    resizeMode="contain"
                    style={{flex: 1}}
                />
            </Animated.View>
            <Animated.View style={[styles.header, opacityStyle]}>
                <TouchableOpacity onPress={close}>
                    <Text style={styles.closeButton}>×</Text>
                </TouchableOpacity>
            </Animated.View>
        </Modal>
    </React.Fragment>;
};

export default ImageLightBox;
import React from "react";
import {SafeAreaView, ScrollView, View} from "react-native";
import ImageLightBox from "../../components/ImageLightBox";


const ReactNativeLightBox = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView>
        <ScrollView style={{paddingTop: 100}}>
            <View style={{flexDirection: "row", justifyContent: "space-between"}}>
                <ImageLightBox
                    uri="https://xxx.jpeg"
                    style={{height: 40}}
                />
                <ImageLightBox
                    uri="https://xxx.jpeg"
                    style={{height: 100}}
                />
                <ImageLightBox
                    uri="https://xxx.jpeg"
                    style={{height: 200}}
                />
            </View>
        </ScrollView>
    </SafeAreaView>;
};

export default ReactNativeLightBox;

IMG_9140.GIF

react-native-action-button浮动折叠菜单

import React from "react";
import {SafeAreaView, ScrollView, StyleSheet} from "react-native";
import ActionButton from 'react-native-action-button';
import Icon from 'react-native-vector-icons/Ionicons';


const ReactNativeActionButton = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView style={{flex: 1, backgroundColor: '#f3f3f3'}}>
        <ScrollView>

        </ScrollView>
        <ActionButton buttonColor="rgba(231,76,60,1)">
            <ActionButton.Item buttonColor='#9b59b6' title="新任务" onPress={() => {}}>
                <Icon name="md-create" style={styles.actionButtonIcon} />
            </ActionButton.Item>
            <ActionButton.Item buttonColor='#3498db' title="新消息" onPress={() => {}}>
                <Icon name="md-notifications-off" style={styles.actionButtonIcon} />
            </ActionButton.Item>
            <ActionButton.Item buttonColor='#1abc9c' title="新问题" onPress={() => {}}>
                <Icon name="md-done-all" style={styles.actionButtonIcon} />
            </ActionButton.Item>
        </ActionButton>
    </SafeAreaView>;
};

const styles = StyleSheet.create({
    actionButtonIcon: {
        fontSize: 20,
        height: 22,
        color: 'white',
    },
});

export default ReactNativeActionButton;

593255E0-219F-4BF8-9B17-C9BC98C8BFCD-12038-0000492FA3F7E3BB.GIF

react-native-masonry砖块格子类似小红书

这个库的里的ListView不能用了,需要替换成deprecated-react-native-listview,顺便把查看图片也加了进去,建议用最新的FlatList去重写

import React from "react";
import {SafeAreaView} from "react-native";
import Masonry from "../../components/Masonry";

const ReactNativeMasonry = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView style={{flex: 1}}>
        <Masonry
            columns={2}
            bricks={[
                {uri: 'https://xxx'},
                {uri: 'https://xxx'},
                {uri: 'https://xxx'},
                {uri: 'https://xxx'},
                {uri: 'https://xxx'},
                {uri: 'https://xxx'},
                {uri: 'https://xxx'},
                {uri: 'https://xxx'},
                {uri: 'https://xxx'},
            ]}
        />
    </SafeAreaView>;
};

export default ReactNativeMasonry;

7E4E2CEA-7CFA-4C64-9681-C88F564803DE-33154-00004995FD1BC83F.GIF

react-native-looped-carousel反复的轮播(图片)

react-native-swiperreact-native-carousel差不多。
图片浏览的轮播用react-native-image-carousel可以放大缩小
react-native-fading-slides轮播幻灯片没什么用

import React, {FC} from "react";
import {View, Image} from "react-native";
import Carousel from 'react-native-looped-carousel';

interface Props {
    images?: { uri: string }[];
    currentPage?: number;
    onAnimateNextPage?: (page: number) => void;
}

const Images: FC<Props> = (props) => {
    const [size, setSize] = React.useState<{ width?: number, height?: number }>({});
    React.useEffect(() => {

    }, []);
    return <View style={{width: '100%', height: '100%'}} onLayout={(e) => {
        const layout = e['nativeEvent'].layout;
        setSize({width: layout.width, height: layout.height});
    }}>
        <Carousel
            currentPage={props.currentPage}
            pageInfo={true}
            style={{...size}}
            autoplay={false}
            pageInfoBackgroundColor="rgba(255,255,255,0.5)"
            onAnimateNextPage={props.onAnimateNextPage}>
            {props.images!.map((i, index) => {
                return <View style={[size]} key={index}>
                    <Image
                        source={{uri: i.uri}}
                        resizeMode="contain"
                        style={{flex: 1, }}
                    />
                </View>
            })}
        </Carousel>
    </View>;
};

Images.defaultProps = {
    images: [],
    currentPage: 0,
}
export default Images;

7D6936A9-FEBB-4EDB-B032-B5294667EBC5-42276-00004BFA9E68428D.GIF

react-native-svg-charts图表

import React from "react";
import {SafeAreaView, ScrollView, View} from "react-native";
import {
    AreaChart,
    Grid,
    StackedAreaChart,
    BarChart,
    LineChart,
    PieChart,
    ProgressCircle,
    YAxis,
    XAxis
} from 'react-native-svg-charts';
import * as shape from 'd3-shape'

const ReactNativeSvgCharts = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView style={{flex: 1}}>
        <ScrollView>
            <AreaChart
                style={{height: 200}}
                data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                contentInset={{top: 30, bottom: 30}}
                curve={shape.curveNatural}
                svg={{fill: 'rgba(134, 65, 244, 0.8)'}}
            >
                <Grid/>
            </AreaChart>
            <StackedAreaChart
                style={{height: 200, paddingVertical: 16}}
                data={[
                    {
                        month: new Date(2015, 0, 1),
                        apples: 3840,
                        bananas: 1920,
                        cherries: 960,
                        dates: 400,
                    },
                    {
                        month: new Date(2015, 1, 1),
                        apples: 1600,
                        bananas: 1440,
                        cherries: 960,
                        dates: 400,
                    },
                    {
                        month: new Date(2015, 2, 1),
                        apples: 640,
                        bananas: 960,
                        cherries: 3640,
                        dates: 400,
                    },
                    {
                        month: new Date(2015, 3, 1),
                        apples: 3320,
                        bananas: 480,
                        cherries: 640,
                        dates: 400,
                    },
                ]}
                keys={['apples', 'bananas', 'cherries', 'dates']}
                colors={['#8800cc', '#aa00ff', '#cc66ff', '#eeccff']}
                curve={shape.curveNatural}
                showGrid={false}
                svgs={[
                    {onPress: () => console.log('apples')},
                    {onPress: () => console.log('bananas')},
                    {onPress: () => console.log('cherries')},
                    {onPress: () => console.log('dates')},
                ]}
            />
            <BarChart
                style={{height: 200}}
                data={[50, 10, 40, 95, -4, -24, null, 85, undefined, 0, 35, 53, -53, 24, 50, -20, -80]}
                svg={{fill: 'rgb(134, 65, 244)'}}
                contentInset={{top: 30, bottom: 30}}
            >
                <Grid/>
            </BarChart>

            <LineChart
                style={{height: 200}}
                data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                svg={{stroke: 'rgb(134, 65, 244)'}}
                contentInset={{top: 20, bottom: 20}}
            >
                <Grid/>
            </LineChart>

            <PieChart
                style={{height: 200}}
                data={
                    [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80].filter((value) => value > 0)
                        .map((value, index) => ({
                            value,
                            svg: {
                                fill: ('#' + ((Math.random() * 0xffffff) << 0).toString(16) + '000000').slice(0, 7),
                                onPress: () => console.log('press', index),
                            },
                            key: `pie-${index}`,
                        }))
                }
            />

            <ProgressCircle
                style={{height: 200}}
                progress={0.7}
                progressColor={'rgb(134, 65, 244)'}
            />

            <View style={{height: 200, flexDirection: 'row'}}>
                <YAxis
                    data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                    contentInset={{top: 20, bottom: 20}}
                    svg={{
                        fill: 'grey',
                        fontSize: 10,
                    }}
                    numberOfTicks={10}
                    formatLabel={(value) => `${value}ºC`}
                />
                <LineChart
                    style={{flex: 1, marginLeft: 16}}
                    data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                    svg={{stroke: 'rgb(134, 65, 244)'}}
                    contentInset={{top: 20, bottom: 20}}
                >
                    <Grid/>
                </LineChart>
            </View>

            <View style={{height: 200, padding: 20}}>
                <LineChart
                    style={{flex: 1}}
                    data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                    gridMin={0}
                    contentInset={{top: 10, bottom: 10}}
                    svg={{stroke: 'rgb(134, 65, 244)'}}
                >
                    <Grid/>
                </LineChart>
                <XAxis
                    style={{marginHorizontal: -10}}
                    data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                    formatLabel={(value, index) => index}
                    contentInset={{left: 10, right: 10}}
                    svg={{fontSize: 10, fill: 'black'}}
                />
            </View>
        </ScrollView>
    </SafeAreaView>;
};

export default ReactNativeSvgCharts;

C6DEEC18-4E4E-47F0-84B7-3277296EAACB-42276-00004CE9927F21F8.GIF

react-native-progress-bar进度条

import React, {FC} from "react";
import {StyleSheet, Animated, Easing, View, ViewStyle} from "react-native";


interface Props {
    initialProgress?: number;
    progress: number;
    easing?: (value: number) => number;
    easingDuration?: number;
    backgroundStyle?: ViewStyle;
    style?: ViewStyle;
    fillStyle?: ViewStyle;
}

const ProgressBar: FC<Props> = (props) => {
    const progressRef = React.useRef(new Animated.Value(props.initialProgress || 0));

    React.useEffect(() => {
        update();
    }, [props.progress]);

    const update = () => {
        Animated.timing(progressRef.current, {
            useNativeDriver: false,
            easing: props.easing!,
            duration: props.easingDuration!,
            toValue: props.progress
        }).start();
    };


    React.useEffect(() => {

    }, []);

    return <View style={[styles.background, props.backgroundStyle, props.style]}>
        <Animated.View style={[styles.fill, props.fillStyle, {
            width: progressRef.current.interpolate({
                inputRange: [0, 1],
                outputRange: [0, 1 * (props.style!.width || 0)],
            })
        }]}/>
    </View>;
};

const styles = StyleSheet.create({
    background: {
        backgroundColor: '#bbbbbb',
        height: 5,
        overflow: 'hidden'
    },
    fill: {
        backgroundColor: '#3b5998',
        height: 5
    }
});

ProgressBar.defaultProps = {
    initialProgress: 0,
    easingDuration: 500,
    easing: Easing.inOut(Easing.ease),
    backgroundStyle: {},
    style: {},
    fillStyle: {}
}

export default ProgressBar;
<ProgressBar
    fillStyle={{}}
    backgroundStyle={{backgroundColor: '#cccccc', borderRadius: 2}}
    style={{marginTop: 10, width: 300}}
    progress={progress}
/>

957E4EAA-3FAF-4030-B415-68DFF9DED649-72122-00004E1A6BF907FF.GIF

react-native-gesture-password手势密码

import React from "react";
import {SafeAreaView, ScrollView} from "react-native";
import GesturePassword from "react-native-gesture-password";


const ReactNativeGesturePassword = () => {
    const [status, setStatus] = React.useState('normal');
    const [message, setMessage] = React.useState('请输入密码');
    const refPassword = React.useRef<GesturePassword>();

    React.useEffect(() => {

    }, []);

    const onStart = () => {

    }

    const onEnd = (password: string) => {
        refPassword.current!.resetActive();
    }
    return <SafeAreaView>
        <GesturePassword
            ref={(e) => refPassword.current = e}
            status={status}
            message={message}
            onStart={() => onStart()}
            onEnd={(password) => onEnd(password)}
            innerCircle={true}
            outerCircle={true}
        />
    </SafeAreaView>;
};

export default ReactNativeGesturePassword;

github

持续更新...
IMG_9163.JPG


perkz
51 声望28 粉丝