>웹 프론트엔드 >JS 튜토리얼 >React에서 반응형 회의 타일을 위한 동적 그리드 시스템 구축

React에서 반응형 회의 타일을 위한 동적 그리드 시스템 구축

WBOY
WBOY원래의
2024-08-28 06:03:06276검색

Building a Dynamic Grid System for Responsive Meeting Tiles in React

원격 근무 및 가상 회의 시대에는 참가자 비디오 타일을 표시하기 위한 반응성이 뛰어나고 동적인 그리드 시스템을 만드는 것이 중요합니다. Google Meet과 같은 플랫폼에서 영감을 받아 최근 React에서 다양한 참가자 수와 다양한 화면 크기에 원활하게 적응하는 유연한 그리드 시스템을 개발했습니다. 이 블로그 게시물에서는 구현 과정을 안내하고 주요 구성 요소와 이러한 구성 요소가 함께 작동하여 효율적이고 반응성이 뛰어난 레이아웃을 만드는 방법을 설명하겠습니다.

목차

  1. 소개
  2. 그리드 레이아웃 정의
  3. 적절한 그리드 레이아웃 선택
  4. useGridLayout 후크
  5. 사용예
  6. 그리드 스타일링
  7. 결론

소개

동적 그리드 시스템을 만들려면 항목(또는 '타일') 수와 사용 가능한 화면 공간을 기반으로 레이아웃을 조정해야 합니다. 화상 회의 애플리케이션의 경우 이는 참가자 수나 사용 중인 장치에 관계없이 각 참가자의 비디오 피드가 최적으로 표시되도록 보장합니다.

제가 개발한 솔루션은 React 후크와 CSS 그리드를 활용하여 그리드 레이아웃을 동적으로 관리하고 렌더링합니다. 이 시스템의 핵심 구성요소를 자세히 살펴보겠습니다.

그리드 레이아웃 정의

먼저 시스템에서 사용할 수 있는 그리드 레이아웃을 정의합니다. 각 레이아웃은 열과 행의 수는 물론 수용할 수 있는 최소 및 최대 타일 수에 대한 제약 조건을 지정합니다.

import { useState, useEffect, RefObject } from 'react';

export type GridLayoutDefinition = {
  name: string;
  columns: number;
  rows: number;
  minTiles: number;
  maxTiles: number;
  minWidth: number;
  minHeight: number;
};

export const GRID_LAYOUTS: GridLayoutDefinition[] = [
  { columns: 1, rows: 1, name: '1x1', minTiles: 1, maxTiles: 1, minWidth: 0, minHeight: 0 },
  { columns: 1, rows: 2, name: '1x2', minTiles: 2, maxTiles: 2, minWidth: 0, minHeight: 0 },
  { columns: 2, rows: 1, name: '2x1', minTiles: 2, maxTiles: 2, minWidth: 900, minHeight: 0 },
  { columns: 2, rows: 2, name: '2x2', minTiles: 3, maxTiles: 4, minWidth: 560, minHeight: 0 },
  { columns: 3, rows: 3, name: '3x3', minTiles: 5, maxTiles: 9, minWidth: 700, minHeight: 0 },
  { columns: 4, rows: 4, name: '4x4', minTiles: 10, maxTiles: 16, minWidth: 960, minHeight: 0 },
  { columns: 5, rows: 5, name: '5x5', minTiles: 17, maxTiles: 25, minWidth: 1100, minHeight: 0 },
];

설명

  • GridLayoutDefinition: 각 그리드 레이아웃의 속성을 정의하는 TypeScript 유형입니다.
  • GRID_LAYOUTS: 복잡도에 따라 정렬된 사전 정의된 레이아웃 배열입니다. 각 레이아웃은 다음을 지정합니다.
    • 열 및 행: 그리드의 열 및 행 수입니다.
    • 이름: 레이아웃을 설명하는 이름(예: '2x2').
    • minTiles 및 maxTiles: 레이아웃이 수용할 수 있는 타일 수의 범위입니다.
    • minWidth 및 minHeight: 레이아웃에 필요한 최소 컨테이너 크기입니다.

적절한 그리드 레이아웃 선택

타일 수와 컨테이너 크기에 따라 올바른 그리드 레이아웃을 선택하기 위한 핵심 논리는 selectGridLayout 함수에 캡슐화되어 있습니다.

