zoukankan      html  css  js  c++  java
  • 图片横向等高瀑布流,每行占满,限制行数 的实现

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

    但要实现每行中各图片都等高(各行不一定等高,但每行里面等高),且每行都占满,就需要用到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}}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,同时利用这个比例处理保证每行图片的高度一致
                 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              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
  • 相关阅读:
    cocos2dx 3.x(获取当前系统时间)
    cocos2dx 3.x(加载cocostudio进度条)
    cocos2dx 3.x(Button传统按钮)
    cocos2dx 3.x以上(Sprite精灵类的相关属性与创建)
    cocos2dx 3.x版本搭建Mac环境工程(创建一个新的C++工程)百分百可行
    iOS
    iOS -Swift 3.0 -Array(数组与可变数组相关属性及用法)
    iOS -Swift 3.0 -String(字符串常规用法)
    iOS -Swift 3.0 -UIButton属性大全
    iOS -Swift 3.0 -UILabel属性大全
  • 原文地址:https://www.cnblogs.com/imwtr/p/10051995.html
Copyright © 2011-2022 走看看