1. 程式人生 > >React Native仿某電商店鋪首頁

React Native仿某電商店鋪首頁

前言:有朋友需要實現一個仿某電商app店鋪首頁的效果,花了一點點時間簡單的實現了一下,最後的效果大概是這樣的:

這裡寫圖片描述

1、可以看到,當底部的滑動控制元件滑動的時候tabview滑動到指定的位置後懸浮。
2、當底部的滑動控制元件滑動的時候狀態列view透明度從0到1的轉變。
3、當底部的滑動控制元件滑動的時候背景view需要根據滑動的距離縮放最後跟狀態列view重合。

當然,需求遠不止這些,我只是簡單的實現了一下功能,覺得有必要把自己實現的東西記錄下來,就當筆記了額~ 大牛勿噴。

思路:

1、底部滑動元件滑動的時候回撥,通知headerview(縮放)跟statusview(透明度)重新整理,也就是說headerview跟scrollview在一個view中,一上一下,然後scrollview滑動的時候改變headerview的高度,這時scrollview的高度也在不斷的增加,當達到指定的位置後scrollview固定不動。

2、scrollview的高度一直固定不動,絕對定位到狀態列view下面,tabview相對定位到headerview底部,scrollview滑動的時候回撥,tabview跟隨著scrollview滾動,(其實scrollview高度一直固定不變的,但是scrollview的內容模組給了一個初始marintop值,使內容模組恰好在tabview的下面,雖然加了一個預設的marintop,但是當scrollview滾動的時候,tabview是隨著scrollview滾動的,所以預設的margintop是看不出來的)

有人說了, 第一種方式soeasy就實現了,幹嘛還用第二種方案呢? 哈哈~~ 我也想簡單的就實現了,但是其中遇到的問題就是,headerview跟scrollview是擺在一個view中的,headerview的高度固定,scrollview的高度為flex:1,所以headerview高度變化也意味著scrollview的高度在變,但是在android中,如果scrollview正在滑動並且高度也在改變的時候,會抖動。。。 很抖!!! 唯一不抖動的方式就是不要改變scrollview的高度,所以才會有了方案二,方案二中我一直在強調不改變scrollview的高度。

哈哈~~ 說多了小夥伴應該累了,我們直接上程式碼了哈,第一種方式我就不掩飾了,ios很好,android很抖。我就直接上第二種方式了:

demo中用到了ScrollableTabView這個第三方控制元件,我就不解釋用法了,小夥伴自己去查,很多用法的文章。

首先我們建立一個叫StoreDetails的組建:

render方法

 render() {
        return (
            <View style={styles.container}>
                <StatusBar
                    translucent={true}
barStyle={'light-content'} backgroundColor={'transparent'}/> {/*渲染內容view*/} {this._renderContent()} {/*渲染headerview*/} {this._renderHeader()} {/*渲染頂部搜尋view*/} {this._renderTopBar()} </View> ); }

頭部背景view是懸浮在頭部的,style樣式為:

headerContainer: {
        width: ScreenUtils.screenW,
        height: ScreenUtils.scaleSize(273),
        justifyContent: 'flex-end',
        position: 'absolute',
        top: 0,
    },
/**
     * 渲染頭部
     * @private
     */
    _renderHeader() {
        let icon = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496299246419&di=f6d9e7d99236cb4319782d95cbd7f740&imgtype=0&src=http%3A%2F%2Fwww.pptbz.com%2FSoft%2FUploadSoft%2F200911%2F2009110521380826.jpg';
        let icon2 = 'http://pic28.nipic.com/20130503/9252150_153601831000_2.jpg';
        return (
            <Image
                ref={(ref)=>this.topView = ref}
                style={styles.headerContainer}
                source={{uri: icon2}}>
                <View style={styles.headerBottomStyle}>
                    <Image
                        style={styles.storeIcon}
                        source={{uri: icon}}
                    />
                    <View style={styles.headerDescStyle}>
                        <Text allowFontScaling={false} style={styles.storeTitleStyle}>蘋果旗艦店</Text>
                        <Text allowFontScaling={false} style={styles.storeDescStyle}>此處是促銷資訊</Text>
                    </View>
                    <View style={styles.headerObseverStyle}>
                        <Text allowFontScaling={false} style={styles.obseverDeStyle}>已有2000人關注</Text>
                    </View>
                </View>
            </Image>
        );
    }

