You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Vestride_Shuffle/src/layout.js

124 lines
4.4 KiB
JavaScript

'use strict';
import Point from './point';
import arrayMax from './array-max';
import arrayMin from './array-min';
/**
* 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}
*/
export 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.
*/
export 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.
*/
export 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;
}