搜尋
首頁web前端js教程JavaScript 如何實現橫向瀑布流

JavaScript 如何實現橫向瀑布流

May 13, 2020 am 10:33 AM
javascriptjs

JavaScript 如何實現橫向瀑布流

最近在做一個小程式項目,在UI 上借鑒了一下其他App 設計,其中有一個圖片橫向佈局鋪滿的UI 感覺挺好看的,類似於傳統的瀑布流佈局橫過來一樣。於是就自己實現了一下,並且將原本的橫向兩張圖的方法擴展了下,改成了可以自訂顯示張數的方法。以下是基本的顯示效果:

JavaScript 如何實現橫向瀑布流

單行顯示兩張圖

JavaScript 如何實現橫向瀑布流

單行顯示五張圖

下面先說說寫這個方法時的想法:

效果分析

可以看到在上圖中,單行不管顯示幾張圖片,都幾乎能夠保證圖片被完整顯示出來,並且每一行的高度都不同——這是為了保證每張圖都能幾乎完整顯示,所以必須根據實際要顯示的圖片來動態地調整行高。

由於像素渲染必須取整,所以計算圖片的寬高方面會存在 1~2px 的誤差。這個誤差可以直接忽略,不會導致圖片在視覺上產生拉伸。

分析完效果後,就有了下面幾個問題:

1、如何保證每一行都能完整顯示裡面的圖片?要知道每張圖片的寬高都是不同的

2、如何動態計算每一行的高度?

3、在最後圖片剩餘數量不滿足單行顯示的圖片數的情況下,如何對最後一行的圖片進行佈局?

4、……

問題分析

先來看第一個問題:如何保證單行的每一張圖片都能完整顯示。

首先我們可以確定單行的圖片顯示數量,這個是預先設定好的,例如上面的單行 5 張圖 numberInLine = 5。而同一行中的每張圖片高度都相同,這樣就可以根據圖片寬度與所有圖片的總寬度的比值,計算出這張圖片實際渲染時佔單行的寬度,公式如下:

imageRenderWidth = (imageWidth / imagesTotalWidth) * lineWidth

雖然圖片的實際寬高各不相同,但是由於單行圖片的高度都相同,我們就可以透過先假設一個標準高度stdHeight ,透過這個標準高度把每張圖片的寬度都進行比例縮放,這樣就可以順利計算出單張圖片寬度在所有圖片總寬度中的比值

如何計算每一行的高度

在能夠確定圖片寬度的前提下,要確定每一行的高度相對就非常簡單了。以每行第一張圖片為基準,先計算出第一張圖片的渲染寬度,然後計算出這張圖片的渲染高度,並以此作為行高,之後的每張圖片都透過行高計算出各自的渲染寬度。但是要注意的是,為了填滿單行,最後一張圖片需要透過總寬度-之前所有圖片寬度總和的方式計算出,否則就會有空白,表達公式如下:

// 第一张图片渲染宽度
firstImageRenderWidth = (firstImageWidth / imagesTotalWidth) * lineWidth
// 第一张图片渲染高度,即行高,即该行所有图片高度
lineHeight = imagesRenderHeight = firstImageRenderWidth / (firstImageWidth / firstImageHeight)
// 中间图片渲染宽度
middleImageRenderWidth = lineHeight * (middleImageWidth / middleImageHeight)
// 最后一张图片渲染宽度
lastImageRenderWidth = lineWidth - otherImagesTotalRenderWidth

#當剩餘圖片數量不足單行數量時如何佈局?

這個問題需要分兩種情況來考慮:

1、當單行需要5 張,而剩餘數量不足5 張但大於1 張時(如4 張圖片) :該行可依照剩餘圖片的數量佈局,演算法依然如上。所以對於這點,需要讓程式碼具有可重複使用性;

2、只剩下1 張圖片時,有以下幾種處理方法:

可以將這張圖片佔滿全部行寬且完整顯示,但是如果這張圖片是高度很高的圖片,就會嚴重影響佈局的美觀性

#依然將圖片佔滿行寬,但是給定一個最大高度,當高度不及最大高度時完整顯示,當超過時只顯示部分,這樣能保證佈局美觀性,但是最後一張圖片的顯示存在瑕疵

取前一行的行高作為最後一行的行高,這樣可以在確保整體佈局一致性的情況下,依然可以完整顯示圖片,但是這樣做最後一行會留有大量空白位置

對於上面三種處理方式,作者採用的是第二種。有興趣的小夥伴可以自己嘗試其他兩種方式。或者如果你有更好的佈局方式,也可以在評論裡告訴作者哦!

不知道上面三個問題的解釋小夥伴們有沒有理解了呢?不理解也沒事,可以直接透過程式碼來了解是如何解決這些問題的。

程式碼實作

