原生JS实现各大网站的瀑布流

    技术2025-10-30  4

    单张图片加载

    确定图片加载的临界条件校验一下图片路径是否正确 <style> * { padding: 0; margin: 0; } div { width: 300px; height: 200px; margin: 800px auto; } img { width: 100%; height: 100%; } </style> <body> <div> <img src="./img/default.jpg" trueImg="./img/1.jpg" alt=""> </div> <script src="../../js/utils.js"></script> <script> let div = document.getElementsByTagName('div')[0]; let img = document.getElementsByTagName('img')[0]; /* 确定图片加载的临界条件: 让图片的下边框和可视窗口的下边框重合时,就该让图片的真实路径显示了(图片该加载了) 转化为代码:滚动条卷去的高度+当前可视窗口的高度 === 图片的下边框到body顶部的距离(图片自身的高度+图片的上偏移量) */ let winH = utils.win('clientHeight') // 当前可视窗口的高度 let curH = img.offsetHeight; // 图片的总高度 let cutT = utils.offset(img).top // 图片的上偏移量 window.onscroll = function () { if (img.flag) { return } let winT = utils.win('scrollTop'); // console.log(winT + winH, curH + cutT); if (winT + winH > curH + cutT) { let address = img.getAttribute('trueImg'); // 拿到图片身上的真实路径 // img.src = address // 把图片的真实路径赋值给img /* 当我给图片赋值真实路径的时候,先校验一下这个路径是否正确,如果正确,你在给人家赋值路径 */ // 动态创建一个img元素,把真实的路径赋值给他,如果这个路径是正确的,那这个图片就会被读取,那onload属性绑定的方法就会被执行 let newImg = new Image; newImg.src = address; newImg.onload = function () { // 这个方法执行,说明路径是正确的,在把真实路径赋值给咱们的图片 img.src = address; fadeIn(img); // 让图片渐渐显示 img.flag = true; // 图片一旦加载成功,就给当前图片元素增加一个自定义属性flag,属性值是true,防止图片再次被加载 } } } function fadeIn(img) { // 图片如果直接出来,会很突兀,咱们利用opacity让图片慢慢出来; utils.setCss(img, 'opacity', 0.1); // 咱们直到元素的透明度默认是1,上来先给他设置为0.1 let cur = Number(utils.getCss(img, 'opacity')); // 这个数值是要被累加的,为了防止字符串拼接的出现,给他转数字 // 获取元素的透明度,然后在此基础上利用定时器不断累加 // 定时器执行一次,透明度就会被重新被赋值一次 var timer = setInterval(() => { cur += 0.2; if (cur >= 1) { clearInterval(timer) } utils.setCss(img, 'opacity', cur); }, 200) } </script> </body>

    多张图片加载

    <style> * { padding: 0; margin: 0; } div { margin: 500px auto; width: 300px; } img { display: block; width: 300px; height: 200px; margin-bottom: 10px; } </style> <body> <div> <img src="./img/default.jpg" trueImg = "./img/1.jpg" alt=""> <img src="./img/default.jpg" trueImg = "./img/2.jpg" alt=""> <img src="./img/default.jpg" trueImg = "./img/3.jpg" alt=""> <img src="./img/default.jpg" trueImg = "./img/4.jpg" alt=""> <img src="./img/default.jpg" trueImg = "./img/5.jpg" alt=""> <img src="./img/default.jpg" trueImg = "./img/6.jpg" alt=""> </div> <script src="../../js/utils.js"></script> <script> let imgs = document.getElementsByTagName('img'); let winH = utils.win('clientHeight'); // 当前屏幕的高度 // 此方法每执行一次,就会把每一个图片都判断一下要不要显示 function dealy(){ for (var i = 0; i < imgs.length; i++) { dealyImg(imgs[i]) } } function dealyImg (img){ if(img.flag){ return } let curH = img.offsetHeight; // 图片的自身高度 let curT = utils.offset(img).top; // 图片相对于body的上偏移量 let winT = utils.win('scrollTop'); // 浏览器滚动条卷去的高度 if(winH+winT>curH+curT){ let address = img.getAttribute('trueImg'); let newImg = new Image; newImg.src = address; newImg.onload = function(){ img.src = address; img.flag = true; newImg = null; fadeIn(img) } } } function fadeIn(img){ utils.setCss(img, 'opacity', 0.05); let cur = Number(utils.getCss(img,'opacity')) ; var timer = setInterval(()=>{ cur+=0.05; if(cur>=1){ clearInterval(timer) } utils.setCss(img, 'opacity', cur); },50) } dealy() // 页面初始化的时候,要把第一屏的图片展示出来 window.onscroll = dealy </script> </body>

    瀑布流

    CSS

    <style> * { padding:0; margin: 0; } ul { list-style: none; } .container{ width: 840px; margin: auto; background: #cccccc0a; overflow: hidden; } .container ul{ width:19%; margin:0 0.5%; float:left; } .container ul li{ width:100%; margin-top:10px; box-shadow: 5px 5px 5px rgba(0,0,0,.3); } .container ul li img{ width:100%; height: 100%; display: block; background: url("images/timg.gif") no-repeat center; background-size: 60px 60px; transition: all .3s; } .container ul li:hover { box-shadow: 5px 5px 5px rgba(0,0,0,.6); } .container ul li img:hover { transform: scale(1.05); } </style> </head> <body> <div id="container" class="container"> <ul></ul> <ul></ul> <ul></ul> <ul></ul> <ul></ul> </div> <script src="utils.js"></script> <script src="./index.js"></script> </body>

    JS

    // 获取页面的5个ul,并且转换为数组 let ulList = document.getElementsByTagName('ul'); ulList = utils.toArray(ulList); console.log(ulList) //获取数据 let data = null; let xhr = new XMLHttpRequest(); xhr.open("get",'data.txt',false); xhr.onreadystatechange = function(){ if(xhr.readyState === 4 && /^2\d{2}/.test(xhr.status)){ data = JSON.parse(this.responseText); } } xhr.send(); console.log(data) // 数据绑定 function bindHtml(){ for (var i = 0; i < 50; i++) { // 1、随机生成一个索引。然后去data里面获取某一个对象 let index = Math.round(Math.random()*9); let curObj = data[index]; let li = document.createElement('li'); let img = document.createElement('img'); // 2、给img设置各种属性,然后把img插入到li元素里 img.setAttribute('trueImg', curObj.src); img.className = 'bg'; img.style.height = Math.round(Math.random()*(250-180)+180)+'px'; // 让img的高度随机生成 li.appendChild(img); // 在把li插入到页面的ul里之前,先对ul进行排序(按照ul的真实高度), // 排序之后ulList[0]每次获取的都是高度最矮的那个ul,然后将li插入到里面 // 每插入一个li,ul就会重新排一下续 ulList.sort((a,b)=> { return a.scrollHeight - b.scrollHeight // 按照页面的真实高度排序 }) // console.log(ulList) ulList[0].appendChild(li); } } bindHtml(); //数据绑定方法封装完成之后让其执行一次 var imgs = document.getElementsByClassName("bg"); /* * 此方法是循环判断每一个img需不需要加载的 */ function dealy(){ // 循环页面中所有的图片,判断每一个图片看看是否要加载 for (var i = 0; i < imgs.length; i++) { dealyImg(i); // 把当前图片的索引传递给dealyImg } } let winH = utils.win('clientHeight'); // 获取浏览器可视区域的高度(因为这个高度是不会变得,我给他拿到函数外边来) function dealyImg(index){ let curImg = imgs[index]; // 通过传递过来的索引前获取到当前的img if(curImg.flag){ // 如果图片已经加载过,那当前的img身上就会有flag属性,值为true,那以后这个图片就不需要重复加载了 return } // 判断图片是否加载的临界条件 // 图片的底边框和浏览器可视窗口的的下边框重合时,就是代表图片已经完全露出来了,需要加载了 let curH = curImg.offsetHeight // 图片的总高度 let curT = utils.offset(curImg).top // 图片距离body的上边框距离(上偏移量) let winT = utils.win('scrollTop'); // 获取浏览器卷去的高度 if(curH+curT<winT+winH){ var address = curImg.getAttribute('trueImg'); var newImg = new Image; newImg.src = address; newImg.onload = function(){ curImg.src = address; curImg.flag = true; fadeIn(curImg); } } } function fadeIn(img){ utils.setCss(img ,'opacity', 0.1); let cur = Number(utils.getCss(img,'opacity')); var timer = setInterval(()=>{ cur+=0.2; if(cur>=1){ clearInterval(timer); utils.setCss(img, 'opacity', 1); }; utils.setCss(img, 'opacity', cur); }, 200); } dealy(); // 页面初次展现的时候就第一屏的图片加载出来 /* *监听页面滚动事件 */ window.onscroll = function(){ dealy(); let bodyH = document.body.scrollHeight; // 浏览器真实的高度 let winT = utils.win('scrollTop'); // 浏览器卷去的高度 // 判断数据是否需要再次插入的临界条件是当前浏览器的底部和当前窗口内容的真实高度的底部重合时 // 浏览器滚动条卷去的高度+当前可视窗口的高度 === 当前屏幕的真实内容高度 // 那如果我给(浏览器滚动条卷去的高度+当前可视窗口的高度)提前加上300,那下边的判断条件就会提前成立,从而实现滚动条在即将触碰到页面底端的时候再次发送数据请求 if(winT+ winH+300>bodyH){ bindHtml(); } }

    utils

    工具方法库,在使用中,利用自执行函数和return引用类型值形成闭包。 // 闭包 // 单例模式 var utils = (function () { //类数组转数组 function toArray(likeAry) { var ary = []; try{ ary = Array.prototype.slice.call(likeAry); }catch(e){ for(var i=0;i<likeAry.length;i++){ // ary.push(likeAry[i]) ary[ary.length] = likeAry[i]; } }; return ary; }; function toJSON(str) { return "JSON" in window ? JSON.parse(str):eval("("+str+")"); }; function getCss(curEle,attr) { var val; if("getComputedStyle" in window){ val = getComputedStyle(curEle)[attr]; }else{ if(attr==="opacity"){ var cur = curEle.currentStyle["filter"]; // alpha(opacity=30) var reg = /alpha\(opacity=(\d+(\.\d+)?)\)/; val =reg.exec(cur)[1]/100; }else{ // 不是透明度执行此处代码 val = curEle.currentStyle[attr]; } } if(!isNaN(parseFloat(val))){ val = parseFloat(val); } return val; } function setCss(curEle,attr,value) {// curELe:元素 attr:属性 value :值 if(attr==="opacity"){ curEle.style[attr] = value; curEle.style["filter"] = "alpha(opacity="+value*100+")"; return; } var reg = /^((width|height)|((margin|padding)?(top|left|right|bottom)?))$/i; if(reg.test(attr)){ value = value + "px"; } curEle.style[attr] = value; } function setGroupCss(curEle,options) { if(typeof options==="object"){ for(var key in options){ // console.log(1); // 只循环私有属性 setCss(curEle,key,options[key]) } } } //获取css,设置css,设置一组css function css() { // 根据参数的个数以及参数的类型进行判断,执行不同的方法 var curEle = arguments[0], val = null, attr = null, length = arguments.length; if(length===3){ val = arguments[2]; attr = arguments[1]; setCss(curEle,attr,val); return; } if(length===2&&typeof arguments[1]=== "object"){ val = arguments[1]; setGroupCss(curEle,val); return; } attr = arguments[1]; return getCss(curEle,attr) }; //获取目标的左偏移量 和 上偏移量 function offset(curEle) { var l = curEle.offsetLeft; var t = curEle.offsetTop; var p= curEle.offsetParent; //offsetParent:父级参照物 (先找有没有position定位,有就是父级参照物,没有就是body) //offsetLeft:左偏移量 :从当前元素的左外边框到父级参照物的左内边框 //offsetTop:上偏移量 : 从当前元素的上外边框到父级参照物的上内边框 while(p){ if(!/MSIE 8.0/.test(navigator.userAgent)){ // 只要不是IE8浏览器,那么会进此处的判断 l +=p.clientLeft; t +=p.clientTop; } l+=p.offsetLeft; t+=p.offsetTop; p = p.offsetParent; } return {left:l,top:t} } // 传一个参数,获取html或者body的属性对应的属性值 function win(attr,value) { if(typeof value==="undefined"){ return document.documentElement[attr] || document.body[attr] } // 设置 document.documentElement[attr] = value; document.body[attr] = value; }; return { toArray : toArray, toJSON : toJSON, getCss:getCss, setCss:setCss, setGroupCss:setGroupCss, css:css, offset:offset, win:win } })();
    Processed: 0.020, SQL: 9