写了一个动态的计算方法,就先记录一下。(其实是反面教材)
/**
* 超出2行可展开的组件
*/
import YpIconTabSxNor from '@/components/Iconfont/YpIconTabSxNor'
import { c, create, pxToDp, s } from '@/core/styleSheet'
import React, { memo, useEffect, useRef, useState } from 'react'
import { View, Text, TouchableOpacity } from 'react-native'
type Iprops = {
/** 标题 */
title: string
/** 标签数据 */
tags: any[]
}
const SLICE_NUM = 6
const ExtendTags = (props: Iprops) => {
const { title, tags = [] } = props
// 是否显示所有标签
const [showAllTags, setShowAllTags] = useState(false)
// containerRef
const containerRef = useRef(null)
// 是否显示展开更多文案
const [showMore, setShowMore] = useState(false)
// 显示的实际标签数量
const [visibleTags, setVisibleTags] = useState(tags?.slice(0, SLICE_NUM))
// 次数
const [once, setOnce] = useState(0)
// 如果个数刚好是初始化截取的个数的数据处理
const initSliceData = useRef<number>(SLICE_NUM)
// 是否结束循环验证
const isEnd = useRef<number>(0)
// 折半的个数
const middleNum = useRef<number>(0)
// 最终能展示为2行的个数
const finallyNum = useRef<number>(0)
// 记录上一次的状态是否已经是2行
const lastStatus = useRef<boolean>(false)
useEffect(() => {
if (tags.length > 0) {
middleNum.current = Math.floor(tags.length / 2)
setVisibleTags(tags)
}
}, [tags.length])
useEffect(() => {
if (containerRef.current) {
containerRef.current.measure((x, y, width, height) => {
if (isEnd.current == 1) {
return
}
// 如果刚好为初始化截取的个数,并且还超出了2行
if (tags?.length == SLICE_NUM && SLICE_NUM > 1) {
if (height > 72) {
if (once == 0) {
if (height == 73) {
setVisibleTags(tags)
isEnd.current = 1
setOnce(-1)
} else if (isEnd.current != 1) {
initSliceData.current = SLICE_NUM - 1
setVisibleTags(tags?.slice(0, initSliceData.current))
setOnce(SLICE_NUM - initSliceData.current)
}
}
// 如果还超出了2行
if (height > 109 && once == (SLICE_NUM - initSliceData.current) && isEnd.current != 1) {
if (initSliceData.current == 1) {
setVisibleTags(tags?.slice(0, 1))
setShowMore(true)
isEnd.current = 1
setOnce(-1)
} else {
initSliceData.current -= 1
setVisibleTags(tags?.slice(0, initSliceData.current))
setOnce(SLICE_NUM - initSliceData.current)
}
} else if (once == (SLICE_NUM - initSliceData.current) && isEnd.current != 1) {
setVisibleTags(tags?.slice(0, initSliceData.current))
setShowMore(true)
isEnd.current = 1
setOnce(-1)
}
}
} else {
if (once == 0) {
// 第一次都展示全部,看下具体的高度
setVisibleTags(tags)
setOnce(1)
}
// 如果高度就2行内直接展示
if (height < 109 && once == 1) {
setVisibleTags(tags)
isEnd.current = 1
setOnce(-1)
}
if (height > 109 && once == 1 && isEnd.current != 1) {
// 折半
finallyNum.current = middleNum.current
setVisibleTags(tags?.slice(0, middleNum.current))
setOnce(finallyNum.current)
}
// 折半后 判断高度
if (height > 109 && once == finallyNum.current && isEnd.current != 1) {
if (lastStatus.current) {
setVisibleTags(tags?.slice(0, finallyNum.current - 1))
setShowMore(true)
isEnd.current = 1
setOnce(-1)
} else {
// 逐个减1
finallyNum.current -= 1
setVisibleTags(tags?.slice(0, finallyNum.current))
setOnce(finallyNum.current)
}
} else if (height < 109 && height > 0 && once == finallyNum.current && isEnd.current != 1) {
// 如果折半后刚好2行内, 逐个增1
lastStatus.current = true
finallyNum.current += 1
setVisibleTags(tags?.slice(0, finallyNum.current))
setOnce(finallyNum.current)
}
}
})
}
}, [containerRef.current, once])
const onPressMore = () => {
setVisibleTags(tags)
setShowAllTags(true)
setShowMore(false)
}
if (tags?.length > 0) {
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<View
style={[styles.tagsContainer, showAllTags ? styles.tagsMore : {}]}
ref={containerRef}
>
{visibleTags.map((tag: any, index: number) => {
// 仅针对(老板评价场景)
const isGoodRating = tag?.type && tag.type == 1
return (
<View key={index} style={[styles.tagBox, isGoodRating ? styles.goodBg : {}]}>
<Text style={[styles.tagText, isGoodRating ? styles.goodText : {}]}>{tag.txt}</Text>
</View>
)
})}
</View>
{!!showMore && (
<TouchableOpacity onPress={onPressMore} style={styles.showMoreBox}>
<Text style={styles.showMoreText}>展开更多</Text>
<YpIconTabSxNor size={pxToDp(24)} style={styles.arrowIcon}/>
</TouchableOpacity>
)}
</View>
)
}
return <></>
}
const styles = create({
container: {
...s.padding(32, 24),
backgroundColor: c.white,
marginBottom: 16,
borderRadius: 16,
flexDirection: 'column',
},
title: {
height: 48,
width: '100%',
fontSize: 34,
fontWeight: 'bold',
marginBottom: 32,
color: c.black85,
},
tagsContainer: {
width: '100%',
flexDirection: 'row',
flexWrap: 'wrap',
overflow: 'hidden',
},
tagsMore: {
height: 'auto',
minHeight: 'auto',
overflow: 'visible',
},
tagBox: {
backgroundColor: '#F5F6FA',
borderRadius: 8,
paddingHorizontal: 16,
paddingVertical: 12,
marginRight: 16,
marginBottom: 16,
},
goodBg: {
backgroundColor: '#E0F3FF',
},
tagText: {
fontSize: 26,
color: c.black85,
lineHeight: 36,
},
goodText: {
color: c.primary,
},
showMoreBox: {
width: '100%',
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
height: 36,
marginTop: 16,
},
showMoreText: {
fontSize: 26,
color: c.black85,
},
arrowIcon: {
marginLeft: 8,
},
})
export default memo(ExtendTags)
/**
* 超出2行可展开的组件
*/
import YpIconTabSxNor from '@/components/Iconfont/YpIconTabSxNor'
import { c, create, pxToDp, s } from '@/core/styleSheet'
import React, { memo, useEffect, useRef, useState } from 'react'
import { View, Text, TouchableOpacity } from 'react-native'
type Iprops = {
/** 标题 */
title: string
/** 标签数据 */
tags: any[]
}
const ExtendTags = (props: Iprops) => {
const { title, tags = [] } = props
// 是否显示所有标签
const [showAllTags, setShowAllTags] = useState(false)
// containerRef
const containerRef = useRef(null)
// 是否显示展开更多文案
const [showMore, setShowMore] = useState(false)
// 显示的实际标签数量
const [visibleTags, setVisibleTags] = useState(tags)
// 是否结束循环验证
const isEnd = useRef<number>(0)
useEffect(() => {
if (tags.length > 0) {
setVisibleTags(tags)
}
}, [tags.length])
useEffect(() => {
if (containerRef.current) {
containerRef.current.measure((x, y, width, height) => {
if (isEnd.current == 1) {
return
}
if (height > 109) {
setShowMore(true)
isEnd.current = 1
}
})
}
}, [containerRef.current])
const onPressMore = () => {
setVisibleTags(tags)
setShowAllTags(true)
setShowMore(false)
}
if (tags?.length > 0) {
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<View
style={[styles.tagsContainer, showAllTags ? styles.tagsMore : {}, showMore ? styles.outHeight : {}]}
ref={containerRef}
>
{visibleTags.map((tag: any, index: number) => {
// 仅针对(老板评价场景)
const isGoodRating = tag?.type && tag.type == 1
return (
<View key={index} style={[styles.tagBox, isGoodRating ? styles.goodBg : {}]}>
<Text style={[styles.tagText, isGoodRating ? styles.goodText : {}]}>{tag.txt}</Text>
</View>
)
})}
</View>
{!!showMore && (
<TouchableOpacity onPress={onPressMore} style={styles.showMoreBox}>
<Text style={styles.showMoreText}>展开更多</Text>
<YpIconTabSxNor size={pxToDp(24)} style={styles.arrowIcon}/>
</TouchableOpacity>
)}
</View>
)
}
return <></>
}
const styles = create({
outHeight: {
height: 149,
},
container: {
...s.padding(32, 24),
backgroundColor: c.white,
marginBottom: 16,
borderRadius: 16,
flexDirection: 'column',
},
title: {
height: 48,
width: '100%',
fontSize: 34,
fontWeight: 'bold',
marginBottom: 32,
color: c.black85,
},
tagsContainer: {
width: '100%',
flexDirection: 'row',
flexWrap: 'wrap',
overflow: 'hidden',
},
tagsMore: {
height: 'auto',
minHeight: 'auto',
overflow: 'visible',
},
tagBox: {
backgroundColor: '#F5F6FA',
borderRadius: 8,
paddingHorizontal: 16,
paddingVertical: 12,
marginRight: 16,
marginBottom: 16,
},
goodBg: {
backgroundColor: '#E0F3FF',
},
tagText: {
fontSize: 26,
color: c.black85,
lineHeight: 36,
},
goodText: {
color: c.primary,
},
showMoreBox: {
width: '100%',
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
height: 36,
marginTop: 16,
},
showMoreText: {
fontSize: 26,
color: c.black85,
},
arrowIcon: {
marginLeft: 8,
},
})
export default memo(ExtendTags)