function selectGridLayout(
  layouts: GridLayoutDefinition[],
  tileCount: number,
  width: number,
  height: number,
): GridLayoutDefinition {
  let currentLayoutIndex = 0;
  let layout = layouts.find((layout_, index, allLayouts) => {
    currentLayoutIndex = index;
    const isBiggerLayoutAvailable = allLayouts.findIndex((l, i) => 
      i > index && l.maxTiles === layout_.maxTiles
    ) !== -1;
    return layout_.maxTiles >= tileCount && !isBiggerLayoutAvailable;
  });

  if (!layout) {
    layout = layouts[layouts.length - 1];
    console.warn(`No layout found for: tileCount: ${tileCount}, width/height: ${width}/${height}. Fallback to biggest available layout (${layout?.name}).`);
  }

  if (layout && (width < layout.minWidth || height < layout.minHeight)) {
    if (currentLayoutIndex > 0) {
      const smallerLayout = layouts[currentLayoutIndex - 1];
      layout = selectGridLayout(
        layouts.slice(0, currentLayoutIndex),
        smallerLayout.maxTiles,
        width,
        height,
      );
    }
  }

  return layout || layouts[0];
}

작동 방식

  1. 초기 선택: 이 함수는 레이아웃 배열을 반복하여 maxTiles가 TileCount보다 크거나 같은 첫 번째 레이아웃을 찾고 동일한 maxTiles를 사용할 수 있는 더 큰 레이아웃이 없는지 확인합니다.

  2. 폴백 메커니즘: 적합한 레이아웃을 찾을 수 없는 경우 기본적으로 사용 가능한 가장 큰 레이아웃을 사용하고 경고를 기록합니다.

  3. 반응형 조정: 선택한 레이아웃의 minWidth 또는 minHeight 제약 조건이 컨테이너 크기에 맞지 않는 경우 함수는 제약 조건에 맞는 더 작은 레이아웃을 반복적으로 선택합니다.

  4. 최종 반환: 선택한 레이아웃이 반환되어 그리드가 타일 수에 적합하고 컨테이너 크기에 맞는지 확인합니다.

useGridLayout 후크

그리드 선택 로직을 캡슐화하고 여러 구성요소에서 재사용할 수 있도록 하기 위해 useGridLayout 사용자 정의 후크를 만들었습니다.

export function useGridLayout(
  gridRef: RefObject<HTMLElement>,
  tileCount: number
): { layout: GridLayoutDefinition } {
  const [layout, setLayout] = useState<GridLayoutDefinition>(GRID_LAYOUTS[0]);

  useEffect(() => {
    const updateLayout = () => {
      if (gridRef.current) {
        const { width, height } = gridRef.current.getBoundingClientRect();
        const newLayout = selectGridLayout(GRID_LAYOUTS, tileCount, width, height);
        setLayout(newLayout);

        gridRef.current.style.setProperty('--col-count', newLayout.columns.toString());
        gridRef.current.style.setProperty('--row-count', newLayout.rows.toString());
      }
    };

    updateLayout();

    window.addEventListener('resize', updateLayout);
    return () => window.removeEventListener('resize', updateLayout);
  }, [gridRef, tileCount]);

  return { layout };
}

후크 분석

  • 매개변수:

    • GridRef: 그리드 컨테이너 요소에 대한 참조입니다.
    • TileCount: 현재 표시할 타일 수입니다.
  • 상태 관리: useState를 사용하여 현재 레이아웃을 추적하고 GRID_LAYOUTS의 첫 번째 레이아웃으로 초기화합니다.

  • 효과 후크:

    • updateLayout 함수: 컨테이너의 너비와 높이를 검색하고, selectGridLayout을 사용하여 적절한 레이아웃을 선택한 후 상태를 업데이트합니다. 또한 스타일 지정을 위해 컨테이너에 CSS 변수 --col-count 및 --row-count를 설정합니다.
    • 이벤트 리스너: 창 크기가 변경될 때마다 레이아웃을 업데이트하기 위해 크기 조정 이벤트 리스너를 추가합니다. 구성요소 마운트 해제 시 리스너를 정리합니다.
  • 반환 값: 현재 레이아웃 객체를 소비 구성 요소에 제공합니다.

사용 예

이 동적 그리드 시스템이 실제로 어떻게 작동하는지 보여주기 위해 다음은 useGridLayout 후크를 사용하는 React 구성 요소의 예입니다.

'use client'

import React, { useState, useRef, useEffect } from 'react'
import { Button } from "@/components/ui/button"
import { useGridLayout, GridLayoutDefinition } from './useGridLayout'

