React瀑布流

前端页面样式修改的时候,要用到瀑布流样式,本来是想用flex直接换行显示,试了发现并不是想要的效果;真正的瀑布流是每项的宽度固定,高度不固定,自动填充到高度最小的列下面,还具有有上拉加载更多功能;网上大部分是js实现的瀑布流,react方式实现的比较少,这里记录一下

  1. 用数组记录每列高度,遍历item数组,将item添加到高度最小的列下面;
    renderItemList = () => {//上面图片,下面文字;图片为正方形或长方形,文字一行或两行
        const { itemList } = this.state;
        let columnHeights = [0, 0];//记录每列的高度
        return itemList.map((item, index) => {
            const width = (document.body.clientWidth - 80)/2;//计算图片宽度
            const height = (item.type==3) ? width : width * (200/335);//计算图片高度
            let columIndex = columnHeights[0] > columnHeights[1] ? 1 : 0;//最小高度列
            let top = columnHeights[columIndex];//item的top设置为最小高度列的高度
            let left = columIndex==0 ? 0 : (document.body.clientWidth - 80)/2 + 20;//item的left根据最小高度列设置
            const textHeight = Math.min(this.getTextSize(item.name).height, 72);//文字高度不同的话,需要计算文字高度
            columnHeights[columIndex] += (height + 96 + textHeight + 24);//更新最小高度列的高度,内容高度+间隔
            //item使用absolute定位,根据计算出来的top和left设置位置
            return <div onClick={onClick} style={{width, top, left, position: 'absolute'}}>
                <img
                    src={item.image}
                    style={{height}}
                />
                <div>
                    {item.name}
                </div>
            </div>
        });
    }
    getTextSize = (text) => {
        var span = document.createElement('span')
        var dom = document.createElement('div')
        dom.style.width = (document.body.clientWidth - 80)/2 - 40 + 'px';
        var result = {}
        result.width = span.offsetWidth
        result.height = span.offsetHeight
        span.style.visibility = "hidden";
        span.style.fontSize = '28px';
        span.style.lineHeight = '36px';//最好设置行高 方便项目中计算
        span.style.fontFamily = 'PingFangSC-Regular, PingFang SC';
        span.style.display = 'inline-block';
        dom.appendChild(span)
        document.body.appendChild(dom)
        if (typeof span.textContent !== 'undefined') {
          span.textContent = text
        } else {
          span.innerText = text
        }
        result.width = parseFloat(window.getComputedStyle(span).width) - result.width
        result.height = parseFloat(window.getComputedStyle(span).height) - result.height
        document.body.removeChild(dom);//删除节点
        // console.log('text size', text, result);
        return result
    }
    
  2. 设置父元素position为relative,计算父元素高度并设置;
    itemContainerWidth = document.body.clientWidth - 60;
    let columnHeights = [0, 0];
    itemList.forEach((item, index) => {
        const imgWidth = (document.body.clientWidth - 80)/2;
        const imgHeight = (item.type==3) ? imgWidth : imgWidth * (200/335);
        let columIndex = columnHeights[0] > columnHeights[1] ? 1 : 0;
        const textHeight = Math.min(this.getTextSize(item.name).height, 72);
        columnHeights[columIndex] += (imgHeight + 96 + textHeight + 24);
    });
    height = Math.max(columnHeights[0], columnHeights[1]) - 24;
    
    <div style={{ width: itemContainerWidth, height, position: 'relative' }}>
        {this.renderItemList()} 
    </div>
    
  3. 上拉加载更多,原来使用的都是列表,需要在外面包裹一层组件,这里发现不适合,所以改用自定义监听页面滚动事件判断;
    componentDidMount() {
        window.addEventListener('scroll', this.handleScroll, true);
    }
    componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScroll, true);
    }
    
    handleScroll = () => {
        const indexDiv = document.querySelector('#example .container > div');
        if (!indexDiv) return;
        if (indexDiv.scrollTop > indexDiv.scrollHeight - indexDiv.clientHeight - 200) {
            const { pageNo, hasMore, isLoading } = this.state;
            if (hasMore && !isLoading) {
                this.getSmartRecommendList(pageNo + 1);
            }
        }
    };
    

问题

  1. 内容显示不全,因为absolute元素不会占用空间,导致父元素高度为0,需要在render里面,计算所有行最大高度,设置为容器的高度;
  2. 直接使用span计算文字高度不对,需要在外面包裹一个div,指定div宽度为列的宽度;
  3. 在didMount里写window.addEventListener的scroll方法无效,需要把最后一个参数设置为true,window.addEventListener('scroll', this.handleScroll, true)在捕获阶段而不是在冒泡阶段执行事件;

参考链接

https://blog.csdn.net/qq_45473786/article/details/105814902
https://blog.csdn.net/lhz_19/article/details/120413374

posted @ 2022-10-08 20:42  yuyuyu37  阅读(908)  评论(0编辑  收藏  举报