寫一個地區選擇組件備用(ReactNative)

組件效果以下

IOS Android

使用組件

<AreaPicker
    onFinished={this.onFinished.bind(this)}
    modalHeight={800}
    visible={visible} 
/>
複製代碼
  • visible: 類型 boolean, 控制是否顯示組件node

  • modalHeight:類型 number, 設置內容窗口的高度react

  • onFinished:類型 func, 選取完整地區後回調, 返回值是一個數組[{label: 'xx', value: 'xx'}]git

    onFinished(list) {
        this.setState({
            selected: (list.map(o => o.label)).join('')
        })
    }
    複製代碼

實現邏輯

動畫
  • 使用react-native-animatable, 須要先安裝。github

    yarn add react-native-animatable
    複製代碼
  • 遮罩層的淡入效果npm

    <Animatable.View style={[styles.container, !visible && {opacity: 0, transform:[{scale: 0}]}]} ref = {ref => this.containerRef = ref}>
    
    </Animatable.View>
    複製代碼

    初始化組件的時候,componentWillReceiveProps 不會執行,當props發生變化時執行,能夠在這個函數裏調用this.setState()去更新組件內部屬性,其中也能夠經過this.props去獲取到舊的屬性值,完成一些對比邏輯。react-native

    componentWillReceiveProps(nextProps){
        const { visible } = nextProps
        !!this.containerRef && this.containerRef.animate('fadeIn', ANIMATE_DURATION).then(() => {
            // todo...
        })
    }
    複製代碼
  • 選項卡的滾動線條數組

    線條元素絕對定位,寬度動態獲取,移動的位置經過 translateX 設置bash

    <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 方法以下:

    xy 是相對於整個屏幕,左上角座標

    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 ~ !

相關文章
相關標籤/搜索