前言:

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

        本篇文章为您分析一下原生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. 【Net】ABP框架学习之它并不那么好用

    前言 上一篇文章介绍了ABP的Web API,本文在继续介绍ABP的其他内容. 在ABP中,WEBAPI是一个值得用的东西.但其他东西,就不一定是那么好用了. 下面我们看一下ABP的Controlle ...

  2. 除了chrome、Firefox之外其他浏览器全都连不上网

    在调试jsp时,总是会遇到eclipse打开jsp网页失败,没有网络,浏览器也除了chrome.Firefox之外其他浏览器全都连不上网,这里我也不清楚是什么问题,但是解决方法是: 打开Interne ...

  3. 简单记录下RestTemplate 中postForObject调用例子

    学无止境! 今天无意中做了下RestTemplate调用demo,简单的尝试了下一个项目调用另一个项目接口示例 在A项目中创建可访问controller 然后在B项目中进行调用 调用成功

  4. stylus--安装及使用方法

    stylus介绍 Stylus 是一个CSS的预处理框架,2010年产生,来自Node.js社区,主要用来给Node项目进行CSS预处理支持,所以 Stylus 是一种新型语言,可以创建健壮的.动态的 ...

  5. Redis设计与实现笔记 - hash

    基本结构如下 初始状态一直使用 dictht[0],即 0 号哈希表 在发生扩容 rehash的时候,开始渐进式向 dictht[1]哈希表转移, 转移完成后交换 dicth[0] 与 dictht[ ...

  6. 为什么MySQL要用B+树?聊聊B+树与硬盘的前世今生【宇哥带你玩转MySQL 索引篇(二)】

    为什么MySQL要用B+树?聊聊B+树与硬盘的前世今生 在上一节,我们聊到数据库为了让我们的查询加速,通过索引方式对数据进行冗余并排序,这样我们在使用时就可以在排好序的数据里进行快速的二分查找,使得查 ...

  7. 曹工说Redis源码(3)-- redis server 启动过程完整解析(中)

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  8. Spring(一):Spring入门程序和IoC初步理解

    本文是按照狂神说的教学视频学习的笔记,强力推荐,教学深入浅出一遍就懂!b站搜索狂神说或点击下面链接 https://space.bilibili.com/95256449?spm_id_from=33 ...

  9. 微信小程序placeholder设置自定义颜色

    原地址链接:https://blog.csdn.net/august_leo/article/details/80877382 这是微信小程序input组件的官方文档描述,下图红框里的placehol ...

  10. (js描述的)数据结构[哈希表1.2](9)

    一. 优秀的哈希函数 1.快速的计算: 需要快速的计算来获得对应的hashCode(霍纳法则来减少乘除次数) 2.均匀的分布: 尽可能将元素映射到不同的位置,让元素在哈希表中均匀分布 二.哈希表的扩容 ...