頂部搜尋view也是懸浮在view中的,樣式為:

  headerTopContainer: {
        flexDirection: 'row',
        alignItems: 'center',
        position: 'absolute',
        paddingTop: getStatusHeight(),
        paddingBottom: ScreenUtils.scaleSize(20),
        left: 0,
        right: 0,
    },
 /**
     * 渲染topbar
     * @private
     */
    _renderTopBar() {
        return (
            <View
                style={styles.headerTopContainer}
                ref={(ref)=>this.statusView = ref}
                onLayout={(event)=> {
                    let height = event.nativeEvent.layout.height;
                    if (this.statusHeight !== height) {
                        this.statusHeight = height;
                        this.contentView.setNativeProps({
                            style: {
                                top: (height),
                                backgroundColor: 'red'
                            }
                        })
                    }
                }}
            >
                <TouchableOpacity style={styles.headerBackStyle} onPress={()=> {
                    Actions.pop();
                }} activeOpacity={1}>
                    <Image
                        style={styles.leftArrowDefaultStyle}
                        source={require('../../../../../foundation/Img/icons/Icon_back_.png')}
                    />
                </TouchableOpacity>
                <View style={styles.headerCenterContainer}>
                    <Image
                        style={styles.headerQueryStyle}
                        source={require('../../../../../foundation/Img/icons/question.png')}
                    />
                    <TextInput
                        style={styles.queryTextStyle}
                        selectionColor={Colors.text_dark_grey}
                        underlineColorAndroid={'transparent'}
                        placeholder={'搜尋'}
                        placeholderTextColor={Colors.text_light_grey}
                        onFocus={this._jumpToSearch}
                    />
                </View>
                <View style={styles.headerRightContainer}>
                    <TouchableOpacity onPress={this._jumpToCart} activeOpacity={1}>
                        <Image
                            style={styles.navRightCartStyle}
                            source={require('../../../../../foundation/Img/home/store/Icon_cart_.png')}
                        />
                    </TouchableOpacity>
                    <TouchableOpacity activeOpacity={1}>
                        <Image
                            style={styles.navRightShareStyle}
                            source={require('../../../../../foundation/Img/home/store/Icon_more_.png')}
                        />
                    </TouchableOpacity>
                </View>
            </View>
        );
    }

最後是主體內容模組了,也是懸浮在view中的,樣式為:

 contentStyle: {
        width: ScreenUtils.screenW,
        bottom: 0,
        position: 'absolute',
    },

畫一個這樣的頁面想必小夥伴很easy的就實現了哈~

當搜尋欄view渲染完畢後,我們需要拿到搜尋欄view的高度,然後設定內容view的top值:


    /**
     * 渲染topbar
     * @private
     */
    _renderTopBar() {
        return (
            <View
                style={styles.headerTopContainer}
                ref={(ref)=>this.statusView = ref}
                onLayout={(event)=> {
                //獲取頂部搜尋view的高度
                    let height = event.nativeEvent.layout.height;
                    if (this.statusHeight !== height) {
                        this.statusHeight = height;
       //設定內容view的top值                 this.contentView.setNativeProps({
                            style: {
                                top: (height),
                                backgroundColor: 'red'
                            }
                        })
                    }
                }}
            >
            .......

可以看到我們的內容view渲染的時候預設加了一個margintop:

//77為搜尋欄view的高度,我這就直接寫死了,這個值是需要動態傳入的
 <View style={{ marginTop: topHeight - 77}}>
                                    <Text>{title+'1'}</Text>
                     .....
                                </View>

然後我們的tabview也是懸浮在view中,預設一個top值:

//77為搜尋欄view的高度,我這就直接寫死了,這個值是需要動態傳入的,還給了一個zindex,就是為了浮在ScrollableTabView這個第三方控制元件中。
   renderTabBar={(props) =>
                        <View
                            ref={(ref)=>this.tabViews = ref}
                            style={{position: 'relative', top: topHeight - 77, zIndex: 10}}
                        >

                            <DefaultTabBar
                                {...props}
                            />
                        </View>
                    }

最後出現的效果應該是這樣的:

這裡寫圖片描述

這個時候scrollview的滑動是沒有任何效果的,因為我們都還沒進行處理哈~

好啦! 接下來我們就來處理一下scrollview的滑動事件:

  <ScrollView
                                style={{flex: 1,backgroundColor:'green'}}
                                key={index}
                                tabLabel={title}
                                scrollEventThrottle={1}
                                onScroll={this._onScroll.bind(this)}//滑動回撥
                                overScrollMode={'never'}
                            >
/**
     *
     * @private
     */
    _onScroll(e) {
        //獲取滑動個的y軸偏移量
        let y = e.nativeEvent.contentOffset.y;
        //y軸滑動到頂部的臨界值,此時需要懸浮
        let limit = topHeight - this.statusHeight;
        //當y軸滑動的距離<=臨界值的時候,headerview的高度=(headerview的初始高度-滑動的高度)
        if (y <= limit) {
            //headerview的高度=(headerview的初始高度-滑動的高度)
            this.topView.setNativeProps({
                style: {
                    height: topHeight - y,
                }
            });
            //tabview的top值需要隨著scrollview的滑動改變(headerview的初始高度-搜尋欄的高度-scrollview的滑動高度)
            this.tabViews.setNativeProps({
                style: {
                    top:(topHeight-this.statusHeight-y)
                }
            });
            //當y軸滑動的距離>臨界值的時候,headerview的高度=(搜尋欄的高度)    
        } else {
            //tabview的top為0
            this.tabViews.setNativeProps({
                style: {
                    top:0
                }
            });
            //headerview的高度=(搜尋欄的高度) 
            this.topView.setNativeProps({
                style: {
                    height: this.statusHeight,
                }
            });
        }
        //通過(y/limit)(scrollview滑動的y值/臨界值)算出一個offset,給搜尋欄view設定背景色,
        //背景色是rgba(0,0,0,offset),
        this.statusView.setNativeProps({
            style: {
                backgroundColor: `rgba(0,0,0,${y / limit})`,
            }
        })

    }

ios跟android的效果差不多,只是android沒有bounce的效果,哈哈~ 看了一下某東也是沒有的,心理平衡了~ 個人覺得實現起來思路還是有點問題了,不過也算是可以簡單搞定產品了。 各位小夥伴有啥好的思路可以告知一下哈~ 歡迎交流!! 歡迎入群!!!

這裡寫圖片描述