类小红书的社交卡片瀑布流

接到一个瀑布流布局的需求,网上找了一遍也没有找到合适的实现方式。无奈之下只能自己实现了。

先看效果:

实现思路

  1. 遍历列表创建异步任务(用任务队列管理),单个任务渲染一个卡片(计算获取最短列,然后将卡片推入最短列中)
  2. 使用 IntersectionObserver 监听加载,当滚动到底部时加载更多数据。

废话不多说,上代码吧

瀑布流渲染逻辑

这里进行了浅对比,如果判断是新增元素的情况,则不需要全部重新渲染,只需要将新增的数据创建对应异步任务即可

scss 复制代码
​
watch(
    [() => props.list],
    ([list], [prevList]) => {
        // 判断是不是新增了元素
        if (Array.isArray(list) && Array.isArray(prevList) && list.length > prevList.length && shallowCompareFirstNElements(list, prevList, prevList.length)) {
            const newArr = list.slice(prevList.length);
            distributeNewItems(newArr);
        } else {
            taskQueue.clear();
            initColumns();
            if (list.length > 0) {
                distributeNewItems(list);
            }
        }
    },
    {
        immediate: true,
    }
)

列高度计算逻辑

ini 复制代码
// 计算列高度
const updateColumnHeights = async () => {
    await nextTick();
    columns.value?.forEach(column => {
        const element = document.getElementById(column.id);
        if (element) {
            const rect = element.getBoundingClientRect();
            column.height = rect.height ?? 0;
        }
    });
};

异步任务创建逻辑

scss 复制代码
// 追加新数据到瀑布流
const distributeNewItems = (newItems: any[]) => {
    newItems.forEach(item => {
        const task = () => new Promise(async (resolve, reject) => {
            try {
                await updateColumnHeights()
                const shortestIndex = findShortestColumnIndex();
                columns.value[shortestIndex].items.push({ data: item, imageLoaded: () => resolve('') });
                if (!props.hasImage) {
                    resolve('');
                }
            } catch (err) {
                console.log(err);
                reject(err);
            }
        });
        taskQueue.enqueue(task);
    });
};

完整的逻辑请看可查看git仓库: github.com/KaiEi1020/w...

demo 还比较粗糙,有什么问题和建议欢迎讨论

相关推荐
漂流瓶jz5 小时前
Webpack中各种devtool配置的含义与SourceMap生成逻辑
前端·javascript·webpack
这是个栗子5 小时前
【问题解决】用pnpm创建的 Vue3项目找不到 .eslintrc.js文件 及 后续的eslint配置的解决办法
javascript·vue.js·pnpm·eslint
前端架构师-老李5 小时前
React 中 useCallback 的基本使用和原理解析
前端·react.js·前端框架
木易 士心6 小时前
CSS 中 `data-status` 的使用详解
前端·css
明月与玄武6 小时前
前端缓存战争:回车与刷新按钮的终极对决!
前端·缓存·回车 vs 点击刷新
花姐夫Jun6 小时前
基于Vue+Python+Orange Pi Zero3的完整视频监控方案
vue.js·python·音视频
牧马少女6 小时前
css 画一个圆角渐变色边框
前端·css
zy happy6 小时前
RuoyiApp 在vuex,state存储nickname vue2
前端·javascript·小程序·uni-app·vue·ruoyi
小雨青年7 小时前
Cursor 项目实战:AI播客策划助手(二)—— 多轮交互打磨播客文案的技术实现与实践
前端·人工智能·状态模式·交互
533_7 小时前
[vue3] h函数,阻止事件冒泡
javascript·vue.js·elementui