瀑布流
瀑布流布局
- 内容框宽度固定,高度不固定。
 - 内容框从左到右排列,一行排满后,其余内容框就会按顺序排在短的一列后。
 
实现效果

实现思路
1. 编写结构样式 三列 每一列中都有很多项 每一项的高度由图片的真实高度决定
2. 发送ajax请求 从服务器获取数据
3. 按照瀑布流的规则实现动态绑定 (每次拿出三条数据,把最小的图片插入到最高的列中)
4. 为了优化页面的第一次渲染速度,我们最开始,并不会去加载真实的图片,当内容渲染完后,我们再加载真实的图片(图片的懒加载)
只有完全出现在可视窗口的图片 再去加载真实的图片
- 
通过获取图片底部距离body父级参照物的上偏移(A) 和 获取页面底部距离父级参照物的偏移(B) A<=B 表示出现在了视口中
 - 
基于getBoundingClientRect优化懒加载条件
 - 
IntersectionObserver ES6 新的监听 能够监听一个或者多个盒子和浏览器可视窗口的交叉信息
 
5. 当页面滚动到底部,我们去加载更多的数据
HTML
为了减少代码量,这里我们删掉不必要的东西,主体结构如以下代码所示
<body>
    <div class="container" id="fallsBox">
        <!-- 第一列 -->
        <div class="column">
            <!-- <div class="item">
                <a href="">
                    <div class="pic-box">
                        <img src="./images/10.jpg" alt="">
                    </div>
                    <p class="desc-box">
                        泰勒·斯威夫特(Taylor Swift),1989年12月13日出生于美国宾州,美国歌手、演员。2006年出道,同年发行专辑《泰勒·斯威夫特》,该专辑获得美国唱片业协会的白金唱片认证
                    </p>
                </a>
            </div> -->
        </div>
        <!-- 第二列 -->
        <div class="column">
        </div>
        <!--第三列  -->
        <div class="column">
        </div>
    </div>
    <div class="loadMoreBox"></div>
    <!-- IMPORT JS -->
    <script src="js/index.js"></script>
</body>
CSS
编写css不成文小规定
1.控制显示和隐藏
2.控制位置
3.width/border/padding
css的代码实在是太多了,这里大家看上面的实例自己随便写一写就好,为了减少代码量,这里我们直接把它删掉
JS
思路
瀑布流的最终效果
- 
获取元素
- 
最外层容器
 - 
每一列
 - 
放图片的数组
 - 
加载更多盒子
 
 - 
 - 
监听器
- 
IntersectionObserver 不兼容IE浏览器
 - 
创建监听器 用来监听图片盒子和可视窗口的交叉状态
 - 
循环每个盒子监听到的交叉盒子,把出现在视口中的盒子做延迟加载
 - 
target监听的盒子
 - 
isIntersecting 为 true 则是显示在了视口中
 - 
当前盒子已经出现在视口中 target监听的盒子
 - 
处理后 ,这个盒子就没必要再监听了
 - 
控制完全出现在视口中 才算交叉状态改变
 
 - 
 - 
从服务器获取数据
- ajax
 
 - 
动态渲染数据
- 
瀑布流规则 每一次从数据中取出3条数据(group) 三列(colums)
 - 
三条数据,按照图片高度排序[小,中,大]三列
 - 
盒子,按照高度进行排序[大,中,小]
 - 
服务器获取到的图片宽度高度,按照真实渲染的宽度(230)动态计算出真实渲染的高度
 - 
服务器获取到的宽高 width:300 height:433 300/433 宽高比
 - 
真实渲染的宽度为width:230px 求真实渲染的高度? 高度为 230/300/433
 - 
用数组中的map方法
 - 
把排好序的数据依次插入到对应的排好序的列中
 - 
动态创建每一个图片盒子 插入到指定列
 - 
数据绑定完成后,获取到所有的IMG
 
 - 
 - 
