| MMMasonry.js | Fix image sorting to reduce squash/distortion | |
| MMMasonry.js.min | Initial commit | |
| README.md | Improve examples and add horizontal rules | |
MMMasonry
A simple custom HTML component for laying out images of different sizes in a grid.
Similar in concept to the well known Masonry package by David DeSandro.
Specific goals
- Fit the images into an even rectangle (ie no ragged bottom edge)
- Handle the situation where images are not the direct descendents of the container (eg
<a><img /></a>) - Simple/no-frills approach
Usage
Include the script
Include it at the top of your page (probably in the <head>) eg:
<!-- index.html -->
<head>
<!-- ... -->
<script src="/js/MMMasonry.js"></script>
<!-- ... -->
</head>
Now you can use the <mm-masonry> component
The component is your container/wrapper. You fill it with your images (optionally wrapped in other elements) eg:
<!-- index.html -->
<mm-masonry base-width="200" gap-x="5" gap-y="5">
<a href="https://mm-dev.rocks/">
<img src="/images/image1.jpg" width="200" height="268" />
</a>
<a href="https://mm-dev.rocks/">
<img src="/images/image2.jpg" width="200" height="268" />
</a>
<!-- ...the rest of your images -->
</mm-masonry>
Attributes and customisation
The HTML element attributes are all optional:
base-widthThis is the starting width of the images (although they will be increased to fill any excess space in the width of the<mm-masonry>container)gap-xThe gap between columns (in pixels)gap-yThe gap between rows (in pixels)
If you do not set a base width, the width of the first found child image will be used.
If you want an outer gap or border just use CSS to add a margin, padding or border property eg:
/* main.css */
mm-masonry {
border: solid 5px white;
}
If you want to style your images just use CSS as normal, for example to add a zoom on hover animation:
/* main.css */
mm-masonry {
a,
a:link,
a:hover,
a:visited {
padding: 0;
background: none;
img {
transition-duration: 150ms;
transition-property: transform;
}
}
a:hover {
z-index: 1;
img {
transform: scale(1.05);
transition-duration: 300ms;
transition-property: transform;
}
}
}
Potential issues and things to be aware of
For my intended use-case these things are fine but they won't suit everybody:
- Images can be stretched distorted (although usually not by very much)
- The order of images is changed
- The container
<mm-masonry>will internally set its own CSS toposition: relativeanddisplay: block - The child
<img>elements will be set todisplay: block - Any wrappers to the
<img>elements (eg<a>), or the images themselves if no wrappers are present, will be set toposition: absolute
How does it work?
For those who may be interested, these are the 4 basic steps taken:
1. Calculate number of columns and their width
- Starting with the base width, see how many columns will fit inside the container
- Increase the column widths until the container is completely full
Example pseudocode:
// Number of columns
numberOfColumns = floor(containerWidth / baseWidth);
// Space left over
remainingSpace = containerWidth - (baseWidth * numberOfColumns);
// Final column width
spaceToAddToEachColumn = floor(remainingSpace / numberOfColumns);
finalColumnWidth = baseWidth + spaceToAddToEachColumn;
2. Rearrange images to help make the column heights more even
This is a naive first attempt to even out the total height of each column (for this example say there are 3 columns):
- Sort the images into order (tallest -> shortest)
- Fill the first row with the 3 tallest images
- Fill the next row with the 3 shortest images
- Continue filling rows as above, alternating tallest/shortest remaining images on each row until all of the images are used
The end result is for each column to have a roughly even mix of short and tall images.
3. Calculate a scaling value for each column
- Calculate the average (mean) column height
- Based on this average, calculate how much each column will need to be scaled for its height to match
Example pseudocode:
columnMeanHeight = column1Height + column2Height + column3Height;
column1Scale = columnMeanHeight / column1Height;
// ...etc
4. Scale and reposition the images
For each image, based on the scale of its column:
- Adjust the height/width if the image
- Adjust the position to take allow for the size adjustment