likes
comments
collection
share

瀑布流之window.onscroll+函数节流版

作者站长头像
站长
· 阅读数 23

瀑布流布局

  • 内容框宽度固定,高度不固定。

  • 内容框从左到右排列,一行排满后,其余内容框就会按顺序排在短的一列后。

实现效果

瀑布流之window.onscroll+函数节流版

实现思路

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

方案 window.onscroll+用到了函数节流

  • window.onscroll+用到了函数节流

    • ONSCROLL事件 会在浏览器最快反应事件内触发一次 (谷歌:5-7MS IE10~17MS) 这样频率太快了

    • 我们需要降低它的频率,【方案:函数的节流】

图片的懒加载思路图解

瀑布流之window.onscroll+函数节流版

加载更多思路图解

瀑布流之window.onscroll+函数节流版

思路

  • 获取元素

    • 最外层容器

    • 每一列

    • 放图片的数组

  • 工具类方法:offset 获取当前元素距离BODY的偏移(top/left)

    • 拿到元素的副参照物

    • 拿到当前元素距离其父参照物的左偏移/上偏移

    • 如果还存在父参照物

    • 重复之前的操作,直到找到BODY为止

    • 返回top/left的值

  • 工具类方法:函数节流处理

  • 从服务器获取数据

    • ajax
  • 动态渲染数据

    • 瀑布流规则 每一次从数据中取出3条数据(group) 三列(colums)

    • 三条数据,按照图片高度排序[小,中,大]三列盒子,按照高度进行排序[大,中,小]

    • 服务器获取到的图片宽度高度,按照真实渲染的宽度(230)动态计算出真实渲染的高度

    • 服务器获取到的宽高 width:300 height:433 300/433 宽高比

    • 真实渲染的宽度为width:230px 求真实渲染的高度? 高度为 230/300/433

    • 用数组中的map方法,算出来真实渲染的高度

    • 再把更改完的元素返回

    • 如果不想生成块级上下文 则写在外面 不想产生闭包

    • 从当前索引 取3项

    • 按照图片高度 从小到大排序

    • 按照盒子的高度 从大到小排序

    • 把排好序的数据依次插入到对应的排好序的列中

    • 动态创建每一个图片盒子 插入到指定列

    • 数据绑定完成后,获取到所有的IMG

  • 图片的延迟加载

    • 单张图片

      • 传递的Img就是需要加载的图片对象

      • 加载成功:图片地址是正确的

      • 更改透明度

      • 处理过的IMG设置已经处理过的标识

    • 循环图片集合 进行懒加载

      • ONSCROLL事件 会在浏览器最快反应时间内触发一次 (谷歌:5-7MS IE10-17MS) 这样频率太快了

      • 我们需要降低它的频率,【方案:函数的节流】

      • 已经处理过的IMG就无需判断了

      • item 当前迭代图片

      • 拿到父元素盒子

      • 图片盒子距离Body的上偏移

      • 窗口视口距离Body的上偏移

      • 当图片盒子距离Body的上偏移小于或者等于窗口视口距离Body的上偏移

      • 触发单个图片函数懒加载,加载图片

  • 加载更多数据

    • HTML.scrollTop+HTML.clientHeight+100 =>卷去的高度+可视窗口高度+100(误差)

    • HTML.scrollHeight页面真实的高度约等于

    • 当HTML.scrollTop+HTML.clientHeight+100>=HTML.scrollHeight时,说明滚动到底部了

    • 此时让count++

    • 模拟加载5次,数据加载完不再进行处理

  • init里面的函数

    • 获取数据

    • 动态绑定数据 按照瀑布流规则

    • 延迟加载

代码实现

let fallsModel = (function () {
    let fallsBox = document.querySelector('#fallsBox'),
        columns = Array.from(document.querySelectorAll('.column')),
        imgList = [];

    let data = [];
    const offset = function offset(element) {
        let parent = element.offsetparent,
            top = element.offsetTop,
            left = element.offsetLeft;

        while (parent) {
            top += parent.clientTop + parent.offsetTop;
            left += parent.clientLeft + parent.offsetLeft;
            parent = parent.offsetparent;
        }
        return {
            top,
            left
        }
    };
    const throttle = function throttle(func, wait) {
        if (typeof func !== "function") throw new TypeError('func must be an function');
        if (typeof wait !== "number") wait = 300;
        let timer,
            previous = 0;
        return function proxy(...params) {
            let now = +new Date(),
                remaining = wait - (now - previous),
                self = this,
                result;
            if (remaining <= 0) {
                if (timer) {
                    clearTimeout(timer);
                    timer = null;
                }
                result = func.call(self, ...params);
                previous = now;
            } else if (!timer) {
                timer = setTimeout(() => {
                    if (timer) {
                        clearTimeout(timer);
                        timer = null;
                    }
                    result = func.call(self, ...params);
                    previous = +new Date();
                }, remaining);
            }
            return result;
        };
    }; 
    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); 
            //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;
                let imgBox = item.parentNode, 
                    HTML = document.documentElement;
            A = imgBox.offsetHeight + offset(imgBox).top;
            B = HTML.clientHeight + HTML.scrollTop;
            if (A <= B) {
                singleImgLazy(item); 
            };
        });
    };
    let count=0;
    const loadMoreData=function loadMoreData(){
        let HTML=document.documentElement;
        if(HTML.scrollTop+HTML.clientHeight+100>=HTML.scrollHeight){
            count++;
            if(count>5)return; 
            queryData();
            binding();
            lazyImgsLoading();
        }
    };
    return {
        init() {
            queryData();
            binding();
            lazyImgsLoading();
            window.onscroll =throttle(()=>{
                lazyImgsLoading();
                loadMoreData();
            },500); //滚动的时候 500MS
        }
    };
})();
fallsModel.init();

相关文章

图片懒加载

IntersectionObserver交叉观察器

转载自:https://juejin.cn/post/7179927970126463037
评论
请登录