图片的延迟加载
- 
单张图片
 - 
传递的Img就是需要加载的图片对象
 - 
加载成功:图片地址是正确的
 - 
处理过的IMG设置已经处理过的标识
 
 - 
 - 
循环图片集合
- 
ONSCROLL事件 会在浏览器最快反应事件内触发一次(谷歌:5-7MS IE:10~17MS) 这样频率太快了
 - 
我们需要降低它的频率,【方案:函数的节流】
 - 
已经处理过的IMG就无需判断了
 - 
把没有处理过的图片,它所在的盒子交给监听器去监听
 
 - 
 - 
加载更多数据
 
图解

代码实现
let fallsModel = (function () {
    
    let fallsBox = document.querySelector('#fallsBox'),
        columns = Array.from(document.querySelectorAll('.column')),
        imgList = [],
        loadMoreBox=document.querySelector('.loadMoreBox');
    let data ;    
    let ob=new IntersectionObserver(changes=>{
        changes.forEach(change=>{
            let{isIntersecting,target}=change;
            if(isIntersecting){
                singleImgLazy(target.querySelector('img'));
                ob.unobserve(target);
            }
        });
    },{
        threshold:[1]
    });
    
    const queryData = function queryData() {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', './data.json', false);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                data = JSON.parse(xhr.responseText);
            };
        };
        xhr.send();
    };
    const binding = function binding() {
        data = data.map((item) => {
            let { width, height } = item;
            item.width = 230;
            item.height = 230 / (width / height);
            return item;//在把更改完的元素返回
        });
        let i = 0, //如果不想生成块级上下文 则写在外面 不想产生闭包
            group;
        for (; i < data.length; i += 3) {
            group = data.slice(i, i + 3); //从当前索引 取3项
            //i=0  0 1 2 
            //i=3  3 4 5
            group.sort((a, b) => {//按照图片高度 从小到大排序
                return a.height - b.height;
            });
            columns.sort((a, b) => {//按照盒子的高度 从大到小排序
                return b.offsetHeight - a.offsetHeight;
            });
            group.forEach((item, index) => {
                let { pic, height, title, link } = item;  
                let box = document.createElement("div");
                box.className = 'item';
                box.innerHTML = `<a href="${link}" target="_blank">
                <div class="pic-box" style="height:${height}px;">
                    <img src="" alt="" data-img="${pic}">
                </div>
                <p class="desc-box">
                    ${title}
                </p>
            </a>`;
                // 插入到指定列
                columns[index].appendChild(box);
            });
        };
        imgList = Array.from(fallsBox.querySelectorAll('.pic-box img'));
    };
    
    const singleImgLazy = function singleImgLazy(img) {   
        let pic = img.getAttribute('data-img'),
            temp = new Image();//=>document.createElement('img')
        temp.src = pic;
        temp.onload = () => {   
            img.src = pic;
            img.style.opacity = 1; //更改透明度  
        };
        img.setAttribute('isLoad', 'TRUE');
    };
    const lazyImgsLoading = function lazyImgsLoading() {
        imgList.forEach(item => {      
            if (item.getAttribute('isLoad')) return;
            ob.observe(item.parentNode);
        });
    };
   
    const loadMoreData = function loadMoreData() {
        let count = 0;
        let ob2=new IntersectionObserver(changes=>{
            let change=changes[0];
            if(change.isIntersecting){
                count++;
                if(count>5){
                    // 已经加载完数据了
                    ob2.unobserve(loadMoreBox);
                    return;
                }
                queryData();
                binding();
                lazyImgsLoading();
            }
        });
        ob2.observe(loadMoreBox);    
    };
    
    return {
        init() {
            queryData();
            binding();
            lazyImgsLoading(); //延迟加载
            loadMoreData();
        }
    };
})();
fallsModel.init();
相关文章
转载自:https://juejin.cn/post/7178857480255897657