组件效果如下
IOS | Android |
使用组件
<AreaPicker
onFinished={this.onFinished.bind(this)}
modalHeight={800}
visible={visible}
/>
复制代码
-
visible
: 类型boolean
, 控制是否显示组件 -
modalHeight
:类型number
, 设置内容窗口的高度 -
onFinished
:类型func
, 选取完整地区后回调, 返回值是一个数组[{label: 'xx', value: 'xx'}]
onFinished(list) { this.setState({ selected: (list.map(o => o.label)).join('') }) } 复制代码
实现逻辑
动画
-
使用
react-native-animatable
, 需要先安装。yarn add react-native-animatable 复制代码
-
遮罩层的淡入效果
<Animatable.View style={[styles.container, !visible && {opacity: 0, transform:[{scale: 0}]}]} ref = {ref => this.containerRef = ref}> </Animatable.View> 复制代码
初始化组件的时候,
componentWillReceiveProps
不会执行,当props
发生变化时执行,可以在这个函数里调用this.setState()
去更新组件内部属性,其中也可以通过this.props去获取到旧的属性值,完成一些对比逻辑。componentWillReceiveProps(nextProps){ const { visible } = nextProps !!this.containerRef && this.containerRef.animate('fadeIn', ANIMATE_DURATION).then(() => { // todo... }) } 复制代码
-
选项卡的滚动线条
线条元素绝对定位,宽度动态获取,移动的位置通过
translateX
设置<Animatable.View animation={{ 0: { translateX: startTranslateValue }, 1: { translateX: endTtranslateValue } }} delay={0} duration={200} easing="ease-in-out" // iterationCount="infinite" useNativeDrive style={[styles.lineStyle, {width: lineWidth}]} > </Animatable.View> 复制代码
其中
startTranslateValue
是始点,endTtranslateValue
是终点。 如果一开始从0
移动到100
,再接下来可能是从100
移动200
有一个属性
currentActiveIdx
指向当前Tab索引,当切换Tab时,currentActiveIdx
跟着变化,再发现currentActiveIdx
发生变化的时候, 想办法调整线条宽度和移动的位置componentWillReceiveProps(nextProps) { const newVal = nextProps.currentActiveIdx const oldVal = this.props.currentActiveIdx if (newVal == oldVal) return // todo 调整线条样式 } 复制代码
具体
todo
了什么?-
想办法获取组件宽高位置信息。
-
获取到上一次
oldVal
的组件的宽高位置, 记录。 -
获取到这一次
newVal
的组件的宽高位置, 记录。 -
始点
startTranslateValue
设置为这一次的终点。 -
终点
endTtranslateValue
设置为这一次的x坐标值(nodeProps.x - scaleSize(30)
减去容器的左边距内填充值)。 -
定时器设置,确保组件完成渲染,宽高已经撑开。
setTimeout(() => { getNodePropsByRef(this['item' + oldVal]).then(nodeProps => { this.map[oldVal] = { w: nodeProps.width, x: nodeProps.x } getNodePropsByRef(this['item' + newVal]).then(nodeProps => { this.map[newVal] = { w: nodeProps.width, x: nodeProps.x - scaleSize(30) } this.setState({ startTranslateValue: this.state.endTtranslateValue, endTtranslateValue: this.map[newVal].x, lineWidth: parseFloat(this.map[newVal].w) }) }) }) }, 100) 复制代码
getNodePropsByRef
方法如下:x
、y
是相对于整个屏幕,左上角坐标import {findNodeHandle, UIManager} from 'react-native' export const getNodePropsByRef = ref => { const handle = findNodeHandle(ref); return new Promise((resolve) => { UIManager.measure(handle, (x, y, width, height, pageX, pageY) => { resolve({ x, y, width, height, pageX, pageY }) }) }) } 复制代码
-
省市区数据及其处理
-
数据用的是
area-data
pca['86'] // 等同于 AreaData['86'] // 所有省份:{'110000': '北京市', '120000': '天津市', '130000': '河北省', ...} pcaa['130000'] // 等同于 AreaData['130000'] // 对应省份的所有城市:{'130100': '石家庄市', '130200': '唐山市', '130300': '秦皇岛市', ...} pcaa['130200'] // 等同于 AreaData['130200'] // 对应市的所有县区:{'130201': '市辖区', '130202': '路南区', '130203': '路北区', ...} 复制代码
-
初始时配置的数据
selectedRows: [ { label: '请选择', value: null, values: pca['86'] } ] 复制代码
当选中其中一个数据时,向
selectedRows
添加第二组数据selectedRows.push({ label: '请选择', value: null, values: pcaa[item.key] }) 复制代码
当已经有三组数据,并选中第一组的某个值时, 去掉第三组数据
selectedRows.length == 3 && selectedRows.splice(2, 1) 复制代码
同样的思路处理第二组,第三组数据的选取,最后判断选完后,调用函数返回数据
onFinished(selectedRows.map(o => ({label: o.label, value: o.value}))) 复制代码
-
列表组件
FlatList
<FlatList ref={getFlatListRef} showsVerticalScrollIndicator={false} onContentSizeChange={onFlatListContentSizeChange} data={values} keyExtractor={(item, index) => item.key + ''} renderItem={ItemView} /> 复制代码
每次更换列表数据时,需要滚动到顶部
onFlatListContentSizeChange(){ if (!!this.flatListRef) { this.flatListRef.scrollToIndex({index: 0, animated: false}) } } 复制代码
-
GITHUB - 博客
欢迎start ~ ! -
GITHUB - 组件
欢迎start ~ !