Move layout positioning logic to its own file.
parent
fd330bd800
commit
7aa6461b4d
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,129 @@
|
||||
'use strict';
|
||||
|
||||
import Point from './point';
|
||||
|
||||
function arrayMax(array) {
|
||||
return Math.max.apply(Math, array);
|
||||
}
|
||||
|
||||
function arrayMin(array) {
|
||||
return Math.min.apply(Math, array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the location of the next item, based on its size.
|
||||
* @param {Object} itemSize Object with width and height.
|
||||
* @param {Array.<number>} positions Positions of the other current items.
|
||||
* @param {number} gridSize The column width or row height.
|
||||
* @param {number} total The total number of columns or rows.
|
||||
* @param {number} threshold Buffer value for the column to fit.
|
||||
* @param {number} buffer Vertical buffer for the height of items.
|
||||
* @return {Point}
|
||||
*/
|
||||
export function getItemPosition({ itemSize, positions, gridSize, total, threshold, buffer }) {
|
||||
var span = getColumnSpan(itemSize.width, gridSize, total, threshold);
|
||||
var setY = getAvailablePositions(positions, span, total);
|
||||
var shortColumnIndex = getShortColumn(setY, buffer);
|
||||
|
||||
// Position the item
|
||||
var point = new Point(
|
||||
Math.round(gridSize * shortColumnIndex),
|
||||
Math.round(setY[shortColumnIndex]));
|
||||
|
||||
// Update the columns array with the new values for each column.
|
||||
// e.g. before the update the columns could be [250, 0, 0, 0] for an item
|
||||
// which spans 2 columns. After it would be [250, itemHeight, itemHeight, 0].
|
||||
var setHeight = setY[shortColumnIndex] + itemSize.height;
|
||||
for (var i = 0; i < span; i++) {
|
||||
positions[shortColumnIndex + i] = setHeight;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of columns an items spans.
|
||||
* @param {number} itemWidth Width of the item.
|
||||
* @param {number} columnWidth Width of the column (includes gutter).
|
||||
* @param {number} columns Total number of columns
|
||||
* @param {number} threshold A buffer value for the size of the column to fit.
|
||||
* @return {number}
|
||||
*/
|
||||
function getColumnSpan(itemWidth, columnWidth, columns, threshold) {
|
||||
var columnSpan = itemWidth / columnWidth;
|
||||
|
||||
// If the difference between the rounded column span number and the
|
||||
// calculated column span number is really small, round the number to
|
||||
// make it fit.
|
||||
if (Math.abs(Math.round(columnSpan) - columnSpan) < threshold) {
|
||||
// e.g. columnSpan = 4.0089945390298745
|
||||
columnSpan = Math.round(columnSpan);
|
||||
}
|
||||
|
||||
// Ensure the column span is not more than the amount of columns in the whole layout.
|
||||
return Math.min(Math.ceil(columnSpan), columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the column set to use for placement.
|
||||
* @param {number} columnSpan The number of columns this current item spans.
|
||||
* @param {number} columns The total columns in the grid.
|
||||
* @return {Array.<number>} An array of numbers represeting the column set.
|
||||
*/
|
||||
function getAvailablePositions(positions, columnSpan, columns) {
|
||||
// The item spans only one column.
|
||||
if (columnSpan === 1) {
|
||||
return positions;
|
||||
}
|
||||
|
||||
// The item spans more than one column, figure out how many different
|
||||
// places it could fit horizontally.
|
||||
// The group count is the number of places within the positions this block
|
||||
// could fit, ignoring the current positions of items.
|
||||
// Imagine a 2 column brick as the second item in a 4 column grid with
|
||||
// 10px height each. Find the places it would fit:
|
||||
// [20, 10, 10, 0]
|
||||
// | | |
|
||||
// * * *
|
||||
//
|
||||
// Then take the places which fit and get the bigger of the two:
|
||||
// max([20, 10]), max([10, 10]), max([10, 0]) = [20, 10, 0]
|
||||
//
|
||||
// Next, find the first smallest number (the short column).
|
||||
// [20, 10, 0]
|
||||
// |
|
||||
// *
|
||||
//
|
||||
// And that's where it should be placed!
|
||||
//
|
||||
// Another example where the second column's item extends past the first:
|
||||
// [10, 20, 10, 0] => [20, 20, 10] => 10
|
||||
var available = [];
|
||||
|
||||
// For how many possible positions for this item there are.
|
||||
for (var i = 0; i <= columns - columnSpan; i++) {
|
||||
// Find the bigger value for each place it could fit.
|
||||
available.push(arrayMax(positions.slice(i, i + columnSpan)));
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find index of short column, the first from the left where this item will go.
|
||||
*
|
||||
* @param {Array.<number>} positions The array to search for the smallest number.
|
||||
* @param {number} buffer Optional buffer which is very useful when the height
|
||||
* is a percentage of the width.
|
||||
* @return {number} Index of the short column.
|
||||
*/
|
||||
function getShortColumn(positions, buffer) {
|
||||
var minPosition = arrayMin(positions);
|
||||
for (var i = 0, len = positions.length; i < len; i++) {
|
||||
if (positions[i] >= minPosition - buffer && positions[i] <= minPosition + buffer) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue