Skip to content

useRect 📐

はじめに

要素の位置とサイズを正確に取得したいですか?ビューポート内での要素の正確な座標を知りたいですか?useRectはあなたの測定の神器です!📏

このHookは簡単な要素の位置とサイズ取得機能を提供し、Element.getBoundingClientRectに相当します。要素の幾何学的情報を簡単に把握できます!

コードデモ

基本的な使い方 📍

要素の基本的な位置とサイズ情報を取得します:

html
<div ref="root" class="demo-box">
        私はテスト要素です
      </div>
js
import { ref, onMounted } from 'vue';
import { useRect } from '@vant/use';

export default {
  setup() {
    const root = ref();

    onMounted(() => {
      const rect = useRect(root);// 要素の情報をコンソールに出力
      console.log('📐 要素情報:', rect);
      console.log(`📏 幅: ${rect.width}px`);
      console.log(`📏 高さ: ${rect.height}px`);
      console.log(`📍 位置: (${rect.left}, ${rect.top})`);
    });

    return { root };
  },
};

リアルタイム位置監視 🎯

イベントリスナーと組み合わせて、要素位置の変化をリアルタイムで監視します:

html
<div ref="container" class="scroll-container">
  <div ref="target" class="target-element">
            ドラッグまたはスクロールしてみてください!
          </div>
</div>
js
import { ref, onMounted } from 'vue';
import { useRect } from '@vant/use';
import { useEventListener } from '@vant/use';

export default {
  setup() {
    const container = ref();
    const target = ref();

    const updatePosition = () => {
        const rect = useRect(target);
        console.log(`🎯 現在の位置: x=${rect.left}, y=${rect.top}`);
        console.log(`📦 現在のサイズ: ${rect.width} × ${rect.height}`);
      };

    onMounted(() => {
      // 监听滚动事件
      useEventListener('scroll', updatePosition, { target: container });
      
      // 监听窗口大小变化
      useEventListener('resize', updatePosition);
      
      // 初始位置
      updatePosition();
    });

    return {
      container,
      target,
    };
  },
};

衝突検出 💥

2つの要素が衝突しているかどうかを検出します:

html
<div ref="element1" class="box box1">要素 1</div>
      <div ref="element2" class="box box2">要素 2</div>
      <div class="collision-status">{{ collisionStatus }}</div>
js
import { ref, onMounted, computed } from 'vue';
import { useRect } from '@vant/use';

export default {
  setup() {
    const element1 = ref();
    const element2 = ref();
    const rect1 = ref({});
    const rect2 = ref({});

    const checkCollision = () => {
      rect1.value = useRect(element1);
      rect2.value = useRect(element2);
    };

    const collisionStatus = computed(() => {
        const r1 = rect1.value;
        const r2 = rect2.value;
        
        if (!r1.width || !r2.width) return '🔍 検出中...';
        
        const isColliding = !(
          r1.right < r2.left ||
          r1.left > r2.right ||
          r1.bottom < r2.top ||
          r1.top > r2.bottom
        );
        
        return isColliding ? '💥 衝突発生!' : '✅ 安全距離';
      });

    onMounted(() => {
      // 定期检测碰撞
      setInterval(checkCollision, 100);
    });

    return {
      element1,
      element2,
      collisionStatus,
    };
  },
};

ビューポート可視性検出 👁️

要素がビューポート内で可視かどうかを検出します:

html
<div ref="target" class="lazy-element">
        {{ visibilityStatus }}
      </div>
js
import { ref, onMounted, computed } from 'vue';
import { useRect } from '@vant/use';
import { useEventListener } from '@vant/use';

