InfiniteGrid

repository·master·Indexed 25 days ago

https://github.com/naver/egjs-infinitegrid

A high-performance module for arranging elements in various infinite grid layouts, including Masonry, Justified, Frame, and Packing. It manages DOM performance by controlling the number of rendered elements and supports varying item sizes. The library provides a core engine with dedicated wrappers for React (@egjs/react-infinitegrid), Angular (@egjs/ngx-infinitegrid), and Svelte (@egjs/svelte-infinitegrid), as well as a VanillaRenderer for manual DOM management.

Tokens
49K
Snippets
104
Records
220
Agent score
76%

What's inside egjs-infinitegrid

  1. What is PackingInfiniteGrid?

    master

    The PackingInfiniteGrid is a specialized grid layout designed to emphasize important items by making them larger without significantly increasing the overall weight of the items.

    Unlike standard grids, rows and columns are not strictly separated; instead, items are dynamically placed within the available horizontal and vertical space. This results in a non-orderly, fluid layout.

    Its behavior is controlled by two key weights:

    • If sizeWeight is higher than ratioWeight, the absolute size of items is preserved as much as possible.
    • If ratioWeight is higher than sizeWeight, the relative ratio of items is preserved as much as possible.
  2. Differences between @egjs/vue3-infinitegrid and @egjs/infinitegrid

    master

    When using the Vue 3 wrapper, be aware of the following changes compared to the core @egjs/infinitegrid library:

    1. Event Naming: All camelCased event names have been converted to kebab-case. For example, requestAppend must be used as request-append.
    2. DOM Manipulation: You cannot use methods that manipulate the DOM directly, such as append(), prepend(), insert(), or remove().

    If you are using Vue 2, use @egjs/vue-infinitegrid instead.

  3. Differences between @egjs/vue-infinitegrid and @egjs/infinitegrid

    master

    When using the Vue wrapper instead of the core library, note the following changes:

    1. Event Naming: All camelCased event names are converted to kebab-case. For example, requestAppend becomes request-append.
    2. DOM Manipulation: You cannot use methods that manipulate the DOM directly, such as append(), prepend(), insert(), or remove().
  4. Understand the JustifiedInfiniteGrid concept

    master

    JustifiedInfiniteGrid is a grid implementation where items are filled row-by-row based on a given width, similar to the 'justified' printing term. It automatically calculates how many items fit in a line to maintain a justified layout.

    Ratio Maintenance:

    • If data-grid-inline-offset or data-grid-content-offset are set on an item element, the item's ratio is maintained while accounting for the offset.
    • If data-grid-maintained-target is set on an element, the grid will render items while maintaining that element's aspect ratio.
  5. Choose a Grid Type

    master

    InfiniteGrid supports several layout algorithms depending on your visual requirements:

    • MasonryInfiniteGrid: Stacks items with the same width (like bricks). It finds the lowest height column and inserts the next item there.
    • JustifiedInfiniteGrid: Fills items into rows based on a given size. It can maintain aspect ratios or stretch items to fill the container.
    • JustifiedInfiniteGrid (Stretch): A variation of the justified grid where items can break their original proportions to fill the container's inline size. Use sizeRange to control how much stretching occurs.
    • FrameInfiniteGrid: A grid where items are filled based on a specific frame/pattern configuration.
    • PackingInfiniteGrid: Dynamically places items within horizontal and vertical space to show important items larger. It uses sizeWeight and ratioWeight to balance preserving item size versus preserving aspect ratio.
  6. Manage Grid status with getStatus and setStatus

    master

    You can capture the current layout state of the grid and restore it later. This is useful for preserving the scroll position or item arrangement during re-renders or state changes.

    1. Call getStatus({ minimize: boolean }) to get a GridStatus object. Setting minimize to true will minimize the stored status.
    2. Pass that GridStatus object to setStatus(status: GridStatus) to restore the grid to that exact state.
  7. Insert Data via requestAppend and requestPrepend

    master

    InfiniteGrid handles infinite scrolling by raising events when the user reaches the boundaries of the content:

    • requestAppend: Raised when the scroll reaches the end of the content.
    • requestPrepend: Raised when the scroll reaches the start of the content.

    To add data, use the append or prepend methods on the grid instance. If you provide a groupKey as the second argument to these methods, you do not need to set a separate key for the items in that batch.

    ig.on("requestAppend", e => {
      const nextGroupKey = (+e.groupKey || 0) + 1;
    
      ig.append(getItems(nextGroupKey, 10), nextGroupKey);
    });
  8. Configure the container option in v4

    master

    In v4, the isOverflowScroll option has been renamed to container. If you set container: true, a container element is created inside the wrapper. The class name for this container is now infinitegrid-container (previously _eg-infinitegrid-container_).

    <body>
      <div class="wrapper">
        <div class="infinitegrid-container">
          <div>Item 1</div>
          <div>Item 2</div>
          <div>Item 3</div>
        </div>
      </div>
    </body>
    const ig = new InfiniteGrid(".container", {
      container: true,
    });
  9. Optimize performance with isEqualSize and size groups

    master

    If you know the dimensions of your items, you can use these options to improve performance:

    • isEqualSize: Set this option to treat all items as having the same size. This reduces resize calculations to a single item. If an item is an exception, add the data-grid-not-equal-size="true" attribute to it.
    • data-grid-size-group: If you have multiple distinct size groups (more than just 'equal' or 'not equal'), use this attribute to group items of the same size together.
    • isConstantSize: If items never change size, use this option to prevent resizing from recalculating item dimensions. To force a recalculation, use .updateItems(items, { useOrgResize: true }) or .renderItems({ useOrgResize: true }).
    <!-- Using size groups -->
    <div class="item item1" data-grid-size-group="1"></div>
    <div class="item item1" data-grid-size-group="1"></div>
    <div class="item item2" data-grid-size-group="2"></div>
    <div class="item item2" data-grid-size-group="2"></div>
    
    <!-- Exception to isEqualSize -->
    <div class="item item1"></div>
    <div class="item item2" data-grid-not-equal-size="true"></div>
  10. Implement a custom Component with InfiniteGrid

    master

    To manage the lifecycle of items and elements in InfiniteGrid, you can wrap a Renderer inside a Component class. This component is responsible for syncing items with DOM elements and handling updates.

    Key responsibilities of the Component class:

    • render(items): Calls this.renderer.render(items) to sync items and elements.
    • update(): Triggers a renderer update.
      • In Vanilla mode: If this.renderer.update() returns true, you should manually call this.render() to sync items.
      • In Framework mode: If this.renderer.update() returns false, the update event is emitted.
    • setContainer(container): Sets the target container for the renderer (Vanilla only).
    import { VanillaRenderer, Renderer } from "@egjs/infinitegrid";
    
    // Use VanillaRenderer or Renderer
    
    // Component
    class Component {
      constructor(renderer: Renderer) {
        this.renderer = renderer;
      }
      // Sync items and elements
      render(items) {
        this.renderer.render(items);
      }
    
      // Force update if you want to show only part of it internally
      update() {
        // If true is returned, the update event is not called. (for vanilla)
        // If false is returned, the update event is called. (for framework)
        if (this.renderer.update()) {
          // vanilla
          this.render();
        }
      }
    
      // set renderer container for vanilla
      setContainer(container) {
        this.renderer.setContainer(container);
      }
    }