export default function Component() {
  const [tiles, setTiles] = useState<number[]>([1, 2, 3, 4]);
  const [containerWidth, setContainerWidth] = useState(typeof window !== 'undefined' ? window.innerWidth : 1000);
  const gridRef = useRef<HTMLDivElement>(null);

  const { layout } = useGridLayout(gridRef, tiles.length);

  useEffect(() => {
    const handleResize = () => {
      setContainerWidth(window.innerWidth);
    };

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  const addTile = () => setTiles(prev => [...prev, prev.length + 1]);
  const removeTile = () => setTiles(prev => prev.slice(0, -1));

  return (
    <div className="flex flex-col items-center gap-4 p-4 w-full">
      <div className="flex flex-wrap justify-center gap-2 mb-4">
        <Button onClick={addTile}>Add Tile</Button>
        <Button onClick={removeTile}>Remove Tile</Button>
      </div>
      <div className="w-full border border-gray-300 p-4">
        <div 
          ref={gridRef} 
          className="grid gap-4"
          style={{
            gridTemplateColumns: `repeat(var(--col-count), 1fr)`,
            gridTemplateRows: `repeat(var(--row-count), 1fr)`,
          }}
        >
          {tiles.slice(0, layout.maxTiles).map((tile) => (
            <div key={tile} className="bg-primary text-primary-foreground p-4 rounded flex items-center justify-center">
              Tile {tile}
            </div>
          ))}
        </div>
      </div>
      <div className="text-center mt-4">
        <p>Current Layout: {layout.name} ({layout.columns}x{layout.rows})</p>
        <p>Container Width: {containerWidth}px</p>
        <p>Visible Tiles: {Math.min(tiles.length, layout.maxTiles)} / Total Tiles: {tiles.length}</p>
      </div>
    </div>
  )
}

구성 요소 분석

  1. 국가 관리:

    • tiles: An array representing the current tiles. Initially contains four tiles.
    • containerWidth: Tracks the container's width, updating on window resize.
  2. Refs:

    • gridRef: A reference to the grid container, passed to the useGridLayout hook.
  3. Using the Hook:

    • Destructures the layout object from the useGridLayout hook, which determines the current grid layout based on the number of tiles and container size.
  4. Event Handling:

    • Add Tile: Adds a new tile to the grid.
    • Remove Tile: Removes the last tile from the grid.
    • Resize Listener: Updates containerWidth on window resize.
  5. Rendering:

    • Controls: Buttons to add or remove tiles.
    • Grid Container:
      • Uses CSS Grid with dynamic gridTemplateColumns and gridTemplateRows based on CSS variables set by the hook.
      • Renders tiles up to the layout.maxTiles limit.
    • Info Section: Displays the current layout, container width, and the number of visible versus total tiles.

What Happens in Action

  • Adding Tiles: As you add more tiles, the useGridLayout hook recalculates the appropriate grid layout to accommodate the new number of tiles while respecting the container's size.
  • Removing Tiles: Removing tiles triggers a layout recalculation to potentially use a smaller grid layout, optimizing space.
  • Resizing: Changing the window size dynamically adjusts the grid layout to ensure that the tiles remain appropriately sized and positioned.

Styling the Grid

The grid's responsiveness is primarily handled via CSS Grid properties and dynamically set CSS variables. Here's a brief overview of how the styling works:

/* Example Tailwind CSS classes used in the component */
/* The actual styles are managed via Tailwind, but the key dynamic properties are set inline */

.grid {
  display: grid;
  gap: 1rem; /* Adjust as needed */
}

.grid > div {
  /* Example styles for tiles */
  background-color: var(--color-primary, #3490dc);
  color: var(--color-primary-foreground, #ffffff);
  padding: 1rem;
  border-radius: 0.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

Dynamic CSS Variables

In the useGridLayout hook, the following CSS variables are set based on the selected layout:

  • --col-count: Number of columns in the grid.
  • --row-count: Number of rows in the grid.

These variables are used to define the gridTemplateColumns and gridTemplateRows properties inline:

style={{
  gridTemplateColumns: `repeat(var(--col-count), 1fr)`,
  gridTemplateRows: `repeat(var(--row-count), 1fr)`,
}}

This approach ensures that the grid layout adapts seamlessly without the need for extensive CSS media queries.

Conclusion

Building a dynamic grid system for applications like video conferencing requires careful consideration of both the number of elements and the available display space. By defining a set of responsive grid layouts and implementing a custom React hook to manage layout selection, we can create a flexible and efficient system that adapts in real-time to user interactions and screen size changes.

This approach not only enhances the user experience by providing an optimal viewing arrangement but also simplifies the development process by encapsulating the layout logic within reusable components. Whether you're building a video conferencing tool, a dashboard, or any application that requires dynamic content arrangement, this grid system can be a valuable addition to your toolkit.

Feel free to customize and extend this system to suit your specific needs. Happy coding!

위 내용은 React에서 반응형 회의 타일을 위한 동적 그리드 시스템 구축의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.