@unri/masonry
TypeScript icon, indicating that this package has built-in type declarations

0.5.2 • Public • Published

@unri/masonry

A simple Masonry layout javascript function (+ responsive & animation)

Preview

Table of Contents

Installation

in the browser

<script type="module">
  import * as Masonry from "https://unpkg.com/@unri/masonry@latest/es/index.js";
  ...
</script>

with a package manager

npm i @unri/masonry
# or
yarn add @unri/masonry

Usage

Requirements

  • Every items in the Masonry layout must have the same width.
  • Every items in the Masonry layout should have predetermined height, otherwise it may overlap (example : <img loading="lazy" ... /> you should specify width & height attributes on <img />).

Vanilla JS

Here is an example with pure javascript. (also example on CodePen).

HTML :

<body>
  <div id="masonry-layout">
    <div class="card" style="width: 260px; height: 204px;">1</div>
    <div class="card" style="width: 260px; height: 424px;">2</div>
    <div class="card" style="width: 260px; height: 152px;">3</div>
    <div class="card" style="width: 260px; height: 396px;">4</div>
    <div class="card" style="width: 260px; height: 294px;">5</div>
    <div class="card" style="width: 260px; height: 312px;">6</div>
    <div class="card" style="width: 260px; height: 106px;">7</div>
    <div class="card" style="width: 260px; height: 270px;">8</div>
    <div class="card" style="width: 260px; height: 331px;">9</div>
    <div class="card" style="width: 260px; height: 124px;">10</div>
    <div class="card" style="width: 260px; height: 271px;">11</div>
    <div class="card" style="width: 260px; height: 446px;">12</div>
    <div class="card" style="width: 260px; height: 407px;">13</div>
    <div class="card" style="width: 260px; height: 144px;">14</div>
    <div class="card" style="width: 260px; height: 388px;">15</div>
    <div class="card" style="width: 260px; height: 249px;">16</div>
    <div class="card" style="width: 260px; height: 120px;">17</div>
    <div class="card" style="width: 260px; height: 345px;">18</div>
    <div class="card" style="width: 260px; height: 321px;">19</div>
    <div class="card" style="width: 260px; height: 285px;">20</div>
    <div class="card" style="width: 260px; height: 125px;">21</div>
  </div>
</body>

Javascript :

import { positionMasonry } from "https://unpkg.com/@unri/masonry@latest/dist/index.mjs";
import debounce from "utils/debounce.js";
// Get the HTML element that contains the Masonry layout items
const masonryLayout = document.getElementById("masonry-layout");

// Use debounce on for better performances
const debouncePositionMasonry = debounce(() => {
  positionMasonry({
    // The masonry layout container
    rootElement: masonryLayout,
    // Horizontal alignment of the masonry layout elements (default to center)
    align: "center",
    // The gap between the masonry layout elements in pixels (default to 0)
    gap: 30,
    // or use gapX to specify the horizontal gap between elements (optionnal)
    gapX: 30,
    // or use gapY to specify the vertical gap between elements (optionnal)
    gapY: 30,
    // or use pass a function to gapY that takes the number of column in argument
    gapY: (numColumns) => (numColumns > 1 ? 30 : 0),
    // The duration of the re/positionning of the masonry layout elements (default to 0)
    animationDuration: 0.15,
  });
}, 200);
// Keep track of the device orientation
let orientation = window.screen.orientation.type;

// Add event listener on resize for responsiveness
window.addEventListener("resize", () => {
  const orientationChange = window.screen.orientation.type !== orientation;
  orientation = window.screen.orientation.type;

  if (!orientationChange) {
    debouncePositionMasonry();
  } else {
    // Position with no delay & no animation (to avoid mobile browser reflow bug)
    positionMasonry({
      rootElement: masonryLayout,
      gap: 30,
      align: "center",
    });
  }
});

// Trigger positioning
positionMasonry({ rootElement: masonryLayout, gap: 30, align: "center" });

ReactJS

Here is an example with ReactJS. (also example on CodePen).

import { positionMasonry } from "@unri/masonry";
import debounce from "utils/debounce";

// Array of random heights
const heights = [ 204, 424, 152, 396, 294, ... ];

function MasonryLayout({ gap, children }) {
  // Reference the HTML element that contains the Masonry layout items
  const ref = React.useRef(null);

  // Wrap the call in a useEffect to apply the repositioning on first render
  React.useEffect(() => {
    // Reposition on each rerender
    positionMasonry({rootElement: ref.current, gap});
  });

  // Setup responsive repositionning
  React.useEffect(() => {
    // Keep track of the device orientation
    let orientation = window.screen.orientation.type;
    // Use debounce for better performances
    const debouncePositionMasonry = debounce(() => {
      // Use animation only on window resizing
      positionMasonry({rootElement: ref.current, gap, animationDuration: 0.15}),
        200
    });
    const handleResize = () => {
      if (!ref.current) return;
      // Check if the resizing is a device orientation change
      const orientationChange = window.screen.orientation.type !== orientation;
      orientation = window.screen.orientation.type;

      if (!orientationChange) {
        debouncePositionMasonry();
      } else {
        // Position with no delay & no animation (to avoid mobile browser reflow bug)
        positionMasonry({
          rootElement: ref.current,
          gap,
        });
      }
    }
    // Add event listener on resize for responsiveness
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  // Set the ref on the container of the Masonry layout
  return <div ref={ref}>{children}</div>;
}
// Just wrap your elements inside the <MasonryLayout>
function App() {
  return (
    <MasonryLayout gap={30}>
      {heights.map((height, i) => (
        <div
          key={height * i}
          className="card"
          style={{ width: "260px", height }}
        >
          {i + 1}
        </div>
      ))}
    </MasonryLayout>
  );
}

Package Sidebar

Install

npm i @unri/masonry

Weekly Downloads

0

Version

0.5.2

License

MIT

Unpacked Size

13.6 kB

Total Files

5

Last publish

Collaborators

  • unri