zoukankan      html  css  js  c++  java
  • 等高响应式布局的原理和实现

    砖墙布局

    具体原理,参考了腾讯砖墙布局的思路:http://isux.tencent.com/high-equal-response-layout-html.html

    等高布局效果图:

    如图,并不像等宽一样简单,要在不改变图片分辨率(宽高比)同时保持等高且占满行宽度,如何实现?不妨带着问题跟我走。

    1 等高响应式布局是什么?

    ①行内高度相等;
    ②行间总宽度相等;
    ③自适应宽度布局;
    ④图片分辨率(宽高比)不变;

    2 难在那里?

    ①行内高度一致,行间高度不一致,但是相差不能太多;
    ②并不知道一行需要多少个图片才能占满宽度,由于高度不确定,图片的宽度也不能等比变化;
    ③如何做到自适应?
    ④布局用于用户的个人相册,数据量是有限且未知的,如何保证图片数量满行显示?

    由上可知,这种布局涉及太多变量,而且最难的是做到图片分辨率不改变,不影响图片质量效果。

    该如下下手?我的思路是:确定一个变量,其他变量根据这个变量做适应性调整。

    3 解决方法(具体下面会有图示)

    ①确定一个变量。由于当前的浏览器宽度是固定的,因此可以根据浏览器宽度范围制定一个标准高度,类似CSS的媒体查询(media query);

    ②初次变换。所有图片宽度根据这个标准高度作宽度的等比例缩放;

    ③创造容器。每行建立一个div,并装入尽可能多的图片,直到容器装不下;

    ④第一步调整。每行根据自己与目标宽度(当前浏览器宽度)的差值,再等比例变化宽、高。

    公式如下:当前行总宽度/目标宽度=每个图片当前高度/变化后高度;
    ⑤第二步调整。根据变化后高度再等比变化各图片宽度;

    4 操作图示

    step123

    step45

    大工告成!然而深入考虑和分析,还总结出一些别的问题,我用了以下不同的处理方法把这些问题解决。

    5 其他问题

    ①高度调整公式会产生百分比,浏览器是会直接取整,因此可能会产生-2到2px的误差;

    解决方法:调整后记录每行误差值gap,然后循环把gap的值分给同行每一张图片,这样前2张图片可能会有±1px的图片宽度变化,但是用户基本觉察不了图片的轻微拉伸变化。

    ②用户图片数可能过少,会有图片只有1-3张占不满一行的情况,该怎样显示布局;

    解决方法:判断只有1行图片的时候不作布局调整,少于1行则默认显示等高变化后的图片即可(即只调整一次,不需要为剩余值再自适应)。

    ③ 每行调整前的剩余宽度过大,导致调整后宽高很大;

    解决方法:若调整后宽高是原始宽高的150%左右则该行舍弃,这里考虑到整体图片质量,确保不影响图片墙效果。

    ④ 用户上传的照片太小,例如16×16的小图标,如果一样的方式调整会与400×800这些图片并列放大,造成很大缩放比。

    解决方法:考虑到是图片墙的效果,一般不会有用户传一些其他的图片,例如表情素材等等,同时在图片处理时可以加一个排序,获取了图片宽高后把小于一定值的图片排在最后再一起显示;

    等高布局demo及实现代码

    按照这个思路,我这里实现了一个demo,一般情况下我们去加载图片很多时候不知道每个图片的宽高值,这里采用onload加载完成获取每个图片宽高后再去处理的等高布局。

    还有一点区别于以上思路,就是在上面第三步中“解决方法--③创造容器。每行建立一个div,并装入尽可能多的图片,直到容器装不下”,这句话是说放不下的时候排列进入下一行展示,我这里处理成的是进入本行展示。这样这个标准高度原本现在的是最小高度,而最大高度无法控制,我这里改动后这个标准高度就是最大高度,因为我这里的demo总宽度就这么大,控制下最大高度比较适合。而如果总宽度较大时用原思路的进入下一行展示的效果也是可以的。

    简单的demo效果:

    页面代码 BrickWall.html

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     5     <title>布局展示</title>
     6     <script type="text/javascript" src="js/jquery-3.2.1.min.js"></script>
     7     <style>
     8         .grid {
     9             width: 520px;
    10             height: 1000px;
    11             overflow: auto;
    12         }
    13         
    14         .grid li {
    15             list-style-type: none;
    16             margin: 0;
    17             display: inline-block;
    18         }
    19     </style>
    20 </head>
    21 
    22 <body>
    23     <div class="grid">
    24     </div>
    25     <script type="text/javascript" src="BrickWall.js"></script>
    26     <script>
    27         var images = ["images/1.jpg", "images/2.jpg", "images/3.jpg",
    28             "images/4.jpg", "images/5.jpg", "images/6.jpg",
    29             "images/7.jpg", "images/8.jpg", "images/9.jpg",
    30             "images/10.jpg", "images/11.jpg", "images/12.jpg",
    31             "images/13.jpg", "images/14.jpg", "images/15.jpg",
    32             "images/16.jpg", "images/17.jpg", "images/18.jpg",
    33             "images/19.jpg", "images/20.jpg", "images/21.jpg",
    34             "images/22.jpg", "images/23.jpg", "images/23.jpg",
    35             "images/25.jpg", "images/26.jpg", "images/27.jpg",
    36             "images/28.jpg", "images/29.jpg", "images/30.jpg",
    37         ];
    38         new BrickWall(document.getElementsByClassName("grid")[0], images, 200, 500, null);
    39     </script>
    40 </body>
    41 
    42 </html>

    js代码

    BrickWall.js文件

     1  var BrickWall = (function () {
     2         function BrickWall(container, imagesArr, baseHeight, totalWidth, callback) {
     3             this.container = container;
     4             this.imagesArr = imagesArr;
     5             this.baseHeight = baseHeight;
     6             this.totalWidth = totalWidth;
     7             this.callback = callback;
     8             this.imagesObj = [];
     9             var imageCount = imagesArr.length;
    10             var that = this;
    11             imagesArr.forEach(function (src, i) {
    12                 that.imagesObj[i] = {};
    13                 that.imagesObj[i].src = src;
    14                 var img = document.createElement("img");
    15                 img.src = src;
    16                 that.imagesObj[i].imgEle = img;
    17                 img.onload = function (e) {
    18                     that.imagesObj[i].width = img.width * baseHeight / img.height;
    19                     ;
    20                     that.imagesObj[i].height = baseHeight;
    21                     imageCount--;
    22                     if (imageCount == 0)
    23                         that.onloadAll();
    24                 };
    25                 img.onerror = function (e) {
    26                     imageCount--;
    27                     if (imageCount == 0)
    28                         that.onloadAll();
    29                 };
    30             });
    31         }
    32         //所有图onload完成后,知道所有的宽高值后才能确定位置
    33         BrickWall.prototype.onloadAll = function () {
    34             var that = this;
    35             var baseWidth = 0;
    36             var divNum = 0;
    37             var divTotalWidth = [];
    38             that.imagesObj.forEach(function (imgObj, i) {
    39                 if (baseWidth < that.totalWidth) {
    40                     baseWidth += imgObj.width;
    41                 }
    42                 else {
    43                     divTotalWidth[divNum] = baseWidth;
    44                     divNum++;
    45                     baseWidth = imgObj.width;
    46                 }
    47                 imgObj.divNum = divNum;
    48                 imgObj.imgEle.height = that.baseHeight;
    49                 imgObj.imgEle.width = imgObj.width;
    50                 var li = document.createElement("li");
    51                 li.appendChild(imgObj.imgEle);
    52                 that.container.appendChild(li);
    53                 // var first = that.container.firstChild;//得到页面的第一个元素 
    54                 // that.container.insertBefore(li, first);//在得到的第一个元素之前插入 
    55                 if (i == that.imagesObj.length - 1)
    56                     divTotalWidth[divNum] = baseWidth;
    57             });
    58             that.imagesObj.forEach(function (imgObj, i) {
    59                 if (divTotalWidth[imgObj.divNum] > that.totalWidth) {
    60                     imgObj.imgEle.width = that.totalWidth / divTotalWidth[imgObj.divNum] * imgObj.width;
    61                     imgObj.imgEle.height = that.totalWidth / divTotalWidth[imgObj.divNum] * imgObj.height;
    62                 }
    63             });
    64         };
    65         return BrickWall;
    66     }());

    倒序排列

    另外项目加载图片需要砖墙布局,并且是倒序排列,提供以下几种方法。

    1):扩展一个reverse反转某个元素下子元素的方法

    1             $.extend({
    2                 reverseChild: function (obj, child) {
    3                     var childObj = $(obj).find(child);
    4                     var total = childObj.length;
    5                     childObj.each(function (i) {
    6                         $(obj).append(childObj.eq((total - 1) - i));
    7                     });
    8                 }
    9             });

    2)使用css

            .grid {
                overflow: auto;
                display: flex;
                flex-wrap: wrap-reverse;
            }

    但此时滚动条就失效了,是个问题!

    3)在插入列表的时候,每次都插入到列表的第一个位置

    1    var li = document.createElement("li");
    2    var first = box.firstChild;//得到页面的第一个元素 
    3    box.insertBefore(li, first);//在得到的第一个元素之前插入 
  • 相关阅读:
    Bean生命周期
    Bean的作用域
    神经网络训练中,傻傻分不清Epoch、Batch Size和迭代
    jQuery中选择器有哪几种
    数据库的事务机制
    多线程面试题
    HTTP请求报文和HTTP响应报文
    linux tomcat单机部署多应用
    flexbox预习
    作业
  • 原文地址:https://www.cnblogs.com/fangsmile/p/7201161.html
Copyright © 2011-2022 走看看