欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

图片横向等高瀑布流,每行占满,限制行数 的实现

程序员文章站 2024-01-02 10:29:52
图片的横向瀑布流,其实简单地按顺序排列就可以了 但要实现每行中各图片都等高(各行不一定等高,但每行里面等高),且每行都占满,就需要用到flex的特性了 控制每行图片高度都一致,可能会影响图片的比例,所以不能简单暴力地设置高度,需要按比例来动态计算 另外,如要限制图片展示的行数,则只需判断好每行总高度 ......

图片的横向瀑布流,其实简单地按顺序排列就可以了

但要实现每行中各图片都等高(各行不一定等高,但每行里面等高),且每行都占满,就需要用到flex的特性了

控制每行图片高度都一致,可能会影响图片的比例,所以不能简单暴力地设置高度,需要按比例来动态计算

另外,如要限制图片展示的行数,则只需判断好每行总高度与容器总高度的关系即可

这里就来实现一下这个小功能

 

图片横向等高瀑布流,每行占满,限制行数 的实现

 

因为都是假数据的关系,图片的宽高值是随机数,并非原图宽高值,仅作参考

看完上面那张大大的图,先想一下可以怎么实现..

 

 

要实现每行都能够占满,需要用到 flex-grow 这个属性

flex-grow基于flex-basis基准值来计算,而flex-basis则基于项目的width、min|max-width相关的值来计算,或者手动定义

使用flex-grow可以分配按比例分配主轴的剩余空间

如果有10张图片需要放置,第一行仅可以放置四张图片,剩余100px的空间,那么各图片的flex-grow可以直接配置成图片的宽度width值,即可很方便精准地分配好这剩余的空间

第二行可以放五张图片,剩余n px的空间... 按照这种计算方式来铺满每一行

<h1 class="get-latest-update">
    <a href="javascript:;">获取最近更新</a>
</h1>

<div class="img-items"></div>

<script type="text/template" id="img-item-tpl">
    <div class="img-item" style="flex-grow: {{width}}; width: {{width}}px;">
        <a href="#/img/{{id}}" style="padding-top: {{paddingtop}}%;">
            <img data-src="{{src}}" src="{{src}}" width="100%" height="100%">
        </a>
    </div>
</script>

上面页面模板中,flex-grow 与 width的值一致,用以按比例分配每行剩余空间

另外可以看到这里有个 padding-top 的百分比值

我们都知道  padding-top 的百分比值是基于父元素的宽度来计算的,根据盒模型,一般这种计算方式是为了获取固定宽高比

当父元素有宽度,但高度为0时,整体高度则由padding-top值来撑开,则父元素就有了一个设定的宽高比,

同时我们将子元素(这里是图片)position值设置为absolute,宽高占满父元素,则子元素图片也有了一定的宽高比,实现按比例的图片缩放

 

来看看对应的样式设置

body {
    background-color: #f2f2f2;
}

.get-latest-update {
    font-size: 20px;
    cursor: pointer;
    
    > a {
        color: #0183fd;
        text-decoration: none;
    }
}

.img-items {
    display: flex;
    flex-wrap: wrap;
    overflow: hidden;
}

.img-item {
    margin-right: 10px;
    margin-bottom: 10px;
    background-color: #fff;
    box-shadow: 0 0 10px #ddd;
    
    > a {
        position: relative;
        display: block;
        width: 100%;
    }
    
    img {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
    }
}

 

那么,这个width和padding-top的该怎么计算出来呢

核心代码是

// 图片预定义的高度
    var baseheight = 200;
    
    for (var i = 1; i <= num; ++i) {
        var w = getrandom(width.min, width.max);
        var h = getrandom(height.min, height.max);
        
        imgs.push({
            id: i,
            src: imgsrcbase[math.floor(i / 10)] + (i % 10 + 1) + '.jpg',
            // 设置图片的宽度,需根据预定义的高度值来做好比例处理
            // 为了让每行各图片按自身宽度自动flex-glow,同时利用这个比例处理保证每行图片的高度一致
            width: w * baseheight / h,        
            height: h,
            // padding-top的百分比,用以基于父元素宽度设置该元素的高度
            // 为了保证图片宽高按比例
            paddingtop: h / w * 100
        });
    }

paddingtop的值,按照以下这个映射关系来看就好

容器高度 == 容器宽度 * paddingtop %

