React Native實(shí)現(xiàn)京東下拉菜單

2019-8-3 更新成了TS版本的

我將項(xiàng)目升級成TS版本了,可以看這里

https://github.com/Tzng/React-Component/tree/master/react-native/ActionsBarTs

具體的用法是這樣的:

<ActionBarItem
       bannerAction={flag => this.changeBar(flag, 'aaa')}
       isActive={activeBar === 'aaa'}
       data={deplist}
       themeColor={theme.themeColor}
       handleSubmit={(data, type) => this.onSelectSubmit(data, 'aaa', type)}
/>

注意下層級,如果是多個的話,這個要放在上面哦

首先看看效果

示例.gif

基本思路

看動圖我們可以發(fā)現(xiàn),組件是由三部分組成的,一個是標(biāo)題部分,也就是用來點(diǎn)擊的地方,一個是彈出來的那一塊,還以及一個遮罩層。

標(biāo)題部分

這部分是很簡單,就是一個文本和一個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'
                }
            ]}
        >
            {/* 結(jié)果的展示 */}
            {selectItems.length === 0 ? data.title : this.split(itemNames) + '(' + selectItems.length + ')'}
        </Text>
    </View>
</ZlTouchable>

在使用TouchableOpacity的時候,需要考慮到一個情況就是,用戶可能會快速的點(diǎn)擊,從而出現(xiàn)一些異?,F(xiàn)象,所以要對點(diǎn)擊事件進(jìn)行點(diǎn)擊間隔限制操作,所以這里是用的一個自己寫的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的時間可以延長,根據(jù)需要改變
            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;

這樣的話,我們就可以解決用戶快速點(diǎn)擊的問題了。

彈出菜單

這里的彈出菜單,我是這么想的,拿到數(shù)據(jù)后呢,先把菜單生成出來,放在標(biāo)題部分的上面,高度話,用父組件傳遞進(jìn)來。這樣的話,就得這么寫了

    /**
     * 說明:生成下拉菜單
     * @author tangbin
     * @date 2019/3/29
     * @time 11:04
     */
    renderActivityPanel = () => {

        // 得到數(shù)據(jù)
        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>
        );
    };

把我們的選項(xiàng)用ScrollView包裹起來,這樣就能滾動了,然后,用動畫組件把整改菜單給包裹進(jìn)去,并且在動畫組件設(shè)置相應(yīng)的樣式,比如top要設(shè)置成負(fù)數(shù),zIndex也要設(shè)置對應(yīng)的等級,然后我們就可設(shè)置相應(yīng)的動畫了

動畫

首先,我們要設(shè)置點(diǎn)擊菜單的動畫

    openPanel = () => {
        const { rotationAnim, fadeAnim } = this.state;
        this.isShowCouver = true;
        rotationAnim.setValue(0);
        Animated.parallel([
            // 使用寬松函數(shù)讓數(shù)值隨時間動起來。
            Animated.spring( // 隨時間變化而執(zhí)行動畫
                rotationAnim, // 動畫中的變量值
                {
                    toValue: 1, // 透明度最終變?yōu)?,即完全不透明
                    duration: 300, // 讓動畫持續(xù)一段時間
                    useNativeDriver: true // <-- 加上這一行
                }
            ),
            // 使用寬松函數(shù)讓數(shù)值隨時間動起來。
            Animated.spring( // 隨時間變化而執(zhí)行動畫
                fadeAnim, // 動畫中的變量值
                {
                    toValue: 0.5, // 透明度最終變?yōu)?,即完全不透明
                    duration: 300, // 讓動畫持續(xù)一段時間
                    useNativeDriver: true // <-- 加上這一行
                }
            )
        ]).start();
    };

然后呢,設(shè)置關(guān)閉的動畫

    // 關(guān)閉動畫
    closePanel = () => {
        const { rotationAnim } = this.state;
        this.isShowCouver = false;
        rotationAnim.setValue(1);
        // 這里只執(zhí)行彈窗的動畫,是因?yàn)檎谡謱咏M件在切換的時候,已經(jīng)被卸載了
        Animated.spring(
            rotationAnim,
            {
                toValue: 0,
                duration: 300,
                useNativeDriver: true // <-- 加上這一行
            }
        ).start();
    };

關(guān)閉動畫要把打開的動畫少一點(diǎn),因?yàn)檎谡謱釉陉P(guān)閉的時候,已經(jīng)沒有了,所以是不需要的。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,414評論 4 61
  • 文|阿魚魚_Ayuyu 37 誤會 我愣了一下,忽然想到——剛才拿著刀從扶蘇帳中沖出來,毫不猶豫追到密林,又毫不猶...
    劉瑜_Demi閱讀 340評論 0 0
  • 20180429—20180506 本周八天 以后按周一到周日做檢視 1686踐行第五周檢視 【一,愛非堅(jiān)持?...
    雨黎兒0730閱讀 268評論 0 0
  • 姓名:張獻(xiàn)忠 日精進(jìn)打卡第360天 【打卡始于2016.11.01持續(xù)于2017.10.26】 【知~學(xué)...
    張獻(xiàn)忠閱讀 203評論 0 0
  • 我喜歡世界上的各種美食,但是我對面條并不十分感興趣,不過在特別的情況下也有例外。 就在前天,和同事閑談時,我說就在...
    道形圖閱讀 677評論 2 7

友情鏈接更多精彩內(nèi)容