카테고리 없음

230616 egjs Masonrygrid 사용하기

Neda 2023. 6. 17. 23:48

230616 egjs Masonrygrid 

MasonryLayout을 만드는 여러 라이브러리가 있겠지만, 네이버의 egjs에도 있길래 사용해보았다. 별다른 설정없이 바로 레이아웃이 적용되었다.

 

egjs 라이브러리 설명

다른 사람들의 라이브러리를 사용하고 또 살펴보는 것은 많은 도움이 되는 것 같다.

역시 한국인이 만든 라이브러리여서 그런지 MasonryGrid.ts에는 아래와 같이 한글로 된 기본 설명이 있다

MasonryGrid는 벽돌을 쌓아 올린 모양처럼 동일한 너비를 가진 아이템를 쌓는 레이아웃이다. 모든 이미지의 너비를 동일한 크기로 조정하고, 가장 높이가 낮은 열을 찾아 새로운 이미지를 삽입한다. 따라서 배치된 아이템 사이에 빈 공간이 생기지는 않지만 배치된 레이아웃의 아래쪽은 울퉁불퉁해진다.

 

기본적인 구현방법은 applyGrid 메서드에 있다. 전체 item에 대해서 for문을 통해서 각각의 위치를 찾고 마지막에 display:absolute인 item의 위치를 인라인 스타일로 정해주고 있다.

item.cssInlinePos = inlinePos;
item.cssContentPos = contentPos;
 public applyGrid(items: GridItem[], direction: "start" | "end", outline: number[]): GridOutlines {
    items.forEach((item) => {
      item.isRestoreOrgCSSText = false;
    });
    const columnSize = this.getComputedOutlineSize(items);
    const column = this.getComputedOutlineLength(items);

    const {
      gap,
      align,
      observeChildren,
      columnSizeRatio,
    } = this.options;
    const outlineLength = outline.length;
    const itemsLength = items.length;
    const alignPoses = this._getAlignPoses(column, columnSize);
    const isEndDirection = direction === "end";
    const nearestCalculationName = isEndDirection ? "min" : "max";
    const pointCalculationName = isEndDirection ? "max" : "min";
    let startOutline = [0];

    if (outlineLength === column) {
      startOutline = outline.slice();
    } else {
      const point = outlineLength ? Math[pointCalculationName](...outline) : 0;

      startOutline = range(column).map(() => point);
    }
    const endOutline = startOutline.slice();
    const columnDist = column > 1 ? alignPoses[1] - alignPoses[0] : 0;
    const isStretch = align === "stretch";

    for (let i = 0; i < itemsLength; ++i) {
      const item = items[isEndDirection ? i : itemsLength - 1 - i];
      const columnAttribute = parseInt(item.attributes.column || "1", 10);
      const maxColumnAttribute = parseInt(item.attributes.maxColumn || "1", 10);
      let contentSize = item.contentSize;
      let columnCount = Math.min(
        column,
        columnAttribute || Math.max(1, Math.ceil((item.inlineSize + gap) / columnDist)),
      );
      const maxColumnCount = Math.min(column, Math.max(columnCount, maxColumnAttribute));
      let columnIndex = getColumnIndex(endOutline, columnCount, nearestCalculationName);
      let contentPos = getColumnPoint(endOutline, columnIndex, columnCount, pointCalculationName);

      while (columnCount < maxColumnCount) {
        const nextEndColumnIndex = columnIndex + columnCount;
        const nextColumnIndex = columnIndex - 1;

        if (isEndDirection && (nextEndColumnIndex >= column || endOutline[nextEndColumnIndex] > contentPos)) {
          break;
        }
        if (!isEndDirection && (nextColumnIndex < 0 || endOutline[nextColumnIndex] < contentPos)) {
          break;
        }
        if (!isEndDirection) {
          --columnIndex;
        }
        ++columnCount;
      }

      columnIndex = Math.max(0, columnIndex);
      columnCount = Math.min(column - columnIndex, columnCount);

      // stretch mode or data-grid-column > "1"
      if ((columnAttribute > 0 && columnCount > 1) || isStretch) {
        const nextInlineSize = (columnCount - 1) * columnDist + columnSize;

        if ((!this._isObserverEnabled() || !observeChildren) && item.cssInlineSize !== nextInlineSize) {
          item.shouldReupdate = true;
        }
        item.cssInlineSize = nextInlineSize;
      }
      if (columnSizeRatio > 0) {
        contentSize = item.computedInlineSize / columnSizeRatio;
        item.cssContentSize = contentSize;
      }
      const inlinePos = alignPoses[columnIndex];
      contentPos = isEndDirection ? contentPos : contentPos - gap - contentSize;

      item.cssInlinePos = inlinePos;
      item.cssContentPos = contentPos;
      const nextOutlinePoint = isEndDirection ? contentPos + contentSize + gap : contentPos;

      range(columnCount).forEach((indexOffset) => {
        endOutline[columnIndex + indexOffset] = nextOutlinePoint;
      });
    }

    // if end items, startOutline is low, endOutline is high
    // if start items, startOutline is high, endOutline is low
    return {
      start: isEndDirection ? startOutline : endOutline,
      end: isEndDirection ? endOutline : startOutline,
    };
  }