文章出處

磚墻布局

具體原理,參考了騰訊磚墻布局的思路: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);//在得到的第一個元素之前插入 

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()