React Native仿某电商店铺首页

本文介绍了一种仿照电商App首页效果的实现方法,包括头部背景视图的缩放、状态栏透明度变化及Tab栏随滚动显示等功能。通过调整布局和监听滚动事件,确保在iOS和Android平台上的良好体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:有朋友需要实现一个仿某电商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的效果,哈哈~ 看了一下某东也是没有的,心理平衡了~ 个人觉得实现起来思路还是有点问题了,不过也算是可以简单搞定产品了。 各位小伙伴有啥好的思路可以告知一下哈~ 欢迎交流!! 欢迎入群!!!

这里写图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值