1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 |
208x
208x
87x
7x
1x
6x
42x
42x
42x
6x
6x
42x
18x
18x
18x
12x
42x
18x
2x
2x
14x
14x
2x
2x
2x
6x
6x
2x
14x
42x
42x
14x
14x
14x
14x
42x
42x
14x
14x
2x
| export function round(value, decimals) {
if (!decimals) decimals = 0;
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
}
// return two decimal places rounded number
export function ratio({ width, height }) {
return round(width / height, 2);
}
// takes the Gallery's photos array, width of the container,
// margin between photos Gallery prop, and columns Gallery prop.
// calculates, sizes based on columns and returns the photos array
// with new height/width props in each object
export function computeSizes({ photos, columns, width, margin }) {
if (!width) {
return [];
}
// divide photos over rows, max cells based on `columns`
// effectively resulting in [[0, 1, 2], [3, 4, 5], [6, 7]]
const rows = photos.reduce((acc, cell, idx) => {
const row = Math.floor(idx / columns);
acc[row] = acc[row] ? [...acc[row], cell] : [cell]; // eslint-disable-line no-param-reassign
return acc;
}, []);
// calculate total ratio of each row, and adjust each cell height and width
// accordingly.
let ratios = [];
const rowsWithSizes = rows.map((row, rowIndex) => {
const totalRatio = row.reduce((result, photo) => result + ratio(photo), 0);
const rowWidth = width - row.length * (margin * 2);
// save total ratio of each row
if (rowIndex !== rows.length - 1) ratios.push(totalRatio);
// assign height, if last row use average rows total ratio to keep images from being too large
const height =
rowIndex !== rows.length - 1 || row.length === columns
? rowWidth / totalRatio
: rowWidth / (ratios.length ? ratios.reduce((acc, item) => acc + item, 0) / (rows.length - 1) : totalRatio);
return row.map(photo => ({
...photo,
height: round(height, 1),
width: round(height * ratio(photo), 1),
}));
});
return rowsWithSizes.reduce((acc, row) => [...acc, ...row], []);
}
export function computeSizesColumns({ photos, columns, width, margin }) {
// calculate each colWidth based on total width and column amount
let colWidth = (width - margin * 2 * columns) / columns;
// loop through each photo to assign adjusted height and width based on colWidth
const photosWithSizes = photos.map(photo => {
const newHeight = photo.height / photo.width * colWidth;
return {
...photo,
width: round(colWidth, 1),
height: round(newHeight, 1),
};
});
// store all possible left positions
// and current top positions for each column
const colLeftPositions = [];
const colCurrTopPositions = [];
for (var i = 0; i < columns; i++) {
colLeftPositions[i] = round(i * (colWidth + margin * 2), 1);
colCurrTopPositions[i] = 0;
}
// loop through each photo, then loop thru each "column"
// find column with the smallest height and assign to photo's 'top'
// update that column's height with this photo's height
const photosPositioned = photosWithSizes.map(photo => {
const smallestCol = colCurrTopPositions.reduce((acc, item, i) => {
acc = item < colCurrTopPositions[acc] ? i : acc;
return acc;
}, 0);
photo.top = colCurrTopPositions[smallestCol];
photo.left = colLeftPositions[smallestCol];
colCurrTopPositions[smallestCol] = colCurrTopPositions[smallestCol] + photo.height + margin * 2;
// store the tallest col to use for gallery height because of abs positioned elements
const tallestCol = colCurrTopPositions.reduce((acc, item, i) => {
acc = item > colCurrTopPositions[acc] ? i : acc;
return acc;
}, 0);
photo.containerHeight = colCurrTopPositions[tallestCol];
return photo;
});
return photosPositioned;
}
|