Heim >Web-Frontend >js-Tutorial >So implementieren Sie einen horizontalen Wasserfallfluss in JavaScript

So implementieren Sie einen horizontalen Wasserfallfluss in JavaScript

Guanhui
Guanhuinach vorne
2020-05-13 10:33:133616Durchsuche

So implementieren Sie einen horizontalen Wasserfallfluss in JavaScript

Ich habe kürzlich an einem kleinen Programmprojekt gearbeitet und mir einige UI-Designs von anderen Apps ausgeliehen. Darunter gibt es eine Benutzeroberfläche mit einem horizontalen Layout von Bildern, die aussieht Ziemlich gut, ähnlich einem traditionellen Wasserfall. Die Strömungsanordnung ist horizontal gleich. Also habe ich es selbst implementiert und die ursprüngliche Methode mit zwei horizontalen Bildern zu einer Methode erweitert, mit der die Anzahl der angezeigten Bilder angepasst werden kann. Das Folgende ist der grundlegende Anzeigeeffekt:

So implementieren Sie einen horizontalen Wasserfallfluss in JavaScript

Zwei Bilder in einer einzigen Zeile anzeigen

So implementieren Sie einen horizontalen Wasserfallfluss in JavaScript

Fünf Bilder in einer Zeile anzeigen einzelne Zeile

Lassen Sie uns zunächst über die Idee beim Schreiben dieser Methode sprechen:

Wirkungsanalyse

Wie Sie im obigen Bild sehen können, egal Wie viele Bilder werden in einer einzelnen Zeile angezeigt? Es ist fast garantiert, dass das Bild vollständig angezeigt wird, und die Höhe jeder Zeile ist unterschiedlich. Dadurch soll sichergestellt werden, dass jedes Bild fast vollständig angezeigt werden kann. Daher muss die Zeilenhöhe gleich sein dynamisch entsprechend dem tatsächlich anzuzeigenden Bild angepasst.

Da die Pixelwiedergabe gerundet werden muss, tritt bei der Berechnung der Breite und Höhe des Bildes ein Fehler von 1 bis 2 Pixel auf. Dieser Fehler kann direkt ignoriert werden und führt nicht dazu, dass das Bild optisch gestreckt wird.

Nach der Analyse des Effekts haben wir folgende Fragen:

1. Wie kann sichergestellt werden, dass jede Zeile die darin enthaltenen Bilder vollständig anzeigen kann? Sie müssen wissen, dass die Breite und Höhe jedes Bildes unterschiedlich sind

2. Wie berechnet man dynamisch die Höhe jeder Zeile?

3. Wenn die verbleibende Anzahl von Bildern am Ende nicht der Anzahl der in einer einzelnen Zeile angezeigten Bilder entspricht, wie werden die Bilder in der letzten Zeile angeordnet?

4. ...

Problemanalyse

Schauen wir uns zunächst die erste Frage an: Wie kann sichergestellt werden, dass jedes Bild in einer einzigen Zeile dargestellt werden kann? vollständig angezeigt werden.

Zuerst können wir die Anzahl der Bilder bestimmen, die in einer einzelnen Zeile angezeigt werden. Dies ist voreingestellt, z. B. die obige einzelne Zeile mit 5 Bildern, numberInLine = 5. Jedes Bild in derselben Zeile hat die gleiche Höhe, sodass Sie die Breite einer einzelnen Zeile beim tatsächlichen Rendern des Bildes anhand des Verhältnisses der Breite des Bildes zur Gesamtbreite aller Bilder berechnen können. Die Formel lautet wie folgt :

imageRenderWidth = (imageWidth / imagesTotalWidth) * lineWidth

Obwohl die tatsächliche Breite und Höhe der Bilder unterschiedlich sind, können wir zunächst eine Standardhöhe stdHeight annehmen und die Breite jedes Bildes proportional skalieren, da die Höhe einer einzelnen Bildreihe gleich ist durch diese Standardhöhe, damit die Berechnung reibungslos erfolgen kann. Ermitteln Sie das Verhältnis der Breite eines einzelnen Bildes zur Gesamtbreite aller Bilder

So berechnen Sie die Höhe jeder Zeile

Unter der Voraussetzung, dass die Breite des Bildes bestimmt werden kann, muss jede Zeile bestimmt werden. Die Höhe ist relativ einfach. Berechnen Sie anhand des ersten Bildes in jeder Zeile zunächst die Rendering-Breite des ersten Bildes, berechnen Sie dann die Rendering-Höhe dieses Bildes und verwenden Sie diese als Zeilenhöhe. Jedes nachfolgende Bild berechnet seine eigene Höhe über die Zeilenhöhe Rendering-Breite. Es ist jedoch zu beachten, dass zum Füllen einer einzelnen Zeile die Gesamtbreite des letzten Bildes berechnet werden muss – die Summe der Breiten aller vorherigen Bilder, da sonst Lücken entstehen. Die Ausdrucksformel lautet wie folgt:

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

Wie wird ein Layout erstellt, wenn die Anzahl der verbleibenden Bilder geringer ist als die Anzahl der Einzelzeilen?

Dieses Problem muss in zwei Situationen berücksichtigt werden:

1 Wenn eine einzelne Zeile 5 Bilder erfordert, die verbleibende Anzahl jedoch kleiner als 5, aber größer als 1 ist (z. B 4 Bilder): Diese Zeile kann entsprechend der Anzahl der verbleibenden Bilder angeordnet werden, und der Algorithmus ist immer noch derselbe wie oben. Für diesen Punkt muss der Code also wiederverwendbar sein.

2. Wenn nur noch 1 Bild übrig ist, gibt es mehrere Verarbeitungsmethoden:

Dieses Bild kann über die gesamte Zeilenbreite ausgefüllt werden wird vollständig angezeigt, aber wenn es sich bei diesem Bild um ein Bild mit sehr hoher Höhe handelt, wird die Ästhetik des Layouts ernsthaft beeinträchtigt

Das Bild füllt immer noch die Linienbreite aus, aber mit einer maximalen Höhe, wenn die Höhe erreicht ist Wenn die maximale Höhe unterschritten wird, wird sie vollständig angezeigt. Wenn sie überschritten wird, wird nur ein Teil davon angezeigt. Dies kann die Schönheit des Layouts gewährleisten, es gibt jedoch Mängel bei der Anzeige des letzten Bildes

Nehmen Sie die Zeilenhöhe der vorherigen Zeile als Zeilenhöhe der letzten Zeile, wie folgt: Das Bild kann immer noch vollständig angezeigt werden, während die Konsistenz des Gesamtlayouts gewährleistet ist, aber dadurch bleibt in der letzten Zeile viel Leerraum Zeile

Für die oben genannten drei Verarbeitungsmethoden verwendet der Autor die zweite. Interessierte Freunde können die beiden anderen Methoden auch selbst ausprobieren. Oder wenn Sie eine bessere Layout-Methode haben, können Sie dies dem Autor auch in den Kommentaren mitteilen!

Ich frage mich, ob ihr die Erklärungen zu den oben genannten drei Fragen versteht? Es ist in Ordnung, wenn Sie es nicht verstehen. Sie können lernen, wie Sie diese Probleme direkt über den Code lösen.

Code-Implementierung

/* 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)
    })
  }
}
rrree

Empfohlenes Tutorial: „JS-Tutorial

Das obige ist der detaillierte Inhalt vonSo implementieren Sie einen horizontalen Wasserfallfluss in JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:zhihu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen