等高響應式佈局的原理和實現

磚牆佈局

具體原理,參考了騰訊磚牆佈局的思路:http://isux.tencent.com/high-equal-response-layout-html.htmljavascript

等高佈局效果圖:css

如圖,並不像等寬同樣簡單,要在不改變圖片分辨率(寬高比)同時保持等高且佔滿行寬度,如何實現?不妨帶着問題跟我走。html

1 等高響應式佈局是什麼?

①行內高度相等;
②行間總寬度相等;
③自適應寬度佈局;
④圖片分辨率(寬高比)不變;java

2 難在那裏?

①行內高度一致,行間高度不一致,可是相差不能太多;
②並不知道一行須要多少個圖片才能佔滿寬度,因爲高度不肯定,圖片的寬度也不能等比變化;
③如何作到自適應?
④佈局用於用戶的我的相冊,數據量是有限且未知的,如何保證圖片數量滿行顯示?jquery

由上可知,這種佈局涉及太多變量,並且最難的是作到圖片分辨率不改變,不影響圖片質量效果。瀏覽器

該以下下手?個人思路是:肯定一個變量,其餘變量根據這個變量作適應性調整。app

3 解決方法(具體下面會有圖示)

①肯定一個變量。因爲當前的瀏覽器寬度是固定的,所以能夠根據瀏覽器寬度範圍制定一個標準高度,相似CSS的媒體查詢(media query);佈局

②初次變換。全部圖片寬度根據這個標準高度做寬度的等比例縮放;flex

③創造容器。每行創建一個div,並裝入儘量多的圖片,直到容器裝不下;ui

④第一步調整。每行根據本身與目標寬度(當前瀏覽器寬度)的差值,再等比例變化寬、高。

公式以下:當前行總寬度/目標寬度=每一個圖片當前高度/變化後高度;
⑤第二步調整。根據變化後高度再等比變化各圖片寬度;

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);//在獲得的第一個元素以前插入 
相關文章
相關標籤/搜索