export default {
  setup() {
    const target = ref();
    const elementRect = ref({});

    const updateRect = () => {
      elementRect.value = useRect(target);
    };

    const visibilityStatus = computed(() => {
        const rect = elementRect.value;
        if (!rect.width) return '🔍 検出中...';

        const windowHeight = window.innerHeight;
        const windowWidth = window.innerWidth;

        const isVisible = (
          rect.top < windowHeight &&
          rect.bottom > 0 &&
          rect.left < windowWidth &&
          rect.right > 0
        );

        const visibleArea = Math.max(0, 
          Math.min(rect.bottom, windowHeight) - Math.max(rect.top, 0)
        ) * Math.max(0,
          Math.min(rect.right, windowWidth) - Math.max(rect.left, 0)
        );

        const totalArea = rect.width * rect.height;
        const visiblePercentage = totalArea > 0 ? (visibleArea / totalArea * 100).toFixed(1) : 0;

        return isVisible 
          ? `👁️ 可視 (${visiblePercentage}%)`
          : '🙈 不可視';
      });

    onMounted(() => {
      updateRect();
      useEventListener('scroll', updateRect);
      useEventListener('resize', updateRect);
    });

    return {
      target,
      visibilityStatus,
    };
  },
};

拖拽边界限制 🚧

限制元素拖拽在指定区域内:

html
<div ref="container" class="drag-container">
  <div ref="draggable" class="draggable-element">
    拖拽我!
  </div>
</div>
js
import { ref, onMounted } from 'vue';
import { useRect } from '@vant/use';

export default {
  setup() {
    const container = ref();
    const draggable = ref();
    let isDragging = false;
    let startX = 0;
    let startY = 0;

    const handleMouseDown = (e) => {
      isDragging = true;
      startX = e.clientX;
      startY = e.clientY;
    };

    const handleMouseMove = (e) => {
      if (!isDragging) return;

      const containerRect = useRect(container);
      const draggableRect = useRect(draggable);
      
      const deltaX = e.clientX - startX;
      const deltaY = e.clientY - startY;
      
      // 计算新位置
      let newLeft = draggableRect.left + deltaX - containerRect.left;
      let newTop = draggableRect.top + deltaY - containerRect.top;
      
      // 边界限制
      newLeft = Math.max(0, Math.min(newLeft, containerRect.width - draggableRect.width));
      newTop = Math.max(0, Math.min(newTop, containerRect.height - draggableRect.height));
      
      // 应用新位置
      draggable.value.style.left = `${newLeft}px`;
      draggable.value.style.top = `${newTop}px`;
      
      startX = e.clientX;
      startY = e.clientY;
      
      console.log(`🎯 拖拽位置: (${newLeft.toFixed(1)}, ${newTop.toFixed(1)})`);
    };

    const handleMouseUp = () => {
      isDragging = false;
    };

    onMounted(() => {
      draggable.value.addEventListener('mousedown', handleMouseDown);
      document.addEventListener('mousemove', handleMouseMove);
      document.addEventListener('mouseup', handleMouseUp);
    });

    return {
      container,
      draggable,
    };
  },
};

响应式布局计算 📱

根据元素尺寸动态调整布局:

html
<div ref="adaptive" class="adaptive-container">
  <div class="content">{{ layoutInfo }}</div>
</div>
js
import { ref, onMounted, computed } from 'vue';
import { useRect } from '@vant/use';
import { useEventListener } from '@vant/use';

export default {
  setup() {
    const adaptive = ref();
    const containerRect = ref({});

    const updateLayout = () => {
      containerRect.value = useRect(adaptive);
    };

    const layoutInfo = computed(() => {
      const rect = containerRect.value;
      if (!rect.width) return '📱 计算中...';

      const aspectRatio = (rect.width / rect.height).toFixed(2);
      let deviceType = '';
      
      if (rect.width < 768) {
        deviceType = '📱 移动端';
      } else if (rect.width < 1024) {
        deviceType = '📟 平板端';
      } else {
        deviceType = '💻 桌面端';
      }

      return `
        ${deviceType}
        📐 尺寸: ${rect.width} × ${rect.height}
        📏 比例: ${aspectRatio}
        📍 位置: (${rect.left}, ${rect.top})
      `;
    });

    onMounted(() => {
      updateLayout();
      useEventListener('resize', updateLayout);
    });

    return {
      adaptive,
      layoutInfo,
    };
  },
};

