基于react native的锚点
效果示例图
示例代码
/* eslint-disable react-native/no-inline-styles */
import React, { useEffect, useRef, useState } from 'react';
import {
Image,
ImageBackground,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import HTML from 'react-native-render-html';
import { pxToPd, pxToPdT } from '../../common/js/device';
const desc =
'<p>如违反《赛事须知》中的内容,可能使我受到警告、判负、丧失比赛资格/获奖资格以及禁赛等赛事处罚,亦可能被请离现场。如违反《赛事须知》中的内容,可能使我受到警告、判负、丧失比赛资格/获奖资格以及禁赛等赛事处罚,亦可能被请离现场。如违反《赛事须知》中的内容,可能使我受到警告、判负、丧失比赛资格/获奖资格以及禁赛等赛事处罚,亦可能被请离现场。</p>';
const tempData = [
{
id: 1,
name: '工藤新一一级标题',
data: [
{
id: 2,
name: '二级标题002',
content: desc,
},
{
id: 3,
name: '二级标题003',
content: desc,
},
{
id: 4,
name: '二级标题004',
content: desc,
},
],
},
{
id: 5,
name: '毛利兰一级标题',
data: [
{
id: 6,
name: '二级标题006',
content: desc,
},
{
id: 7,
name: '二级标题007',
content: desc,
},
{
id: 8,
name: '二级标题008',
content: desc,
},
],
},
{
id: 9,
name: '流放了一级标题',
data: [
{
id: 10,
name: '二级标题0010',
content: desc,
},
{
id: 11,
name: '二级标题0011',
content: desc,
},
{
id: 12,
name: '二级标题0012',
content: desc,
},
],
},
];
const TestCatalog = () => {
const scrollViewRef = useRef(null);
//列表数据
const [dataList, setDataList] = useState([]);
const viewRefs = useRef([]);
const menuRef = useRef([]);
const initFunction = () => {
setDataList(() => tempData);
};
const goSkipHandle = row => {
let tempArr = menuRef.current;
tempArr.some(item => {
if (item.id === row.id) {
scrollViewRef.current.scrollTo({ y: item?.y - 100, animated: true });
}
});
};
const getPositionValue = (row, y) => {
let tempMenu = [...menuRef.current];
let tempItem = row;
if (tempItem.y === undefined) {
tempItem.y = y;
}
tempMenu.push(row);
menuRef.current = tempMenu;
};
useEffect(() => {
initFunction();
return () => {};
}, []);
return (
<>
<View style={{ flex: 1 }}>
<ScrollView ref={scrollViewRef}>
{/* 列表内容 */}
{dataList?.map((item, index) => (
<View
style={styles.ruleBlock}
key={'ruleBlock-' + index}
ref={ref => {
viewRefs.current[item?.id] = ref;
}}
onLayout={() => {
if (viewRefs.current[item?.id]) {
viewRefs.current[item?.id].measure(
(x, y, width, height, pageX, pageY) => {
console.log(x, y, width, height, pageX);
getPositionValue(item, pageY);
},
);
}
}}>
{/* 一级标题 */}
<View style={styles.ruleBlockTitle}>
<ImageBackground
style={styles.ruleBlockTitleBg}
source={require('../../common/imgs/main_ruleDetail_titleBg.png')}>
<Text
style={styles.ruleBlockTitleTxt}
ellipsizeMode="middle"
numberOfLines={1}>
{item?.name}
</Text>
</ImageBackground>
</View>
{/* 装饰图 */}
<Image
style={styles.ruleBlockDecorate}
source={require('../../common/imgs/main_ruleDetail_tips.png')}
/>
{/* 列表内容-start */}
{item?.data?.map((subitem, subindex) => (
<View
style={styles.ruleBlockSubCon}
key={'ruleBlockSubCon-' + subindex}>
{/* 二级标题 */}
<View
style={styles.ruleBlockSubTitle}
ref={ref => {
viewRefs.current[subitem?.id] = ref;
}}
onLayout={() => {
if (viewRefs.current[subitem?.id]) {
viewRefs.current[subitem?.id].measure(
(x, y, width, height, pageX, pageY) => {
console.log(x, y, width, height, pageX);
getPositionValue(subitem, pageY);
},
);
}
}}>
<Text style={styles.ruleBlockSubTitleTxt}>
{subitem?.name}
</Text>
</View>
{/* 内容 */}
<View style={{ width: '100%', height: pxToPd(24) }} />
<View style={styles.ruleBlockSubDesc}>
<HTML
contentWidth={100}
source={{
html: `<div>${subitem?.content}</div>`,
}}
/>
</View>
</View>
))}
{/* 列表内容-end*/}
<View style={{ width: '100%', height: pxToPd(40) }} />
</View>
))}
</ScrollView>
</View>
<View style={styles.modalWrap}>
{dataList?.map(item => (
<View>
<TouchableOpacity onPress={() => goSkipHandle(item)}>
<Text
style={{
width: '100%',
height: pxToPd(60),
lineHeight: pxToPd(60),
textAlign: 'center',
}}>
{item?.name}
</Text>
</TouchableOpacity>
{item?.data?.map(subitem => (
<TouchableOpacity onPress={() => goSkipHandle(subitem)}>
<Text
style={{
width: '100%',
height: pxToPd(60),
lineHeight: pxToPd(60),
textAlign: 'center',
}}>
{subitem?.name}
</Text>
</TouchableOpacity>
))}
</View>
))}
</View>
</>
);
};
const styles = StyleSheet.create({
detailBg: {
width: '100%',
height: pxToPd(1624),
},
container: {
flex: 1,
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
//内容
ruleBlock: {
width: '93.6%',
marginLeft: '3.2%',
borderRadius: pxToPd(40),
backgroundColor: '#fff',
minHeight: pxToPd(400),
position: 'relative',
marginBottom: pxToPd(97),
},
ruleBlockTitle: {
width: pxToPd(533),
height: pxToPd(86),
marginLeft: -pxToPd(8),
marginTop: -pxToPd(8),
},
ruleBlockTitleBg: {
width: '100%',
height: '100%',
},
ruleBlockTitleTxt: {
height: pxToPd(86),
width: pxToPd(500),
lineHeight: pxToPd(86),
fontSize: pxToPdT(32),
fontWeight: 'bold',
color: '#fff',
marginLeft: pxToPd(19),
},
ruleBlockDecorate: {
width: pxToPd(160),
height: pxToPd(193),
position: 'absolute',
top: -pxToPd(14),
right: -pxToPd(1),
},
ruleBlockSubCon: {
width: '93.6%',
marginLeft: '3.2%',
marginTop: pxToPd(42),
},
ruleBlockSubTitle: {
width: '100%',
},
ruleBlockSubTitleTxt: {
width: '100%',
fontSize: pxToPdT(30),
fontWeight: 'normal',
color: '#333',
textAlign: 'center',
},
ruleBlockSubDesc: {
width: '100%',
},
modalWrap: {
borderRadius: pxToPd(24),
width: pxToPd(300),
position: 'absolute',
top: 100,
right: 20,
backgroundColor: '#f5f5f5',
flexDirection: 'column',
},
});
export default TestCatalog;