React Native 锚点 ScrollView 实现

引用:

<Anchor
      anchorList={anchorContentCultureList}
      initCurIndex='shiming'
 />

 

anchorContentCultureList   config.tsx
import React from 'react';
import { ImageSourcePropType } from 'react-native';
import { Image } from 'react-native-ui-lib';
import shimingImg from '../images/shiming.png';
import yuanjingImg from '../images/yuanjing.png';
import jiazhiguanImg from '../images/jiazhiguan.png';
import linianImg from '../images/linian.png';
import sanwenImg from '../images/sanwen.png';
import jilv1Img from '../images/jilv1.png';
import tielvImg from '../images/tielv.png';
import jilv2Img from '../images/jilv2.png';
import { AnimatableView } from '../../../components';
import { deviceWidth } from '../../../utils';

const imgConfig = {
  shiming: shimingImg,
  yuanjing: yuanjingImg,
  jiazhiguan: jiazhiguanImg,
  linian: linianImg,
  sanwen: sanwenImg,
};

const reactNode = (id: string) => {
  if (id === 'jilv') {
    return (
      <AnimatableView key={id} style={{ marginLeft: 15 }}>
        <Image style={{ width: deviceWidth - 30 }} source={jilv1Img as ImageSourcePropType} />
        <Image style={{ width: deviceWidth - 30 }} source={tielvImg as ImageSourcePropType} />
        <Image style={{ width: deviceWidth - 30 }} source={jilv2Img as ImageSourcePropType} />
      </AnimatableView>
    );
  }
  return (
    <AnimatableView key={id} style={{ marginLeft: 15 }}>
      <Image style={{ width: deviceWidth - 30 }} source={imgConfig[id] as ImageSourcePropType} />
    </AnimatableView>
  );

};

export const anchorContentCultureList = [{
  id: 'shiming',
  name: '使命',
  reactNode: reactNode('shiming'),
}, {
  id: 'yuanjing',
  name: '愿景',
  reactNode: reactNode('yuanjing'),
}, {
  id: 'jiazhiguan',
  name: '价值观',
  reactNode: reactNode('jiazhiguan'),
}, {
  id: 'linian',
  name: '理念',
  reactNode: reactNode('linian'),
}, {
  id: 'sanwen',
  name: '三问',
  reactNode: reactNode('sanwen'),
}, {
  id: 'jilv',
  name: '纪律',
  reactNode: reactNode('jilv'),
}];

 

 

Anchor.tsx 代码

// 社区Tab下 文化Tab页 锚点
import React, { ReactNode, useState, useRef } from 'react';
import { ScrollView } from 'react-native';
import { Text, View, Colors } from 'react-native-ui-lib';
import { LineSpace, Touchable } from '../../components';

type Iarr = {
  y: number,
  height: number,
  id: string
};
type IAnchorItem = {
  id: string,
  name: string,
  reactNode: ReactNode,
};
type IAnchorProps = {
  otherChildren: ReactNode, // 锚点所在滚动区的其他组件
  anchorList: IAnchorItem[], // 锚点list
  initCurIndex: string, // 初始化时选中的锚点,一般是第一个
};
const Anchor = (props: IAnchorProps) => {
  const anchorScrollViewRef: any = useRef(null); //锚点内容滚动容器
  const { otherChildren, anchorList, initCurIndex = 'shiming' } = props;
  const [curIndex, setCurIndex] = useState(initCurIndex); // 锚点的id

  const [anchorArr, setAnchorArr] = useState<Iarr[]>([]); // 存储锚点数组,记录滚动位置y 组件高度height 锚点id

  // 点击锚点,滚动到指定位置
  const handleLocation = (index: number) => {
    setCurIndex(anchorArr[index].id);
    anchorScrollViewRef && anchorScrollViewRef.current && anchorScrollViewRef.current.scrollTo({ x: 0, y: anchorArr[index].y, animated: false });  //anchorArr 就是之前定义的arr
  };

  // 文化滚动内容的一些布局属性
  const onLayout = (e: any, id: string) => {
    const newAnchorArr = [...new Set([...anchorArr, { y: e.layout.y, height: e.layout.height, id: id }])];
    // 按照y大小排序,因为苹果手机上顺序是乱的。
    newAnchorArr.sort((a: Iarr, b: Iarr) => { return a.y - b.y; });
    setAnchorArr([...newAnchorArr]);
  };
  const onScroll = (event: any) => {
    const scrollY = event?.nativeEvent?.contentOffset.y;
    for (let i = 0; i < anchorArr.length - 1; i++) {
      if (scrollY < anchorArr[0].height - 10) {
        setCurIndex(anchorArr[0].id);
      } else if (scrollY > anchorArr[i + 1].y - (anchorArr[i].height / 2)) {   //当滚动到超过当前盒子一半的时候
        setCurIndex(anchorArr[i + 1].id);
      }
    }
  };
  return (
    <View style={{ flexDirection: 'row', flex: 1 }}>
      <ScrollView
        scrollEventThrottle={100}
        showsVerticalScrollIndicator={false}
        ref={anchorScrollViewRef}
        onScroll={onScroll}
      >
        {otherChildren}
        {
          anchorList.map((item) => {
            return (
              <>
                <View
                  key={item.id}
                  onLayout={({ nativeEvent: e }) => onLayout(e, item.id)}
                >
                  {item.reactNode}
                </View>
              </>
            );
          })
        }
      </ScrollView>
      <View style={{
        position: 'absolute', top: 156, right: 10, width: 50,
      }}>
        <ScrollView showsVerticalScrollIndicator={false}>
          {anchorList.map((item, index) => (
            <View key={item.id}>
              <Touchable onPress={() => handleLocation(index)}>
                <Text
                  grey66
                  f12
                  l28
                  style={{
                    textAlign: 'center',
                    backgroundColor: curIndex === item.id ? 'rgba(74, 144, 226, 0.8)' : 'transparent',
                    color: curIndex === item.id ? Colors.white : Colors.grey66,
                    borderRadius: 4,
                  }}
                >{item.name || ''}</Text>
              </Touchable>
              <LineSpace height={24} />
            </View>
          ))}
        </ScrollView>
      </View>
    </View>
  );
};

export default Anchor;

 

 

 

 

posted @ 2022-11-03 13:52  飞奔的程序员  阅读(356)  评论(0)    收藏  举报