最终会形成

容器高度 == 图片高度

容器宽度 == 图片宽度

所以 

图片高度 == 图片宽度 * paddingtop %

 

width值的计算可能比较绕

假设这里 width直接取 图片宽度w值,就会出现一行中图片高度不一致的情况

因为最终的图片高度即为容器的高度,而容器的高度是由容器宽度决定的(注意这里的paddingtop值已经确定),而容器宽度就是由这里的width来决定的。图片宽度的不同,就直接导致了最终高度的不同

所以,为了确保图片高度一致,假设有三张图片 50*50  100*100  50*150  放在了同一行中,flex布局会将三张图片所在容器的高度自适应为最高的那个150,如果flex-grow值起作用了,这个最高值还会再多一些

我们可以考虑最简单的情况,正好放满一行。那么最终三张图片的高度都应该为150,按照各自的图片比例来调整,则最终第一张图片宽度的计算  50 / 50 === width / 150 , 则 width = 50 / 50 * 150

可能有些行最高的图片还是不够高,为了也能够显示出比较大的图片,我们还可以定义好这个基准高度值,比如 baseheight设置为 200

所以,最终每一张图片的宽度width值为 w / h * baseheight

 

图片横向等高瀑布流,每行占满,限制行数 的实现

 

 还要一个问题,如何实现只显示三行

显示三行,每行的图片数量不固定,这是通过flex布局自动排列每一行的,都会经过 基本排列 -> 分配剩余空间 的步骤

目前想到的方法是对每一行的容器所占位置进行累加,最后对比即可

不过这种方式会有比较大的性能损耗,看还能不能有更优雅的做法吧

// 设置显示的图片行数
function setlinelimit(num) {
    // 内容区宽度
    var contentwidth = $('.img-items').outerwidth();
    // 定义的外边距
    var marginwidth = 10;
    // 每行宽度
    var curwidth = 0;
    // 行标识
    var lineindex = 1;
    
    // 初始需将图片设置为可见,否则flex无法自适应排版
    $('.img-item').show()
        .each(function() {
            var $item = $(this);
            var itemwidth = $item.outerwidth();
            
            // 隐藏多余的行
            if (lineindex > num) {
                $item.hide();
                return;
            }
            
            $item.show();
            
            // 某一行
            if (curwidth + itemwidth + marginwidth <= contentwidth + marginwidth) {
                curwidth += itemwidth + marginwidth;
            }
            // 下一行
            else {
                ++lineindex;
                curwidth = itemwidth;
                
                if (lineindex > num) {
                    $item.hide();
                }
            }
        });
}

主要注意的点是,为了兼顾视窗缩放的过程中,自动排列也能照常进行,在计算的时候需要将每个项先显示出来,再进入计算环节

// 视窗缩放时处理可视的图片
$(window).resize(throttle(setlinelimit.bind(this, 3), 200));

 

完整js代码

