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 |
| x | left と同じ | number |
| y | top と同じ | 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+ |
関連ドキュメント 📖
🎯 コア機能
- useEventListener - イベントリスナー管理 - 位置変化イベントの監視
- useWindowSize - ウィンドウサイズ - ビューポートサイズ情報の取得
- useScrollParent - スクロールコンテナ - スクロール親要素の取得
🎨 アニメーション関連
- useRaf - アニメーションフレーム管理 - スムーズな位置アニメーション
- usePageVisibility - ページ可視性 - アニメーション再生の制御
🔧 開発ツール
- Vant Use 紹介 - コンポジション API の詳細を了解
- useToggle - 状態切り替え - 表示状態の切り替え
📱 モバイル最適化
- useClickAway - 外部クリック - 外部クリックの処理
- テーマカスタマイズ - Theme - カスタムスタイルテーマ
🎪 高度な使い方
- useRelation - コンポーネント関連 - コンポーネント間の位置関係
- useCustomFieldValue - カスタムフィールド - 位置データのバインディング
- Watermark - ウォーターマークコンポーネント - 位置関連コンポーネント