API リファレンス 📚

型定義

ts
function useRect(
  element: Element | Window | Ref<Element | Window | undefined>,
): DOMRect;

interface DOMRect {
  width: number;
  height: number;
  top: number;
  left: number;
  right: number;
  bottom: number;
  x: number;
  y: number;
}

パラメータ説明

パラメータ説明デフォルト
elementターゲット要素または要素参照Element | Window | Ref<Element | Window | undefined>-

戻り値 DOMRect

プロパティ説明
width要素の幅number
height要素の高さnumber
topビューポート左上隅からの上辺までの距離number
leftビューポート左上隅からの左辺までの距離number
rightビューポート左上隅からの右辺までの距離number
bottomビューポート左上隅からの下辺までの距離number
xleft と同じnumber
ytop と同じnumber

実際の使用シナリオ 🎯

🎯 インタラクティブ機能

  • ドラッグ境界制限
  • 衝突検出システム
  • フロートヒントの位置決め
  • コンテキストメニューの位置決め

📱 レスポンシブデザイン

  • アダプティブレイアウト計算
  • ブレークポイント検出
  • 要素サイズの監視
  • デバイス適応

🎨 アニメーション効果

  • 要素追従アニメーション
  • パララックススクロール効果
  • 入場/退場アニメーション
  • 位置遷移アニメーション

📊 データ可視化

  • チャート要素の位置決め
  • ラベル位置の計算
  • 拡大縮小中心点の計算
  • 座標系変換

ベストプラクティス 💡

✅ 推奨される方法

js
// 1. 適切なタイミングで位置情報を取得
onMounted(() => {
  const rect = useRect(element);
  // 要素がレンダリングされていることを確認
});

// 2. イベントリスナーと組み合わせてリアルタイム更新
useEventListener('resize', () => {
  const rect = useRect(element);
  updateLayout(rect);
});

// 3. 計算結果をキャッシュ
const cachedRect = ref({});
const updateRect = debounce(() => {
  cachedRect.value = useRect(element);
}, 100);

❌ 避けるべき方法

js
// 1. 頻繁な呼び出しを避ける
setInterval(() => {
  const rect = useRect(element); // パフォーマンスの問題
}, 16);

// 2. 要素がレンダリングされる前に呼び出すのを避ける
const rect = useRect(element); // 誤った情報を取得する可能性がある

// 3. ブラウザの互換性を無視しない
const rect = useRect(element);
// 一部の古いブラウザではすべてのプロパティがサポートされていない場合がある


## デバッグテクニック 🔧

### 🔍 可視化デバッグ

```js
// 要素上に位置情報を表示
const showRectInfo = (element) => {
  const rect = useRect(element);
  
  const info = document.createElement('div');
  info.style.cssText = `
    position: fixed;
    top: ${rect.top}px;
    left: ${rect.left}px;
    background: rgba(255, 0, 0, 0.8);
    color: white;
    padding: 4px;
    font-size: 12px;
    z-index: 9999;
  `;
  info.textContent = `${rect.width}×${rect.height} (${rect.left},${rect.top})`;
  
  document.body.appendChild(info);
  
  setTimeout(() => {
    document.body.removeChild(info);
  }, 2000);
};

🐛 パフォーマンスモニタリング

js
// useRect 呼び出しのパフォーマンスを監視
const performanceRect = (element) => {
  const start = performance.now();
  const rect = useRect(element);
  const end = performance.now();
  
  console.log(`📊 useRect 実行時間: ${(end - start).toFixed(2)}ms`);
  return rect;
};

ブラウザ互換性 🌐

ブラウザバージョンサポート
Chrome✅ 1+
Firefox✅ 3+
Safari✅ 4+
Edge✅ 12+
IE✅ 9+

関連ドキュメント 📖

🎯 コア機能

🎨 アニメーション関連

🔧 開発ツール

📱 モバイル最適化

🎪 高度な使い方

Vant に基づく企業向けモバイルソリューション