图片横向等高瀑布流,每行占满,限制行数 的实现
  1 // 事件绑定
  2 function addevent(elem, type, handler) {
  3     elem.addeventlistener(type, handler, false);
  4 }
  5 
  6 function qs(selector) {
  7     return document.queryselector(selector);
  8 }
  9 
 10 function qsa(selectors) {
 11     return document.queryselectorall(selectors);
 12 }
 13 
 14 // 函数节流,频繁操作中间隔 delay 的时间才处理一次
 15 function throttle(fn, delay) {
 16     delay = delay || 200;
 17 
 18     var timer = null;
 19     // 每次滚动初始的标识
 20     var timestamp = 0;
 21 
 22     return function () {
 23         var arg = arguments;
 24         var now = date.now();
 25 
 26         // 设置开始时间
 27         if (timestamp === 0) {
 28             timestamp = now;
 29         }
 30 
 31         cleartimeout(timer);
 32         timer = null;
 33 
 34         // 已经到了delay的一段时间,进行处理
 35         if (now - timestamp >= delay) {
 36             fn.apply(this, arg);
 37             timestamp = now;
 38         }
 39         // 添加定时器,确保最后一次的操作也能处理
 40         else {
 41             timer = settimeout(function () {
 42                 fn.apply(this, arg);
 43                 // 恢复标识
 44                 timestamp = 0;
 45             }, delay);
 46         }
 47     }
 48 }
 49 
 50 // 获取随机数
 51 function getrandom(min, max) {
 52     return math.round(math.random() * (max - min + 1) + min);
 53 }
 54 
 55 // 构造图片数据
 56 function createmockimgs(num) {
 57     var imgs = [];
 58     
 59     // 图片宽高数据范围
 60     var width = {
 61         min: 50,
 62         max: 200
 63     };
 64     
 65     var height = {
 66         min: 150,
 67         max: 300
 68     };
 69     
 70     // 图片源
 71     var imgsrcbase = [
 72         'http://www.deskcar.com/desktop/movietv/2009/2009227225145/',
 73         'http://www.deskcar.com/desktop/fengjing/2017418153624/',
 74         'http://www.deskcar.com/desktop/fengjing/2017418153624/',
 75         'http://www.deskcar.com/desktop/fengjing/2017418153624/',
 76         'http://www.deskcar.com/desktop/fengjing/2017418153624/',
 77         'http://www.deskcar.com/desktop/else/20161228125639/',
 78         'http://www.deskcar.com/desktop/fengjing/2017418153446/'
 79     ];
 80     
 81     // 图片预定义的高度
 82     var baseheight = 200;
 83     
 84     for (var i = 1; i <= num; ++i) {
 85         var w = getrandom(width.min, width.max);
 86         var h = getrandom(height.min, height.max);
 87         
 88         imgs.push({
 89             id: i,
 90             src: imgsrcbase[math.floor(i / 10)] + (i % 10 + 1) + '.jpg',
 91             // 设置图片的宽度,需根据预定义的高度值来做好比例处理
 92             // 为了让每行各图片按自身宽度自动flex-glow,同时利用这个比例处理保证每行图片的高度一致
 93             width: w * baseheight / h,        
 94             height: h,
 95             // padding-top的百分比,用以基于父元素宽度设置该元素的高度
 96             // 为了保证图片宽高按比例
 97             paddingtop: h / w * 100
 98         });
 99     }
100     
101     return imgs;
102 }
103 
104 // 视窗缩放时处理可视的图片
105 $(window).resize(throttle(setlinelimit.bind(this, 3), 200));
106 
107 // 设置显示的图片行数
108 function setlinelimit(num) {
109     // 内容区宽度
110     var contentwidth = $('.img-items').outerwidth();
111     // 定义的外边距
112     var marginwidth = 10;
113     // 每行宽度
114     var curwidth = 0;
115     // 行标识
116     var lineindex = 1;
117     
118     // 初始需将图片设置为可见,否则flex无法自适应排版
119     $('.img-item').show()
120         .each(function() {
121             var $item = $(this);
122             var itemwidth = $item.outerwidth();
123             
124             // 隐藏多余的行
125             if (lineindex > num) {
126                 $item.hide();
127                 return;
128             }
129             
130             $item.show();
131             
132             // 某一行
133             if (curwidth + itemwidth + marginwidth <= contentwidth + marginwidth) {
134                 curwidth += itemwidth + marginwidth;
135             }
136             // 下一行
137             else {
138                 ++lineindex;
139                 curwidth = itemwidth;
140                 
141                 if (lineindex > num) {
142                     $item.hide();
143                 }
144             }
145         });
146 }
147 
148 var mockimgs = createmockimgs(60);
149 
150 console.log(mockimgs);
151 
152 // 点击渲染
153 addevent(qs('.get-latest-update'), 'click', function() {
154     renderlist(mockimgs);
155     setlinelimit(3);
156 });
157 
158 var itemtpl = qs('#img-item-tpl').innerhtml;
159 var itemsdom = qs('.img-items');
160 
161 /**
162 * 渲染数据
163 * @param  {[type]} data [description]
164 * @return {[type]}      [description]
165  */
166 function renderlist(data) {
167     var html = '';
168     var fragment = document.createdocumentfragment();
169 
170     data.foreach(function(item) {
171         var divtemp = document.createelement('div');
172 
173         // 模板替换
174         divtemp.innerhtml = itemtpl.replace(/{{(\w+)}}/g, function(input, match) {
175             return match ? item[match] || '' : '';
176         });
177 
178         fragment.appendchild(divtemp.firstelementchild);
179     });
180 
181     // 渲染
182     itemsdom.appendchild(fragment);
183 }
view code

 

上一篇:

下一篇: