詳解瀑布流佈局的5種實現方式及object-fit

最近項目中須要處理與圖片相關的佈局,不得不說圖片這玩意真想要獲得完美的展現效果還真是要費些力氣。由於圖片的尺寸或者比例各不相同。因此想要不一樣尺寸的圖片有好的顯示效果,你就須要找到適合的方式。css

並且圖片每每是不可或缺元素。畢竟一圖勝千言,有時候圖片能給帶來很是好的效果。前端

好比咱們天天都會使用的表情包,它每每可以表達出咱們沒法用文字描述的信息,還好比咱們常常在公衆號裏看到的漫畫雖然短短几個字,可是卻可以讓咱們看的不亦樂乎。vue

固然若是咱們作圖片網站的,那圖片的處理就是繞不開的話題了。因對圖片的處理經驗很少,因此就邊學邊用。今天就把最近學習與圖片相關的知識整理出來。css3

好比單個圖片如何更好的展現,瀑布流佈局都有哪些你不知道的實現方式。git

接下來咱們就直接進入正題,咱們先從單張圖片的展現提及。github

設置寬或高 100%

由於圖片其自己的獨特性:瀏覽器

  1. 不設置寬高的狀況下會按原有的尺寸顯示在網頁中。即有多大,顯示多大。
  2. 在非等比縮放的狀況下會被拉伸變形。
  3. 設置寬度或者高度時,會保持原寬高比進行縮放。

<style lang="scss" scoped>
.box1 {
  width: 150px;
  height: 150px;
  border: 2px solid red;
}
.box2 {
  width: 150px;
  height: 100px;
  border: 2px solid red;
  img {
    display: block;
    width: 100%;
    height: 100%;
  }
}
</style>
複製代碼

顯然當咱們採用 一、2 種方式的時候破壞性很強,沒法應用到實際的項目中去。app

因此每每咱們會在項目中使用第 3 種方式,即設置高度或者寬度。它會保持原有比例進行縮放。wordpress

<style lang="scss" scoped>
.box {
  width: 150px;
  height: 150px;
  border: 2px solid red;
}
.img1 {
  width: 100%;
}
.img2 {
  height: 100%;
}
</style>
複製代碼

可是問題又來了,圖片要麼超出容器,要麼就會留有空白,除非容器的寬高比剛好等於圖片的寬高比時,纔會徹底貼合。佈局

對於超出容器的圖片咱們可使用 overflow: hidden 把超出部分隱藏。圖片獲得了好的展現效果。但相應的咱們也損失了圖片的一部分可視區域。

因此這個時候就須要你根據需求進行取捨了,究竟是選擇隱藏圖片的一部分,仍是留有空白。有的小夥伴會說,咱們產品說了,圖片變形沒問題,你就給我充滿容器就好了。好吧....

即便如此,你也要把這篇文章好好讀一讀,由於需求是變幻無窮的,保不齊哪一天就須要了。

又有小夥伴說,這 2 種都不符合咱們的產品需求怎麼辦,還有其餘的方式嗎?答案是必須的,一塊兒來看。

object-fit

CSS3 的 object-fit 屬性是用來指定「可替換元素」的內容是如何適應到容器中的。它的值有 5 種。分別爲:fill | contain | cover | none | scale-down。先看下效果在來一一解釋它們到底都是什麼意思。

<template>
  <div class="box">
    <img src="https://picsum.photos/id/1027/200/300"/>
  </div>
</template>
<style lang="scss" scoped>
.box {
  width: 150px;
  height: 150px;
  border: 2px solid red;
  img {
    width: 100%;
    height: 100%;
    object-fit: contain;
  }
}
</style>

複製代碼

看到上面的顯示效果,理解起來並不難了。

  • fill:會充滿整個容器,不考慮寬高比,因此會被拉伸變形。
  • contain:會縮放到容器內,保持寬高比。
  • cover:會保持比例進行縮放,內容的尺寸必定會大於等於容器,而後進行裁剪。
  • none:保持圖片的原始尺寸。

scale-down 有兩種表現方式因此咱們單獨來看。

  • scale-down:會在 none 或 contain 中選擇一個,原則是:當容器小時,它的表現和 contain 同樣;當圖片小時,它的表現和 none 同樣。即誰小選擇誰。

到這裏不知道有沒有小夥伴和我同樣,在看到圖片的不一樣表現時,我特地去瀏覽器查看了下 <img>的真實尺寸,發現依然是 width: 100%;height: 100%; 是充滿整個容器的。

但爲何內容顯示卻有不一樣的效果呢,這讓我產生了疑惑。本着發現探索的精神,就去尋找答案了。

W3c 是這麼描述的:<img> 標籤建立的是被引用圖像的佔位空間。

而張鑫旭大大在半深刻理解CSS3 object-position/object-fit屬性一文中也指出:

<img>元素其實與內容是相互獨立的。<img>至關於一個外殼包裹着內容。你控制的只是<img> 元素的尺寸。而內容的尺寸則由 object-fit 屬性控制。

綜上索述,<img> 是一個空間佔位符,並不控制內容。原來如此。感受本身又進步了。每一次的探索,都會發現新的東西,這種感受很奇妙。特別是還把它整理出文章,提供你們學習,感受牛逼哄哄帶閃電。

知道了這些以後咱們操控圖片時更加的駕輕就熟一些。那會了這些就結束了嗎?不不不,這纔是剛剛開始,精彩的還在後面。後面的佈局才更加精彩。

多圖片的佈局

上面一直在說的都單張圖片的顯示。只要咱們把圖片用合適的方式放進容器便可。若是是圖片列表呢?或者專門展現圖片的網站會有大量的圖片並且尺寸和比例千奇百怪,各不相同。

假設要想實現一個圖片畫廊效果,首先咱們給圖片一個 float: left,可是因爲圖片的尺寸不同,致使每一個圖片的高度不一樣,下一行的圖片就會卡住,致使佈局錯亂。

此時你不得不給容器設置高度,讓圖片可以底部對齊,但在文章一開始咱們也提到了,這時候圖片要麼超出容器的高度,要麼留有空白。

那若是使用 object-fit 屬性按照業務需求去控制內容,貌似能夠完成任務。

咱們把值設爲 contain,佈局是沒有問題了,可是其實很不美觀。若是設爲 cover,若是圖片過大不少內容都會丟失看不到。 怎麼辦?有什麼解決辦法?這時候就是瀑布流佈局的優點了。

瀑布流佈局即不會出現錯亂現象,並且會最大限度顯示圖片的內容。因此是衆多圖片網站選擇的佈局方式。

而瀑布流佈局目前有兩種形式:一是等寬型,二是等高型。咱們先來講說等寬型。

等寬瀑布流

看到上面你實現的思路是什麼?能夠思考幾秒,接下來一塊兒來看這些實現方式中有沒有和你一思路同樣的。

思路1. JS 計算列數

關鍵思路:

  • 首先設置列寬度,而後計算可以展現的列數。
  • 向每一列中添加圖片。

關鍵代碼:

<script>
export default {
  methods: {
     //計算圖片列數
    getColNumbers() {
      let clientWidth = this.$refs.waterfall.clientWidth
      this.colNumbers = Math.floor(clientWidth / this.colWidth)
    },
    //讀取圖片
    loadImage() {
      this.getColNumbers()
      for (let i = 0; i < 17; i++) {
        let colIndex = i % this.colNumbers
        let url = require(`@/assets/images/${i}.jpg`)
        if (this.imgList[colIndex]) {
          this.imgList[colIndex].push(url)
        } else {
          this.$set(this.imgList, colIndex, [url])
        }
      }
    },
  }
}
</script>
複製代碼

優點:思路清晰簡單,不須要作過多的計算,只要計算須要顯示的列數而後添加圖片便可。

劣勢:每列的末尾可能不夠友好,可能出現有些列會很長,有些又會很短。

思路2. 利用絕對定位

關鍵思路:

  • 首先設置列寬度,而後計算可以展現的列數。
  • 把圖片設置爲絕對定位,而後計算出每一個圖片的top,left值。
  • 先把第一行圖片排好,top 爲 0,left 爲 列的索引*列寬。
  • 從第二行開始,每張圖片都放到最短的一列下面。而後增長此列高度,此時列的高度發生變化,下張圖片又會尋找其餘最短的列。以此持續計算下去。

關鍵代碼:

<script>
export default {
  methods: {
    //計算圖片列數
    getColNumbers() {
      let clientWidth = this.$refs.waterfall.clientWidth
      this.colNumbers = Math.floor(clientWidth / this.colWidth)
    },
    //讀取圖片
    loadImage() {
      this.getColNumbers()
      for (let i = 0; i < 17; i++) {
        let image = new Image()
        let url = require(`@/assets/images/${i}.jpg`)
        image.src = url
        image.onload = () => {
          this.render({
            index: i,
            url: url,
            ratio: image.width / image.height
          })
        }
      }
    },
    render(imgInfo) {
      let colIndex = imgInfo.index % this.colNumbers
      imgInfo.left = colIndex * this.colWidth
      //首行 top爲 0,記錄每列的高度
      if (imgInfo.index < this.colNumbers) {
        imgInfo.top = 0
        this.colHeight[colIndex] = this.colWidth / imgInfo.ratio
      } else {
        //獲取高度的最小值
        let minHeight = Math.min.apply(null, this.colHeight)
        let minIndex = this.colHeight.indexOf(minHeight)
        //此圖片的 top 爲上面圖片的高度,left 相等
        imgInfo.top = minHeight
        imgInfo.left = minIndex * this.colWidth
        //把高度加上去
        this.colHeight[minIndex] += this.colWidth / imgInfo.ratio
      }
      this.imgList.push(imgInfo)
    }
  }
}
</script>
複製代碼

優點:由於每次追加的圖片都是最短列,因此末尾的展現會比思路 1 中要友好不少。

劣勢:沒渲染一張都會計算一次 top,left 值。並且圖片的順序是打亂的。

思路3. CSS3 column 屬性

關鍵思路:

  • column-count:指定列數
  • column-gap: 設置列之間的間距

關鍵代碼:

<template>
  <div class="waterfall-width-column">
    <div class="image-box" v-for="img in imgList" :key="img">
      <img :src="img" alt="" />
    </div>
  </div>
</template>
<style lang="scss" scoped>
.waterfall-width-column {
  column-count: 3;
  column-gap: 10px;
  .image-box {
    img {
      display: block;
      width: 100%;
    }
  }
}
</style>
複製代碼

優點:更加簡單,不用額外計算,直接使用CSS渲染高效。

劣勢:圖片的順序是從上向下排列的,這個要看業務需求允不容許了。另外列數固定。

不過你能夠嘗試經過媒體查詢設置不一樣列數

@media (min-width: 768px) {
  .waterfall-width-column {
    column-count: 3;
  }
}
@media (min-width: 992px) {
  .waterfall-width-column {
    column-count: 4;
  }
}
@media (min-width: 1200px) {
  .waterfall-width-column {
    column-count: 6;
  }
}
複製代碼

等高瀑布流

說完了等寬型接下來咱們來講說等高型。

思路1. JS計算縮放

  • 首先給定一個基準高度
  • 圖片獲取基準高度下的寬度,而後計算每一行可以放入多少張
  • 此時每一行圖片確定會小於容器寬度,而後這一行進行縮放到容器大小。在從新計算放大後的高度。

關鍵代碼:

