美文网首页RN
React Native实现京东下拉菜单

React Native实现京东下拉菜单

作者: 阿拉斌 | 来源:发表于2019-04-06 23:15 被阅读145次

    首先看看效果

    示例.gif

    基本思路

    看动图我们可以发现,组件是由三部分组成的,一个是标题部分,也就是用来点击的地方,一个是弹出来的那一块,还以及一个遮罩层。

    标题部分

    这部分是很简单,就是一个文本和一个Icon

    <ZlTouchable
        onPress={this.openOrClosePanel}
        style={{
            flex: 1,
            height: 40,
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: themeColor
        }}
    >
        <View
            style={{
                flexDirection: 'row',
                alignItems: 'center',
                justifyContent: 'center',
            }}
        >
            <Text
                style={[
                    styles.title_style,
                    {
                        color: '#fff'
                    }
                ]}
            >
                {/* 结果的展示 */}
                {selectItems.length === 0 ? data.title : this.split(itemNames) + '(' + selectItems.length + ')'}
            </Text>
        </View>
    </ZlTouchable>
    

    在使用TouchableOpacity的时候,需要考虑到一个情况就是,用户可能会快速的点击,从而出现一些异常现象,所以要对点击事件进行点击间隔限制操作,所以这里是用的一个自己写的ZlTouchable组件来完成的

    代码:

    import React from 'react';
    import { TouchableOpacity } from 'react-native';
    
    class ZlTouchable extends React.Component<any> {
        constructor(props) {
            super(props);
            this.lastClickTime = 0;
        }
    
        onPress() {
            const { onPress } = this.props;
            const clickTime = Date.now();
            if (!this.lastClickTime || Math.abs(this.lastClickTime - clickTime) > 300) { // 350的时间可以延长,根据需要改变
                this.lastClickTime = clickTime;
                if (onPress) {
                    onPress();
                } else {
                    return '';
                }
            }
            return '';
        }
    
        render() {
    
            const { activeOpacity, style, disabled, children } = this.props;
    
            return (
                <TouchableOpacity
                    onPress={() => this.onPress()}
                    activeOpacity={activeOpacity || 0.85}
                    style={style}
                    disabled={disabled}
                >
                    {children}
                </TouchableOpacity>);
        }
    }
    
    export default ZlTouchable;
    

    这样的话,我们就可以解决用户快速点击的问题了。

    弹出菜单

    这里的弹出菜单,我是这么想的,拿到数据后呢,先把菜单生成出来,放在标题部分的上面,高度话,用父组件传递进来。这样的话,就得这么写了

        /**
         * 说明:生成下拉菜单
         * @author tangbin
         * @date 2019/3/29
         * @time 11:04
         */
        renderActivityPanel = () => {
    
            // 得到数据
            const { data: { items } } = this.props;
    
            // 得到最大高度
            const { maxHeight, themeColor } = this.props;
    
            const { rotationAnim } = this.state;
    
            return (
                <View
                    style={{
                        position: 'absolute',
                        left: 0,
                        right: 0,
                        top: 38, // 决定了弹窗距离顶部的距离
                        bottom: 0
                    }}
                >
                    <Animated.View
                        style={{
                            position: 'absolute',
                            left: 0,
                            right: 0,
                            top: -(maxHeight + 50),
                            width,
                            zIndex: 20,
                            transform: [
                                { translateY: rotationAnim.interpolate({
                                    inputRange: [0, 1],
                                    outputRange: [0, maxHeight + 50]
                                }) },
                            ]
                        }}
                    >
                        <View style={{ height: maxHeight }}>
                            <ScrollView
                                style={{
                                    position: 'absolute',
                                    top: 0,
                                    bottom: 0,
                                    left: 0,
                                    right: 0,
                                    backgroundColor: 'white',
                                }}
                            >
                                {items.map(item => (
                                    <ZlTouchable
                                        key={item.id}
                                        style={{ flex: 1, height: 39 }}
                                        onPress={() => this.itemOnPress(item)}
                                    >
                                        {this.renderChcek(item, themeColor)}
                                    </ZlTouchable>
                                ))}
                            </ScrollView>
                        </View>
                        <View
                            style={{
                                flex: 1,
                                flexDirection: 'row',
                                justifyContent: 'space-between'
                            }}
                        >
                            <ZlTouchable style={styles.cancel_button} onPress={this.openOrClosePanel}>
                                <View style={styles.button_text_view}>
                                    <Text style={styles.cancel_button_text}>取消</Text>
                                </View>
                            </ZlTouchable>
                            <ZlTouchable style={styles.warning_button} onPress={this.handleSubmit}>
                                <View style={styles.button_text_view}>
                                    <Text style={styles.warning_button_text}>确定</Text>
                                </View>
                            </ZlTouchable>
                        </View>
                        <View style={GlobalStyles.line} />
                    </Animated.View>
                </View>
            );
        };
    

    把我们的选项用ScrollView包裹起来,这样就能滚动了,然后,用动画组件把整改菜单给包裹进去,并且在动画组件设置相应的样式,比如top要设置成负数,zIndex也要设置对应的等级,然后我们就可设置相应的动画了

    动画

    首先,我们要设置点击菜单的动画

        openPanel = () => {
            const { rotationAnim, fadeAnim } = this.state;
            this.isShowCouver = true;
            rotationAnim.setValue(0);
            Animated.parallel([
                // 使用宽松函数让数值随时间动起来。
                Animated.spring( // 随时间变化而执行动画
                    rotationAnim, // 动画中的变量值
                    {
                        toValue: 1, // 透明度最终变为1,即完全不透明
                        duration: 300, // 让动画持续一段时间
                        useNativeDriver: true // <-- 加上这一行
                    }
                ),
                // 使用宽松函数让数值随时间动起来。
                Animated.spring( // 随时间变化而执行动画
                    fadeAnim, // 动画中的变量值
                    {
                        toValue: 0.5, // 透明度最终变为1,即完全不透明
                        duration: 300, // 让动画持续一段时间
                        useNativeDriver: true // <-- 加上这一行
                    }
                )
            ]).start();
        };
    

    然后呢,设置关闭的动画

        // 关闭动画
        closePanel = () => {
            const { rotationAnim } = this.state;
            this.isShowCouver = false;
            rotationAnim.setValue(1);
            // 这里只执行弹窗的动画,是因为遮罩层组件在切换的时候,已经被卸载了
            Animated.spring(
                rotationAnim,
                {
                    toValue: 0,
                    duration: 300,
                    useNativeDriver: true // <-- 加上这一行
                }
            ).start();
        };
    

    关闭动画要把打开的动画少一点,因为遮罩层在关闭的时候,已经没有了,所以是不需要的。

    完整代码看地址:
    https://github.com/Tzng/React-Component/tree/master/react-native/ActionBar

    相关文章

      网友评论

        本文标题:React Native实现京东下拉菜单

        本文链接:https://www.haomeiwen.com/subject/knnzbqtx.html