动态计算高度实现展示更多逻辑

发布时间:2023年12月29日

写了一个动态的计算方法,就先记录一下。(其实是反面教材

/**
 * 超出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行,直接给一个2行的高度,然后 overflow : hiddlen 完事。

/**
 * 超出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)

文章来源:https://blog.csdn.net/qq_40610760/article/details/135294758
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。