The idea of this responsive layout is to present all surfaces on a custom viewport preserving the aspect ratios and keeping all surfaces as big as possible.
The codePen of the working responsive layout example is here.
This layout function work with famo.us framework and famous-flex lib.
Gist of the layout function is here.
The codePen of the working responsive layout example is here.
This layout function work with famo.us framework and famous-flex lib.
Gist of the layout function is here.
/**
* This Source Code is licensed under the MIT license. If a copy of the
* MIT-license was not distributed with this file, You can obtain one at:
* http://opensource.org/licenses/mit-license.html.
*
* @author: Oleksandr Zinchenko (Qvatra)
* @license MIT
*/
/*global console*/
/*eslint no-console: 0*/
/**
* Fits (sets max possible size) a collection of renderables with a given aspect ratios to the context size from left to right, and when the right edge is reached,
* continues at the next row.
*
* |options|type|description|
* |---|---|---|
* |`[cellRatios]`|Array.Number|Array of the dataSource elements aspect ratios|
*
* Example:
*
* ```javascript
* var FixedMasonryLayout = require('FixedMasonryLayout');
*
* var layoutController = new LayoutController({
* layout: FixedMasonryLayout,
* layoutOptions: {
* cellRatios: [1, 3, 1, 2]
* },
* dataSource: [
* new Surface({content: '1', properties:{background:'red'}}),
* new Surface({content: '2', properties:{background:'blue'}}),
* new Surface({content: '3', properties:{background:'green'}}),
* new Surface({content: '4', properties:{background:'yellow'}})
* ]
* });
* ```
* @module
*/
define(function (require, exports, module) {
// Define capabilities of this layout function
var capabilities = {
sequence: true,
scrolling: false
};
// data
var size; // layout container size
var index; // iterator
var cellRatios; // integer ratios of elements
var gridArea; // summ of all elements areas. used for calculation of the gridSize
var grid; // binary grid array. 0 means empty cell - 1 means occupied cell
var gridRatio; // aspect ratio of the grid
var gridSize; // size of the grid regarding to the gridArea parameter (not pixels)
var nodes; // array of all elements (nodes)
var node; // current element
var set = { // size and position of an element
size: [0, 0],
translate: [0, 0, 0.01]
};
// returns true if element of size=size could be fitted in to the grid at coordinates [x, y]
function _canBeFitted(x, y, itemSize) {
for (var j = y; j < y + itemSize[1]; j++) {
for (var i = x; i < x + itemSize[0]; i++) {
if (i > grid.length - 1 || j > grid[0].length - 1 || grid[i][j] == 1) {
return false;
}
}
}
return true;
}
// markes occupied grid area with '1'
function _reservePlace(x, y, itemSize) {
for (var j = y; j < y + itemSize[1]; j++) {
for (var i = x; i < x + itemSize[0]; i++) {
grid[i][j] = 1;
}
}
}
// returns position on the grid if element could be fitted or undefined otherwise
function _tryToFit(itemSize) {
for (var j = 0; j < grid[0].length; j++) {
for (var i = 0; i < grid.length; i++) {
if (_canBeFitted(i, j, itemSize)) {
_reservePlace(i, j, itemSize);
return [i, j];
}
}
}
return undefined;
}
// calculate recursively next possible position on the grid for the given element
function _calculatePosition(size) {
var gridPosition = _tryToFit(size);
if (!gridPosition) { // make grid bigger (add additional row and col)
grid.forEach(function (col) { //adding extra row
col.push(0);
});
var col = [];
grid[0].forEach(function () { //adding extra col
col.push(0);
});
grid.push(col);
return _calculatePosition(size);
}
return gridPosition;
}
// calculate canvas size = min size of a rectangle that can fit all elements
function _calculateCanvasSize() {
var canvasSize = [0, 0];
for (var i = 0; i < nodes.length; i++) {
canvasSize[0] = Math.max(nodes[i].gridPosition[0] + nodes[i].aspectRatio, canvasSize[0]);
canvasSize[1] = Math.max(nodes[i].gridPosition[1] + 1, canvasSize[1]);
}
return canvasSize;
}
// height align calculation
function _alignHeight(scale, viewSize, canvasSize) {
return (viewSize[1] - canvasSize[1] * scale) / (canvasSize[1] + 1);
}
// width align calculation
function _alignWidth(scale, viewSize, canvasSize) {
var gutterInRow = [];
var freeSpaceInRow = [];
var numCellsInRow = [];
var lastCellInRow = [];
for (var i = 0; i < canvasSize[1]; i++) {
numCellsInRow.push(0);
freeSpaceInRow.push(0);
gutterInRow.push(0);
lastCellInRow.push(null);
}
for (var i = 0; i < nodes.length; i++) {
var row = nodes[i].gridPosition[1];
numCellsInRow[row] += 1;
nodes[i]['positionInRow'] = numCellsInRow[row];
lastCellInRow[row] = (lastCellInRow[row] === null || nodes[i].gridPosition[0] > lastCellInRow[row].gridPosition[0]) ? nodes[i] : lastCellInRow[row];
}
freeSpaceInRow = lastCellInRow.map(function (cell) {
return viewSize[0] - (cell.gridPosition[0] + cell.aspectRatio) * scale;
})
for (var i = 0; i < freeSpaceInRow.length; i++) {
gutterInRow[i] = Math.floor(freeSpaceInRow[i] / (numCellsInRow[i] + 1));
}
return gutterInRow;
}
// Layout function
function FixedMasonryLayout(context, options) {
// init
size = context.size;
cellRatios = options.cellRatios;
reflowTransition = options.reflowTransition;
// calculate total area of elements
gridArea = 0;
cellRatios.forEach(function (cellRatio) {
gridArea += cellRatio * 1; // we assume that all nodes have height = 1
})
// prepare grid
grid = [];
gridRatio = size[0] / size[1];
gridSize = [Math.ceil(Math.sqrt(gridRatio * gridArea)), Math.ceil(Math.sqrt(gridArea / gridRatio))];
for (var i = 0; i < gridSize[0]; i++) {
grid.push([]);
for (var j = 0; j < gridSize[1]; j++) {
grid[i].push(0); // init of the grid with '0'
}
}
// 1st loop; calculate positions
node = context.next();
nodes = [];
index = 0;
while (node && (index < cellRatios.length)) {
node['aspectRatio'] = cellRatios[index];
node['gridPosition'] = _calculatePosition([cellRatios[index], 1]);
nodes.push(node);
// Move to next renderable
index++;
node = context.next();
}
var canvasSize = _calculateCanvasSize();
var scale = (size[0] / size[1] > canvasSize[0] / canvasSize[1]) ? size[1] / canvasSize[1] : size[0] / canvasSize[0];
var widthAlign = _alignWidth(scale, size, canvasSize);
var heightAlign = _alignHeight(scale, size, canvasSize);
// 2nd loop; set node options
nodes.forEach(function (node) {
set.size = [node.aspectRatio * scale, 1 * scale];
set.translate[0] = node.gridPosition[0] * scale + widthAlign[node.gridPosition[1]] * node.positionInRow;
set.translate[1] = node.gridPosition[1] * scale + heightAlign * (node.gridPosition[1] + 1);
context.set(node, set);
})
}
FixedMasonryLayout.Capabilities = capabilities;
module.exports = FixedMasonryLayout;
});
No comments:
Post a Comment