<script>
export default {
  data() {
    return {
      baseHeight: 200, //圖片的基礎計算高度
      imgList: [[]], //用二維數據保存每一行數據
      rowWidth: 0, //每行的圖片寬度
      rowCount: 0 //每行的索引
    }
  },
  methods: {
    loadImage() {
      for (let i = 0; i < 17; i++) {
        let image = new Image()
        let url = require(`@/assets/images/${i}.jpg`)
        image.src = url
        image.onload = () => {
          this.compare({
            url: url,
            width: this.baseHeight * (image.width / image.height),
            height: this.baseHeight
          })
        }
      }
    },
    //縮放後的總圖片寬度與屏幕寬度比較
    compare(image) {
      //容器寬度
      let clientWidth = this.$refs.waterfall.clientWidth
      //計算每行寬度
      this.rowWidth += image.width
      //若是寬度大於容器寬度,去掉多餘的寬度,總體進行縮放適應容器讓右邊對齊
      if (this.rowWidth > clientWidth) {
        //減去每一個css padding邊距
        clientWidth = clientWidth - this.imgList[this.rowCount].length * 10
        this.rowWidth = this.rowWidth - image.width
        //把高度調整爲放大後的
        let growAfterHeight = (clientWidth * this.baseHeight) / this.rowWidth
        this.imgList[this.rowCount].forEach(item => {
          item.height = growAfterHeight
        })
        //把多餘圖片放入到下一行
        this.rowWidth = image.width
        this.rowCount++
        this.$set(this.imgList, this.rowCount, [image])
      } else {
        this.imgList[this.rowCount].push(image)
      }
    }
  }
}
</script>
複製代碼

優點:圖片的內容獲得所有展現,不會被隱藏。

劣勢:須要反覆計算以及縮放。

思路2. Flex佈局

  • 首先給圖片一個固定高度,而後利用flex-grow的比例分配的特性
  • 給圖片設定object-fit屬性讓其保持比例充滿容器
<template>
  <div class="waterfall-height-css">
    <div class="image-box" v-for="img in imgList" :key="img.url">
      <img :src="img.url" />
    </div>
  </div>
</template>
<script>
<style lang="scss" scoped>
.waterfall-height-css {
  display: flex;
  flex-wrap: wrap;
  .image-box {
    flex-grow: 1;
  }
  img {
    display: block;
    min-width: 100%;
    height: 200px;
    object-fit: cover;
  }
}
</style>
複製代碼

此時你會發現,每一行的圖片都獲得了很好的顯示效果。可是惟獨最後一行會出現一個小小的問題。

想象一下,假如最後一行只有一張圖片的話,他會被縮放到充滿一行,致使圖片只會顯示很是小的一部份內容。

因此,咱們最後一行的圖片不進行縮放處理便可。只須要添加如下css屬性便可。

<style lang="scss" scoped>
.waterfall-height-css {
  &:after {
    content: '';
    display: block;
    flex-grow: 99999;
  }
}
</style>
複製代碼

由於flex-grow: 99999的值很是大,因此會把最後一行的剩餘空間幾乎所有佔用,致使圖片分配不了,只會按照原尺寸顯示,就不會縮放佔滿一行啦。

優點:css 設置簡單,渲染高效。

劣勢:會損失圖片的一部分可見區域。

到此,咱們介紹了圖片的顯示特性以及如何利用 object-fit 進行內容的控制。

對於多圖片的佈局,要想比較合理的顯示圖片,瀑布流佈局是很是好的選擇,固然若是業務需求對圖片的展現友好度及美觀度不作要求,你大可利用 object-fit 控制內容便可。

可是我認爲瀑布流佈局也是咱們應該掌握的內容之一,即使此時用不到,也能夠先把文章收藏起來,以備不時之需,文中採用了多種方式的實現,你能夠選擇一種最貼合你需求的方式。

固然,案例中其實還有不少細節沒有處理,好比瀏覽器窗口發上變化時從新加載圖片會發生閃動該如何優化體驗?小夥伴們不妨本身去嘗試進行優化。動手實踐是掌握技能的重要手段。

文中全部案例的代碼地址:gitHub地址

2T技術資源免費送,包括不限於:前端、Python、Java、Android、產品等等。公衆號後臺回覆「1024」,便可免費獲取!

公衆號:六小登登,更多幹貨文章,後臺回覆「加羣」帶你進入優質交流羣。

相關文章
相關標籤/搜索