Android开发经验谈RN

React-Native 版高仿淘宝、京东商城首页、商品分类页面

2020-10-10  本文已影响0人  飞鱼_9d08

正在做商城项目的同学有福啦,看看是你们想要的效果吗?

项目地址:https://github.com/pengzhenjin/react-native-mall

效果图

已实现功能

沉浸式状态栏
酷炫的顶部导航动画
消息角标
循环轮播图
搜索
商品一级分类
商品二级分类
商品子分类
顶部滑动的tab、智能下拉菜单
用到的技术
自定义 Badge(角标)
动画、动画插值器
Swiper 轮播图
自定义 tab
自定义 popup 弹窗下拉菜单
FlatList、SectionList
重点代码解析

MallHome.js

/**
     * 滚动条监听事件
     * @param event
     */
    onScrollFunc = (event) => {
        // 将滚动的值绑定到渐变动画
        Animated.event([{nativeEvent: {contentOffset: {y: this.state.logoOpacity}}}])(event)
 
        // 将滚动的值绑定到边距动画
        Animated.event([{nativeEvent: {contentOffset: {y: this.state.searchViewMargin}}}])(event)
    }
renderSearchView = () => {
        const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity)
 
        const marginRight = this.state.searchViewMargin.interpolate({
            inputRange: [0, 80], // 当滚动条滚动到0~80的位置时
            outputRange: [0, 80], // 将右边距改为从0~80
            extrapolate: 'clamp' // 滚动超出0~80的范围,不在更改边距
        })
        const marginTop = this.state.searchViewMargin.interpolate({
            inputRange: [0, 160], // 当滚动条滚动到0~160的位置时
            outputRange: [0, -36], // 将上边距改为从0~-36
            extrapolate: 'clamp' // 滚动超出0~160的范围,不在更改边距
        })
 
        return (
            <AnimatedTouchableOpacity
                style={styles.top_search_container(marginRight, marginTop)}
                activeOpacity={0.8}
                onPress={this.gotoSearch}
            >
                <Image style={styles.top_search_icon} source={iconSearch} />
                <Text style={styles.top_search_text}>{'新品'}</Text>
            </AnimatedTouchableOpacity>
        )
    }

GoodsCategory.js 解析

render() {
        return (
            <View style={styles.container}>
                <View style={styles.first_category_container}>
            // 一级分类列表,采用FlatList
                    <FlatList
                        ref={refs => this.flatList = refs}
                        keyExtractor={(item, index) => index.toString()}
                        data={this.state.firstCategoryData}
                        renderItem={this.renderFirstCategoryItem}
                        ItemSeparatorComponent={this.renderSeparatorLine}
                    />
                </View>
                <View style={styles.second_category_container}>
            // 二级、三级分类列表,采用SectionList
                    <SectionList
                        ref={refs => this.sectionList = refs}
                        renderSectionHeader={this.renderSecondCategorySectionHeader}
                        renderItem={this.renderSecondCategoryItem}
                        sections={this.state.secondCategoryData}
                        ItemSeparatorComponent={null}
                        ListHeaderComponent={null}
                        ListFooterComponent={null}
                        keyExtractor={(item, index) => index + item}
                    />
                </View>
            </View>
        );
    }
    // 点击一级分类时,需要计算滚动条的位置
    onClickFirstCategoryItem = (item, index) => {
        this.setState({selectedFirstCategoryIndex: index});
 
        // 计算当前 item 的高度
        const indexHeight = firstCategoryItemHeight * index;
 
        // 计算屏幕一半的高度
        const halfHeight = (height - 65) / 2;
 
        // 如果当前 item 的高度 大于 屏幕一半的高度,就让滚动条滚动 indexHeight - halfHeight 高度(类似京东商品分类效果)
        if (indexHeight > halfHeight) {
            this.flatList.scrollToOffset({
                animated: true,
                offset: indexHeight - halfHeight,
            });
        }
 
        this.sectionList.scrollToLocation({
            animated: true,
            itemIndex: 0,
            sectionIndex: 0,
        });
    };

TopTabView.js 解析

    /**
     * 显示下拉菜单
     * @param index 当前选中时的 tab 下标
     */
    showDropdownMenu = (index) => {
        // measure方法测量"箭头图标"在页面中的位置、宽高
        this.arrowIcon.measure((x, y, width, height, pageX, pageY) => {
            const topOffset = pageY + height // 计算"下拉菜单"距离页面顶部的偏移量
            this.dropdownMenu.show(topOffset, index) // 显示"下拉菜单"
        })
    }

组件的 measure((x, y, width, height, pageX, pageY) => {}) 方法可以动态的获取组件在屏幕中的位置、宽高信息。measure 方法的参数 x,y 表示组件的相对位置,width,height 表示组件的宽度和高度,pageX,pageY 表示组件相对于屏幕的绝对位置。

TopDropdownMenu.js 解析

// 使用 Modal 来实现弹窗菜单,达到遮罩效果
 render() {
        const {isVisible, data, topOffset} = this.state;
        return (
            <Modal
                animationType="fade"
                transparent={true}
                onRequestClose={() => this.hide()}
                visible={isVisible}
            >
                <TouchableOpacity
                    style={styles.container}
                    activeOpacity={1}
                    onPress={() => this.hide()}
                >
                    <FlatList
                        style={[styles.content_container, {top: topOffset}]}
                        keyExtractor={(item, index) => index.toString()}
                        data={data}
                        renderItem={this.renderItem}
                        horizontal={false}
                        numColumns={2}
                        renderSeparator={null}
                    />
                </TouchableOpacity>
            </Modal>
        );
    }

    // 使用绝对布局和 top 来计算弹窗菜单的位置,其中 top 是动态计算的
    content_container: {
        position: 'absolute',
        top: 0,
        backgroundColor: '#FFFFFF',
    },

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可
Links: https://www.pengzhenjin.top/archives/react-native版高仿淘宝京东商城首页商品分类页面

最后

本文在开源项目:https://github.com/xieyuliang/Note-Android中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中...

上一篇下一篇

猜你喜欢

热点阅读