/* imagesLayout.js */
/*
 * 图片横向瀑布流布局 最大限度保证每张图片完整显示 可以获取最后计算出来的图片布局宽高信息 最后的瀑布流效果需要配合 css 实现(作者通过浮动布局实现)当然你也可以对代码进行修改 让其能够直接返回一段已经布局完成的 html 结构
 * 需要先提供每张图片的宽高 如果没有图片的宽高数据 则可以在代码中添加处理方法从而获取到图片宽高数据后再布局 但是并不推荐这样做
 * 尽量保证图片总数能被单行显示的数量整除 避免最后一行单张显示 否则会影响美观
 * 每张图由于宽高取整返回的宽高存在0-2px的误差 可以通过 css 保证视觉效果
 */
/*
 * @param images {Object} 图片对象列表,每一个对象有 src width height 三个属性
 * @param containerWidth {Integer} 容器宽度
 * @param numberInLine {Integer} 单行显示图片数量
 * @param limit {Integer} 限制需要进行布局的图片的数量 如果传入的图片列表有100张 但只需要对前20张进行布局 后面的图片忽略 则可以使用此参数限制 如果不传则默认0(不限制)
 * @param stdRatio {Float} 图片标准宽高比
 */
class ImagesLayout {
  constructor(images, containerWidth, numberInLine = 10, limit = 0, stdRatio = 1.5) {
    // 图片列表
    this.images = images
    // 布局完毕的图片列表 通过该属性可以获得图片布局的宽高信息
    this.completedImages = []
    // 容器宽度
    this.containerWidth = containerWidth
    // 单行显示的图片数量
    this.numberInLine = numberInLine
    // 限制布局的数量 如果传入的图片列表有100张 但只需要对前20张进行布局 后面的图片忽略 则可以使用此参数限制 如果不传则默认0(不限制)
    this.limit = limit
    // 图片标准宽高比(当最后一行只剩一张图片时 为了不让布局看上去很奇怪 所以要有一个标准宽高比 当图片实际宽高比大于标准宽高比时会发生截取 小于时按照实际高度占满整行显示)
    this.stdRatio = stdRatio
    // 图片撑满整行时的标准高度
    this.stdHeight = this.containerWidth / this.stdRatio
    this.chunkAndLayout()
  }
  // 将图片列表根据单行数量分块并开始计算布局
  chunkAndLayout () {
    // 当图片只有一张时,完整显示这张图片
    if (this.images.length === 1) {
      this.layoutFullImage(this.images[0])
      return
    }
    let temp = []
    for (let i = 0; i < this.images.length; i++) {
      if (this.limit && i >= this.limit) return
      temp.push(this.images[i])
      // 当单行图片数量达到限制数量时
      // 当已经是最后一张图片时
      // 当已经达到需要布局的最大数量时
      if (i % this.numberInLine === this.numberInLine - 1 || i === this.images.length - 1 || i === this.limit - 1) {
        this.computedImagesLayout(temp)
        temp = []
      }
    }
  }
  // 完整显示图片
  layoutFullImage (image) {
    let ratio = image.width / image.height
    image.width = this.containerWidth
    image.height = parseInt(this.containerWidth / ratio)
    this.completedImages.push(image)
  }
  // 根据分块计算图片布局信息
  computedImagesLayout(images) {
    if (images.length === 1) {
      // 当前分组只有一张图片时
      this.layoutWithSingleImage(images[0])
    } else {
      // 当前分组有多张图片时
      this.layoutWithMultipleImages(images)
    }
  }
  // 分组中只有一张图片 该张图片会单独占满整行的布局 如果图片高度过大则以标准宽高比为标准 其余部分剪裁 否则完整显示
  layoutWithSingleImage (image) {
    let ratio = image.width / image.height
    image.width = this.containerWidth
    // 如果是长图,则布局时按照标准宽高比显示中间部分
    if (ratio < this.stdRatio) {
      image.height = parseInt(this.stdHeight)
    } else {
      image.height = parseInt(this.containerWidth / ratio)
    }
    this.completedImages.push(image)
  }
  // 分组中有多张图片时的布局
    // 以相对图宽为标准,根据每张图的相对宽度计算占据容器的宽度
  layoutWithMultipleImages(images) {
    let widths = [] // 保存每张图的相对宽度
    let ratios = [] // 保存每张图的宽高比
    images.forEach(item => {
      // 计算每张图的宽高比
      let ratio = item.width / item.height
      // 根据标准高度计算相对图宽
      let relateWidth = this.stdHeight * ratio
      widths.push(relateWidth)
      ratios.push(ratio)
    })
    // 计算每张图片相对宽度的总和
    let totalWidth = widths.reduce((sum, item) => sum + item, 0)
    let lineHeight = 0 // 行高
    let leftWidth = this.containerWidth // 容器剩余宽度 还未开始布局时的剩余宽度等于容器宽度
    images.forEach((item, i) => {
      if (i === 0) {
        // 第一张图片
        // 通过图片相对宽度与相对总宽度的比值计算出在容器中占据的宽度与高度
        item.width = parseInt(this.containerWidth * (widths[i] / totalWidth))
        item.height = lineHeight = parseInt(item.width / ratios[i])
        // 第一张图片布局后的剩余容器宽度
        leftWidth = leftWidth - item.width
      } else if (i === images.length - 1) {
        // 最后一张图片
        // 宽度为剩余容器宽度
        item.width = leftWidth
        item.height = lineHeight
      } else {
        // 中间图片
        // 以行高为标准 计算出图片在容器中的宽度
        item.height = lineHeight
        item.width = parseInt(item.height * ratios[i])
        // 图片布局后剩余的容器宽度
        leftWidth = leftWidth - item.width
      }
      this.completedImages.push(item)
    })
  }
}
<!-- imagesLayout.html -->
<!-- css 布局通过浮动实现 -->
<style>
* {
  box-sizing: border-box;
}
#horizontal-waterfull {
  width: 300px;
}
#horizontal-waterfull:before, #horizontal-waterfull:after {
  content: &#39;&#39;;
  display: table;
  clear: both;
}
img {
  display: block;
  width: 100%;
  height: 100%;
}
.image-box {
  float: left;
  padding: 1px;
  overflow: hidden;
}
</style>
// 水平布局瀑布流容器
<div id="horizontal-waterfull"></div>
<script src="./imagesLayout.js"></script>
<script>
// 待布局图片列表
const images = [{
  src: &#39;https://static.cxstore.top/images/lake.jpg&#39;,
  width: 4000,
  height: 6000
}, {
  src: &#39;https://static.cxstore.top/images/japan.jpg&#39;,
  width: 1500,
  height: 1125
}, {
  src: &#39;https://static.cxstore.top/images/girl.jpg&#39;,
  width: 5616,
  height: 3266
}, {
  src: &#39;https://static.cxstore.top/images/flower.jpg&#39;,
  width: 4864,
  height: 3648
}, {
  src: &#39;https://static.cxstore.top/images/lake.jpg&#39;,
  width: 4000,
  height: 6000
}, {
  src: &#39;https://static.cxstore.top/images/japan.jpg&#39;,
  width: 1500,
  height: 1125
}, {
  src: &#39;https://static.cxstore.top/images/grass.jpg&#39;,
  width: 5184,
  height: 2916
}]
// 获取布局容器
const $box = document.getElementById(&#39;horizontal-waterfull&#39;)
// 创建一个布局实例
const layout = new ImagesLayout(images, $box.clientWidth, 4)
// 通过 layout 的 completedImages 获取所有图片的布局信息
layout.completedImages.forEach(item => {
  let $imageBox = document.createElement(&#39;div&#39;)
  $imageBox.setAttribute(&#39;class&#39;, &#39;image-box&#39;)
  $imageBox.style.width = item.width + &#39;px&#39;
  $imageBox.style.height = item.height + &#39;px&#39;
  let $image = document.createElement(&#39;img&#39;)
  $image.setAttribute(&#39;src&#39;, item.src)
  $imageBox.appendChild($image)
  $box.appendChild($imageBox)
})

推薦教學:《JS教學

以上是JavaScript 如何實現橫向瀑布流的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:zhihu。如有侵權,請聯絡admin@php.cn刪除
JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的演變:當前的趨勢和未來前景JavaScript的演變:當前的趨勢和未來前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

神秘的JavaScript:它的作用以及為什麼重要神秘的JavaScript:它的作用以及為什麼重要Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python還是JavaScript更好?Python還是JavaScript更好?Apr 06, 2025 am 12:14 AM

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

如何安裝JavaScript?如何安裝JavaScript?Apr 05, 2025 am 12:16 AM

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。

在Quartz中如何在任務開始前發送通知?在Quartz中如何在任務開始前發送通知?Apr 04, 2025 pm 09:24 PM

如何在Quartz中提前發送任務通知在使用Quartz定時器進行任務調度時,任務的執行時間是由cron表達式設定的。現�...

在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?Apr 04, 2025 pm 09:21 PM

在JavaScript中如何獲取原型鏈上函數的參數在JavaScript編程中,理解和操作原型鏈上的函數參數是常見且重要的任�...

微信小程序webview中Vue.js動態style位移失效是什麼原因?微信小程序webview中Vue.js動態style位移失效是什麼原因?Apr 04, 2025 pm 09:18 PM

在微信小程序web-view中使用Vue.js動態style位移失效的原因分析在使用Vue.js...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具