前言:

        本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽。

        本篇文章为您分析一下原生JS实现图片瀑布流效果

页面需求

 1. 图片之前拥有最小间隙

 2. 图片可以根据浏览器窗口的改变而改变

 3. 需要用到函数节流与函数防抖的知识

HTML结构


    <div class="container"></div>

    <script src="../../plugin/helpers.js"></script>    <!--函数节流与防抖要引用,   此插件请查看我的第一篇博客JS函数的节流与防抖-->
    <script src="../../plugin/waterfall.js"></script>  <!--瀑布流代码-->

CSS 样式

JS 行为

JS大致思路
 1. 根据用户传入的配置信息设置图片信息

 2. 配置默认值

 3. 设置页面的图片

 4. 设置父级元素的定位信息

 5. 设置每张图片的left、top值
   <1>.计算一行有几张图片
      定义一个数组,这个数组用来记录每一列下一张的top值
      初始值为[0,0,0,n...]; 数组的每一项为0

   <2>. 设置图片位置时
    (1).得到数组中最小的值,设置top
    (2).更新数组中该项的top值
    (3).得到该项是数组中的第几项,用于计算该项的left值     

 5. 获得水平方向上的距离信息(一行排几张图、最小间隙)

 6. 设置图片的left、top值

    <script>
        // 因为我本地有40张以img开头的所以我直接循环来获得。
        var srcs = [];
        for (var i =0;i <=40; i++ ){
            srcs.push(`img/${i}.jpg`);
        }
        // 用户可以自行配置参数
        myPlugin.createWaterFall({
            minGap: 15,        // 垂直方向的最小间隙
            imgSrcs: srcs,     // 图片的路径数组
            imgWidth: 220,     // 单张图片的宽度
            container: document.querySelector(".container")  // 需要渲染的容器
        });
    </script>


/**
 * 第一步: 创建一个图片瀑布流
 * @param {*} option 参数配置
 */
window.myPlugin.createWaterFall = function (option) {
    // option默认值
    var defaultOption = {
        minGap: 10,               //  图片最小间隙
        imgSrcs: [],              //  图片的路径数组
        imgWidth: 220,            //  单张图片的宽度
        container: document.body  //  需要渲染的容器,如果用户没有传,默认为body
    }
    // 第二步: 对象混合
    var option = Object.assign({}, defaultOption, option);
    // console.log(option);
    var imgs = [];  // 存放所有的图片dom对象。
}

createImg();
// setFatherPosition();

/**
* 第三步: 创建图片
*/
function createImg() {
    for (var i = 0; i < option.imgSrc.length; i++) {
        var img = document.createElement("img"); // 创建图片
        img.src = option.imgSrc[i];              // 设置图片路径
        img.style.width = option.imgWidth + "px";// 设置每张图片宽度
        img.style.position = "absolute";         // 设置图片为绝对定位
        imgs.push(img);                          // 添加到图片数组中
        option.container.appendChild(img);       // 添加图片到container中
    }
}
如果我们把他设置为绝对定位他是这样的

接下来我们要设置父级的定位
    /**
     * 第四步: 处理父元素,因为图片都是绝对定位的,父元素必须是一个定位元素。
     */
    function handleParent() {
        var style = getComputedStyle(option.container);  // 获取到父级的最终定位
        if (style.position === "static") {               // 如果父级没有定位 (为何要如此定位,因为万一父级有定位的情况下,我们要保证他不被影响)
            option.container.style.position = "relative";// 设置父级为相对定位
        }
    }

接下来我们要设置每张图片的定位
那么,我们要知道一行内有多少张图?
还有他们之间的最小间隙

    /**
    * 第五步: 得到图片水平方向上的信息。
    */
    function getHorizontalInfo() {
        // 5.1 定义一个对象用来存储数据
        var obj = {};
        // 5.2 容器的宽度
        obj.containerWidth = option.container.clientWidth;
        // 5.3 计算一行有多少个图片
        obj.number = (obj.containerWidth + option.minGap) / (option.imgWidth + option.minGap);
        // 5.4 向下取整, 每行的图片只能少不能多。多了他会放不下
        obj.number = Math.floor(obj.number);
        // 5.5 重新计算每一个水平空隙
        // 总宽度-图片的数量*图片的宽度=剩余空间
        obj.gap = (obj.containerWidth - obj.number * option.imgWidth) / (obj.number - 1);
        console.log(obj.gap);
        return obj; // 返回这个对象
    }

得到了图片水平方向上的距离之后
我们就来设置他们的top和left值
写一个setImgPosition函数

    /**
     * 第六步: 设置每一张图片的坐标
     */
    function setImgPosition() {
        // 6.1 获取水平方向信息保存到info变量中
        var info = getHorizontalInfo();
        // console.log(info);
        // 第七步: 存放每一列下一张图片的top值
        // 7.1 创建数组
        var arr = new Array(info.number);
        // 7.2 填充数组的每一项为0
        arr.fill(0);
        // 7.3 获取所有的图片
        imgs.forEach(function (img) {
            // 设置图片的坐标
            // 7.4 找到数组里面的最小值
            var minTop = Math.min.apply(null, arr);
            // 7.5 图片的纵坐标
            img.style.top = minTop + "px";
            // 7.6 更新数组的top值(最小值拿的是数组的哪一项,找到对应的列编号。再更新)
            var index = arr.indexOf(minTop);
            // 设置数组当前这一项
            //              图片当前的高度  +  垂直方向间隙
            arr[index] += img.clientHeight + info.gap;
            // 横坐标
            img.style.left = index * (option.imgWidth + info.gap) + "px";
        });
    }

