前言:

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

        本篇文章为您分析一下原生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. P1345 [USACO5.4]奶牛的电信(点拆边 + 网络最小割)

    题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流.这些机器用如下的方式发送电邮:如果存在一个由c台电脑组成的序列a1,a2,-,a©,且a1与a2相连,a2 ...

  2. 在 UITextField 中添加删除绑定(绑定删除)

    要解决的问题 在输入框中,需要整体删除诸如 “xxx@xx.com” 或 “@xxxx” 等文本 实现思路 在删除动作时,获取到当前光标的位置,如果在光标正在处在上述文本范围内,就删除一整串文本 如何 ...

  3. 字符串学习笔记(三)---- StringBuilder

    一.前言 StringBuilder是jdk1.5后出现的,而StringBuffer是jdk1.0就出现了,并且在功能上俩者并无太大区别.但为什么后来要添加一个StringBuilder呢?这是为了 ...

  4. 【tensorflow2.0】自动微分机制

    神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情. 而深度学习框架可以帮助我们自动地完成这种求梯度运算. Tensorflow一般使用梯度磁带tf.Gradi ...

  5. 使用gulp自动构建项目

    网址:https://segmentfault.com/a/1190000011514257

  6. 1005 Spell It Right (20 分)

    Given a non-negative integer N, your task is to compute the sum of all the digits of N, and output e ...

  7. JVM 理解性学习(一)

    重新学习,重新理解 1.类加载过程等 验证:.class 文件加载到 JVM 里的时候,会验证下该文件是否符合 JVM 规范. 准备:给实体类分配内存空间,以及给类变量(static 修饰)分配&qu ...

  8. 如何在 Array.forEach 中正确使用 Async

    本文译自How to use async functions with Array.forEach in Javascript - Tamás Sallai. 0. 如何异步遍历元素 在第一篇文章中, ...

  9. 详细解析 HBASE 配置的各种要点

    文章更新于:2020-04-06 安装惯例,需要的文件附上链接放在文首. 文件名:hbase-2.2.4-bin.tar.gz 文件大小:213.24 MB 下载链接:http://download. ...

  10. PHP中嵌入正则表达式常用的函数

    PHP中嵌入正则表达式常用的函数有四个: 1.preg_match() :preg_match() 函数用于进行正则表达式匹配,成功返回 1 ,否则返回 0 . 语法:int preg_match( ...