ホームページ >ウェブフロントエンド >jsチュートリアル >React での応答性の高い会議タイル用の動的グリッド システムの構築

React での応答性の高い会議タイル用の動的グリッド システムの構築

WBOY
WBOYオリジナル
2024-08-28 06:03:06274ブラウズ

Building a Dynamic Grid System for Responsive Meeting Tiles in React

リモートワークと仮想会議の時代では、参加者のビデオ タイルを表示するための応答性の高い動的なグリッド システムを作成することが重要です。 Google Meet のようなプラットフォームに触発されて、私は最近、さまざまな参加者数やさまざまな画面サイズにシームレスに適応する柔軟なグリッド システムを React で開発しました。このブログ投稿では、実装手順を説明し、主要なコンポーネントと、それらがどのように連携して効率的で応答性の高いレイアウトを作成するかを説明します。

目次

  1. はじめに
  2. グリッド レイアウトの定義
  3. 適切なグリッド レイアウトの選択
  4. useGridLayout フック
  5. 使用例
  6. グリッドのスタイル
  7. 結論

導入

動的なグリッド システムの作成には、項目 (または「タイル」) の数と利用可能な画面領域に基づいてレイアウトを調整することが含まれます。ビデオ会議アプリケーションの場合、これにより、参加者の数や使用されているデバイスに関係なく、各参加者のビデオ フィードが最適に表示されるようになります。

私が開発したソリューションは、React フックと CSS Grid を活用して、グリッド レイアウトを動的に管理およびレンダリングします。このシステムのコアコンポーネントについて詳しく見ていきましょう。

グリッドレイアウトの定義

まず、システムが使用できるグリッド レイアウトを定義します。各レイアウトでは、列と行の数、および収容できるタイルの最小数と最大数の制約が指定されます。

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: 複雑さの順に並べられた、事前定義されたレイアウトの配列。各レイアウトは以下を指定します。
    • 列と行: グリッド内の列と行の数。
    • name: レイアウトのわかりやすい名前 (例: '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 を設定します。
    • Event Listener: ウィンドウ サイズが変更されるたびにレイアウトを更新するために、サイズ変更イベント リスナーを追加します。コンポーネントのアンマウント時にリスナーをクリーンアップします。
  • 戻り値: 現在のレイアウト オブジェクトを使用側コンポーネントに提供します。

使用例

この動的グリッド システムが実際にどのように機能するかを示すために、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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。