运行函数setImgPosition
得到效果如下

为什么会这样呢?因为图片是异步加载的,加载完才有图片的高度
所以应该是每一张图片加载完就给他重新设置高度
因此setImgPosition函数不能在外面调用,他应该在图片的事件中调用

            img.onload = function () {
                setImgPosition();
            };

为了防止有时候有好几张图片几乎是同一时间加载完成的
调用setImgPosition函数调用的就太频繁了
因此我们要使用函数防抖来限制他的频繁调用
在createImg函数中引用下面代码:

最后一步就是浏览器窗口改变页面要发生重排
因此给container添加一个监听事件

    // 窗口尺寸变化事件
    var debounce = myPlugin.debounce(setImgPosition, 300, false);
    window.onresize = function () {
        debounce();
    }

下面附上完整代码

JS 行为


if (!window.myPlugin) {
    window.myPlugin = {};
}

/**
 * 第一步: 创建一个图片瀑布流
 * @param {*} option 参数配置
 */
window.myPlugin.createWaterFall = function (option) {
    // option默认值
    var defaultOption = {
        minGap: 10,    // 图片最小间隙
        imgSrcs: [],    // 图片的路径数组
        imgWidth: 220,  // 单张图片的宽度
        container: document.body  // 需要渲染的容器
    }
    // 第二步: 对象混合
    var option = Object.assign({}, defaultOption, option);
    // console.log(option);
    var imgs = [];  // 存放所有的图片dom对象
    // 处理父元素
    handleParent();
    // 3.1 创建图片
    createImgs();

    // 窗口尺寸变化事件
    var debounce = myPlugin.debounce(setImgPosition, 300, false);
    window.onresize = function () {
        debounce();
    }
    /**
     * 第五步: 设置每一张图片的坐标
     */
    function setImgPosition() {
        // 第七步: 获取水平方向信息
        var info = getHorizontalInfo();
        // console.log(info);
        // 7.1 存放每一列下一张图片的top值
        var arr = new Array(info.number);
        // 7.2
        arr.fill(0);
        // console.log(arr);
        // 7.3 获取所有的图片
        imgs.forEach(function (img) {
            // 设置图片的坐标
            // 7.4 找到数组里面的最小值
            var minTop = Math.min.apply(null, arr);
            // 7.5 图片的坐标
            img.style.top = minTop + "px";
            // 7.6 更新数组的top值(最小值拿的是数组的哪一项,找到对应的列编号。再更新)
            var index = arr.indexOf(minTop);
            // 设置数组当前这一项
            //              图片当前的高度  +  垂直方向间隙
            arr[index] += img.clientHeight + info.gap;
            // 横坐标
            img.style.left = index * (option.imgWidth + info.gap) + "px";
        });
        // 设置容器的高度
        var maxTop = Math.max.apply(null, arr);
        option.container.style.height = maxTop - info.gap + "px";

    }

    /**
     * 第六步: 得到图片水平方向上的信息。
     */
    function getHorizontalInfo() {
        // 6.1
        var obj = {};
        // 6.2 容器的宽度
        obj.containerWidth = option.container.clientWidth;
        // 6.3 计算一行有多少个图片
        obj.number = (obj.containerWidth + option.minGap) / (option.imgWidth + option.minGap);
        // 6.4 向下取整, 每行的图片只能少不能多。
        obj.number = Math.floor(obj.number);
        // 6.5 重新计算每一个水平空隙
        // 总宽度-图片的数量*图片的宽度=剩余空间
        obj.gap = (obj.containerWidth - obj.number * option.imgWidth) / (obj.number - 1);
        console.log(obj.gap);
        return obj;
    }
    /**
     * 第三步: 创建图片
     */
    function createImgs() {
        // 函数节流
        var debounce = myPlugin.debounce(setImgPosition, 50, false);
        // 循环图片路径数组
        for (var i = 0; i < option.imgSrcs.length; i++) {
            var img = document.createElement("img");
            img.src = option.imgSrcs[i];
            img.style.width = option.imgWidth + "px";
            img.style.position = "absolute";
            img.style.transition = ".5s";
            imgs.push(img);
            img.onload = function () {
                // 设置图片元素的坐标
                debounce();
            }
            option.container.appendChild(img);
        }
    }

    /**
     * 第四步: 处理父元素,因为图片都是绝对定位的,父元素必须是一个定位元素。
     */
    function handleParent() {
        // 如果父元素不是定位元素,则将其变为相对定位元素
        var style = getComputedStyle(option.container);
        // console.log(style.position);
        if (style.position === "static") {
            option.container.style.position = "relative";
        }
    }
}

