Skip to content

Instantly share code, notes, and snippets.

@599316527
Last active May 4, 2017 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 599316527/f726400400dbffc699570ef15fb667de to your computer and use it in GitHub Desktop.
Save 599316527/f726400400dbffc699570ef15fb667de to your computer and use it in GitHub Desktop.
Waterfall Layout Calculation
import {merge} from 'lodash'
const defaultContainerPadding = {
top: 0,
bottom: 0,
left: 0,
right: 0
}
const defaultBoxMargin = {
top: 0,
bottom: 0,
left: 0,
right: 0
}
const defaultOptions = {
containerWidth: 960,
containerPadding: defaultContainerPadding,
columnCount: 3,
columnGap: 10,
columnStepHeightThreshold: 0,
boxMargin: defaultBoxMargin
}
class Waterfall {
constructor(items, options) {
this.items = items.map(function (item) {
if (typeof item === 'number') {
return item
}
else {
let {width, height} = item
return width / height
}
})
if (this.items.some(item => isNaN(item))) {
throw new Error('Illegal inputs')
}
merge(this, defaultOptions, options)
}
getBoxes() {
let paddingBoxWidth = this.containerWidth - this.containerPadding.left - this.containerPadding.right
let columnWidth = (paddingBoxWidth - (this.columnCount - 1) * this.columnGap) / this.columnCount
let columnPostions = Array.from(new Array(this.columnCount)).map((item, index) => {
return {
top: this.containerPadding.top,
left: this.containerPadding.left + (this.columnGap + columnWidth) * index
}
})
let itemWidth = columnWidth - this.boxMargin.left - this.boxMargin.right
let currentColumnIndex = 0
let boxes = this.items.map((aspectRatio, index) => {
let pos = columnPostions[currentColumnIndex]
if (index > 0) {
for (let i = 0; i < this.columnCount; i++) {
// find next proper column to place the item
let nextColumnIndex = (currentColumnIndex + i + 1) % this.columnCount
let nextPos = columnPostions[nextColumnIndex]
if (nextPos.top - pos.top < this.columnStepHeightThreshold) {
pos = nextPos
currentColumnIndex = nextColumnIndex
break
}
}
}
let top = pos.top + this.boxMargin.top
let left = pos.left + this.boxMargin.left
let height = itemWidth / aspectRatio
pos.top = top + height + this.boxMargin.bottom
return {top, left, height, width: itemWidth}
})
this.boxes = boxes
return boxes
}
getContainerHeight() {
if (!this.boxes) {
this.getBoxes()
}
let boxes = this.boxes
let lastItemPerColumn = boxes.slice(Math.max(0, boxes.length - this.columnCount), boxes.length)
return Math.max.apply(Math, lastItemPerColumn.map(item => item.top + item.height))
+ this.boxMargin.bottom + this.containerPadding.bottom
}
}
export default function (items, options) {
let wf = new Waterfall(items, options)
return {
boxes: wf.getBoxes(),
containerHeight: wf.getContainerHeight()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment