1
0
Fork
You've already forked mm-masonry
0
No description
  • JavaScript 100%
Find a file
2025年07月21日 07:42:15 +01:00
MMMasonry.js Fix image sorting to reduce squash/distortion 2025年07月21日 07:42:15 +01:00
MMMasonry.js.min Initial commit 2025年01月02日 12:08:49 +00:00
README.md Improve examples and add horizontal rules 2025年01月02日 13:51:43 +00:00

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-width This 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-x The gap between columns (in pixels)
  • gap-y The 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 to position: relative and display: block
  • The child <img> elements will be set to display: block
  • Any wrappers to the <img> elements (eg <a>), or the images themselves if no wrappers are present, will be set to position: 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