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,
};
},
};碰撞检测 💥
检测两个元素是否发生碰撞:
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 - 水印组件 - 位置相关组件