圖片橫向等高瀑布流,每行占滿,限制行數 的實現

来源:https://www.cnblogs.com/imwtr/archive/2018/12/02/10051995.html
-Advertisement-
Play Games

圖片的橫向瀑布流,其實簡單地按順序排列就可以了 但要實現每行中各圖片都等高(各行不一定等高,但每行裡面等高),且每行都占滿,就需要用到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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 文章摘抄自:一支小白,博客地址:http://www.cnblogs.com/startnow/p/7580865.html Centos7安裝Oracle 11gR2 - 環境:VM12+centos7 x86_64 minimal - 最小化安裝的Centos7 - 虛擬機配置- 50G硬碟 - ...
  • 一、資料庫能夠做什麼 1.存儲大量的數據。 2.保持數據信息的一致、完整。 3.共用和安全。 4.通過組合分析,產生新的有用信息。 二、資料庫的基本概念 1.資料庫就是“數據”的“倉庫”。 2.資料庫中包含表、關係以及操作對象。 3.數據存放在表中。 三、數據完整性 ...
  • 一、什麼是索引。 索引是用來加速查詢的技術的選擇之一,在通常情況下,造成查詢速度差異 的因素就是索引是否使用得當。當我們沒有對數據表的某一欄位段或者多個 欄位添加索引時,實際上執行的全表掃描操作,效率很低。而如果我們為某 些欄位添加索引, 在執行搜索時便可以通過掃描索引,然後再找出索 引對應的值,從 ...
  • 1 . 背景描述 本公司的SQL Server 伺服器近百台,為了收集伺服器運行的狀態,需要在各個實例上部署監控Job,將收集到的信息推送到中央管理伺服器。 收集的信息主要包括:慢查詢、阻塞、資源等待、Connection_Trace log 、Job執行狀態、複製訂閱狀態、Alwayson狀態、數 ...
  • 本人微信公眾號,歡迎掃碼關註! 從Hadoop框架討論大數據生態 1、Hadoop是什麼 1)Hadoop是一個由Apache基金會所開發的分散式系統基礎架構 2)主要解決,海量數據的存儲和海量數據的分析計算問題。 3)廣義上來說,HADOOP通常是指一個更廣泛的概念——HADOOP生態圈 2、Ha ...
  • 本人微信公眾號,歡迎掃碼關註! 大數據概論 1、大數據概念 大數據(big data),指無法在一定時間範圍內用常規軟體工具進行捕捉、管理和處理的數據集合,是需要新處理模式才能具有更強的決策力、洞察發現力和流程優化能力的海量、高增長率和多樣化的信息資產。 最小的基本單位是bit,按順序給出所有單位: ...
  • ORACLEserver實例DB的概念學習理解與總結,邊學習邊總結 ...
  • 在寫安卓登錄註冊時註銷按鈕閃退發現: 這是因為此處錯誤: 因為用戶名為字元串,不是整型,資料庫查詢要引號,少了引號查詢不了,導致閃退 解決後成功運行 正確用法: 下次謹記,細節決定成敗呀! ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...