结语

整完!!!

js 实现图片瀑布流效果,可更改配置参数 带完整版解析代码[waterFall.js]的更多相关文章

  1. js 实现淘宝无缝轮播图效果,可更改配置参数 带完整版解析代码[slider.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS写淘宝无缝轮播图效果 需求分析: ...

  2. js 实现淘宝放大镜功能,可更改配置参数 带完整版解析代码[magnifier.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS写淘宝放大镜效果 基本功能: 运 ...

  3. js 实现文字滚动功能,可更改配置参数 带完整版解析代码。

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS写文字滚动效果 需求分析: 需要 ...

  4. js 实现对象的混合与克隆效果,带完整版解析代码[helpers.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS写淘宝无缝轮播图效果 对象混合 ...

  5. js图片瀑布流效果

    要实现图片瀑布流效果,首先得准备几张图片. html的部分比较简单就是将图片加载到浏览器就可以了 代码如下(注意放的图片多一点要不然之后无法滑动鼠标就无法达到瀑布流效果): <!DOCTYPE ...

  6. 利用LruCache载入网络图片实现图片瀑布流效果(改进版)

    PS: 2015年1月20日21:37:27 关于LoadImageAsyncTask和checkAllImageViewVisibility可能有点小bug 改动后的代码请參见升级版本号的代码 ht ...

  7. js 图片瀑布流效果实现

    /** * Created by wwtliu on 14/9/5. */$(document).ready(function(){ $(window).on("load",fun ...

  8. JS实现动态瀑布流及放大切换图片效果(js案例)

    整理了一下当时学js写的一些案例,再次体验了一把用原生JS实现动态瀑布流效果的乐趣,现在把它整理出来,需要的小伙伴可以参考一下. 该案例主要是用HTML+CSS控制样式,通过JS实现全局瀑布流以及点击 ...

  9. 纯 css column 布局实现瀑布流效果

    原理 CSS property: columns.CSS属性 columns 用来设置元素的列宽和列数. 兼容性 chrome 50+ IE 10+ android browser 2.1+ with ...

随机推荐

  1. lly的瞬移方块(并查集)

    lly的瞬移方块 Description llyllylly最近发明了一个叫瞬移方块的游戏,为啥llyllylly这么闲呢,这得从一只蝙蝠说起..... llyllylly决定给大家也分享一下这个游戏 ...

  2. RabbitMQ的高可用集群部署

    RabbitMQ的高可用集群部署 标签(空格分隔): 消息队列 部署 1. RabbitMQ部署的三种模式 1.1 单一模式 单机情况下不做集群, 仅仅运行一个RabbitMQ. # docker-c ...

  3. 本地Vue项目跨域请求本地Node.js服务器的配置方法

    前言:跨域请求是在本地开发时经常遇到的需求,也很简单,只是几句代码配置一下的问题.我初次配置跨域请求时由于官方的说明太简洁,找到的教程又落伍,调试了一番并没有解决问题,到最后解决问题,已花费了很多时间 ...

  4. 【php】php操作MySQL数据库

    一.操作步骤: 1. 连接MySQL数据库并判断是否连接成功2. 选择数据库3. 设置字符集4. 准备SQL语句5. 向MySQL服务发送SQL语句6. 解析处理结果集7. 释放结果集,关闭数据库连接 ...

  5. XSS(跨站脚本攻击)简单讲解

    1.1 XSS简介 跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞.这类漏洞能够使得攻击者嵌入恶意脚本代码(一般是JS代码)到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意 ...

  6. Array(数组)对象-->数组遍历

    1.数组的遍历: 方法1:使用for循环语句 /*定义数组*/ var arr=[1,2,3,4,5]; /*遍历*/ for (var i = 0;i<arr.length;i++){ con ...

  7. 【python系统学习14】类的继承与创新

    目录: 目录: [toc] 类的继承 子类和父类 继承的写法 继承示例 父类可以被无限个子类所继承 子类实例可调用父类属性和方法 类的始祖(根类) 根类 - object 实例归属判断 - isins ...

  8. 03-css3中的3D转换

    一.CSS3-3D转换 1.3D 特点:近大远小,物体和面遮挡不可见 1.1三维坐标系 x 轴:水平向右 -- x 轴右边是正值,左边是负值 y 轴:垂直向下 -- y 轴下面是正值,上面是负值 z ...

  9. 获取SVG中g标签的宽度高度及位置坐标

    1. 问题的出现 对于普通的HTML元素,有很多获得其宽度width.高度height.距左left.距顶top等属性的方法: 类似offsetWidth,clientWidth,width之类的,通 ...

  10. MySQL的事务隔离级别是什么?

    我是平也,这有一个专注Gopher技术成长的开源项目「go home」 背景介绍 想必事务大家都已经非常熟悉了,它是一组SQL组成的一个执行单元,要么全执行要么全不执行,这也是它的一个特性--原子性. ...