最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue使用ResizeObserver輕松監(jiān)聽(tīng)元素尺寸變化

 更新時(shí)間:2025年11月28日 08:28:47   作者:Drift_Dream  
在前端開(kāi)發(fā)中,我們經(jīng)常需要知道一個(gè)元素的尺寸是否發(fā)生了變化,過(guò)去,我們只能通過(guò)監(jiān)聽(tīng)window的resize事件,無(wú)法監(jiān)聽(tīng)具體元素的變化,ResizeObserver應(yīng)運(yùn)而生,它可以幫助我們監(jiān)聽(tīng)任意元素的大小變化,所以本文為大家介紹了如何使用ResizeObserver監(jiān)聽(tīng)元素尺寸變化

什么是ResizeObserver?

在前端開(kāi)發(fā)中,我們經(jīng)常需要知道一個(gè)元素的尺寸是否發(fā)生了變化。過(guò)去,我們只能通過(guò)監(jiān)聽(tīng)window的resize事件,但這只能監(jiān)聽(tīng)瀏覽器窗口的變化,無(wú)法監(jiān)聽(tīng)具體元素的變化。ResizeObserver應(yīng)運(yùn)而生,它可以幫助我們監(jiān)聽(tīng)任意元素的大小變化。

簡(jiǎn)單來(lái)說(shuō),ResizeObserver就像是一個(gè)"尺寸監(jiān)視器",當(dāng)被觀察的元素尺寸發(fā)生變化時(shí),它會(huì)立即通知我們。

為什么需要ResizeObserver?

在ResizeObserver出現(xiàn)之前,我們想要監(jiān)聽(tīng)元素尺寸變化通常有以下幾種方法:

  1. window.resize事件:只能監(jiān)聽(tīng)窗口變化,不能監(jiān)聽(tīng)具體元素
  2. 輪詢檢查:通過(guò)定時(shí)器不斷檢查元素尺寸,性能差
  3. MutationObserver:可以監(jiān)聽(tīng)DOM變化,但無(wú)法直接監(jiān)聽(tīng)尺寸變化

這些方法要么功能有限,要么性能不佳。ResizeObserver專門為解決這個(gè)問(wèn)題而生,它提供了高效、精準(zhǔn)的元素尺寸監(jiān)聽(tīng)能力。

基本使用方法

創(chuàng)建ResizeObserver

// 創(chuàng)建ResizeObserver實(shí)例
const resizeObserver = new ResizeObserver((entries) => {
  for (let entry of entries) {
    // entry.target:被觀察的元素
    // entry.contentRect:元素的尺寸信息
    console.log('元素尺寸發(fā)生變化:', entry.contentRect);
  }
});

觀察元素

const element = document.getElementById('myElement');
resizeObserver.observe(element);

停止觀察

// 停止觀察特定元素
resizeObserver.unobserve(element);

// 停止所有觀察并銷毀實(shí)例
resizeObserver.disconnect();

完整示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ResizeObserver</title>
  </head>
  <style>
    .box {
      display: flex;
      gap: 20px;
    }
    .resizeable {
      width: 300px;
      height: 200px;
      resize: both;
      border: 1px solid #ccc;
      overflow: auto;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .resizeable1 {
      background-color: aquamarine;
    }
    .resizeable2 {
      background-color: blueviolet;
    }
    .log {
      width: 300px;
      height: 200px;
      resize: both;
      border: 1px solid #ccc;
      display: flex;
      flex-direction: column;
      justify-content: space-around;
      align-items: center;
    }
  </style>
  <body>
    <div class="box">
      <div class="resizeable resizeable1" id="resizeable1">box1</div>
      <div class="resizeable resizeable2" id="resizeable2">box2</div>
      <div class="log">
        <div class="text1"></div>
        <div class="text2"></div>
      </div>
    </div>
  </body>
  <script>
    const textNode1 = document.querySelector(".text1");
    const textNode2 = document.querySelector(".text2");

    const resizeNode1 = document.querySelector("#resizeable1");
    const resizeNode2 = document.querySelector("#resizeable2");

    const nodeObserver = new ResizeObserver((entries) => {
      for (let entry of entries) {
        console.log(entry);
        const { width, height } = entry.contentRect;
        if (entry.target.id === "resizeable1") {
          textNode1.textContent = `box1當(dāng)前尺寸:${Math.round(
            width
          )} * ${Math.round(height)} 像素`;
        } else {
          textNode2.textContent = `box2當(dāng)前尺寸:${Math.round(
            width
          )} * ${Math.round(height)} 像素`;
        }
      }
    });
    nodeObserver.observe(resizeNode1);
    nodeObserver.observe(resizeNode2);
  </script>
</html>

在Vue 3中的使用實(shí)踐

組合式API用法

<template>
  <div class="resize-observer">
    <div ref="resizableElement" class="resizable-box">
      <p>ResizeObserver 簡(jiǎn)單示例</p>
      <p>當(dāng)前寬度: {{ width }}px</p>
      <p>當(dāng)前高度: {{ height }}px</p>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from "vue";
const resizableElement = ref(null);
const width = ref(0);
const height = ref(0);
let resizeObserver: ResizeObserver | null = null;

onMounted(() => {
  if (resizableElement.value) {
    resizeObserver = new ResizeObserver((entries) => {
      for (let entry of entries) {
        width.value = entry.contentRect.width;
        height.value = entry.contentRect.height;
      }
    });

    resizeObserver.observe(resizableElement.value);
  }
});

onUnmounted(() => {
  if (resizeObserver) {
    resizeObserver.disconnect();
  }
});
</script>

<style lang="scss" scoped>
.resizable-box {
  resize: both;
  overflow: auto;
  border: 1px solid #e0e0e0;
  padding: 20px;
  min-width: 200px;
  min-height: 100px;
  background-color: #f5f5f5;
}
</style>

自定義Hook封裝

為了更好地復(fù)用,我們可以將ResizeObserver封裝成自定義Hook:

import { onUnmounted, ref } from "vue";

export function useResizeObserver() {
  const width = ref(0);
  const height = ref(0);
  let resizeObserver: ResizeObserver | null = null;

  const observe = (el: HTMLElement) => {
    if (resizeObserver) {
      resizeObserver.disconnect();
    }
    resizeObserver = new ResizeObserver((entries) => {
      for (const entry of entries) {
        width.value = entry.contentRect.width;
        height.value = entry.contentRect.height;
      }
    });
    resizeObserver.observe(el);
  };
  const unobserve = () => {
    if (resizeObserver) {
      resizeObserver.disconnect();
    }
  };
  onUnmounted(() => {
    if (resizeObserver) {
      resizeObserver.disconnect();
    }
  });
  return {
    width,
    height,
    observe,
    unobserve,
  };
}

使用自定義Hook

<template>
  <div class="resize-observer">
    <div ref="resizableElement" class="resizable-box">
      <p>使用鉤子函數(shù)實(shí)現(xiàn)的 ResizeObserver</p>
      <p>當(dāng)前寬度: {{ width }}px</p>
      <p>當(dāng)前高度: {{ height }}px</p>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref } from "vue";
import { useResizeObserver } from "@/hook/useResizeObserver.ts";

const { width, height, observe, unobserve } = useResizeObserver();

const resizableElement = ref(null);

onMounted(() => {
  if (resizableElement.value) {
    observe(resizableElement.value);
  }
});

onUnmounted(() => {
  if (resizableElement.value) {
    unobserve(resizableElement.value);
  }
});
</script>

<style lang="scss" scoped>
.resizable-box {
  resize: both;
  overflow: auto;
  border: 1px solid #e0e0e0;
  padding: 20px;
  min-width: 200px;
  min-height: 100px;
  background-color: aquamarine;
}
</style>

Vue 3中ResizeObserver的實(shí)際應(yīng)用:可調(diào)整布局的管理后臺(tái)

<template>
  <div class="demo-container">
    <!-- 可調(diào)整寬度的側(cè)邊欄 -->
    <aside
      ref="sidebarRef"
      class="sidebar"
      :style="{ width: sidebarWidth + 'px' }"
    >
      <div class="sidebar-content">
        <h3>導(dǎo)航菜單</h3>
        <nav>
          <a
            v-for="item in menuItems"
            :key="item.id"
            class="nav-item"
            :class="{ active: activeMenu === item.id }"
            @click="activeMenu = item.id"
          >
            {{ item.name }}
          </a>
        </nav>
      </div>

      <!-- 拖拽把手 -->
      <div class="resize-handle" @mousedown="startResize"></div>
    </aside>
    <main ref="mainRef" class="main-content">
      <div class="content-header">
        <button @click="toggleSidebar" class="toggle-btn">
          {{ isSidebarCollapsed ? "展開(kāi)側(cè)邊欄" : "折疊側(cè)邊欄" }}
        </button>
        <h2>主要內(nèi)容區(qū)域</h2>
      </div>
      <!-- 顯示當(dāng)前尺寸信息 -->
      <div class="size-info">
        <p>側(cè)邊欄寬度: {{ sidebarWidth }}px</p>
        <p>主內(nèi)容區(qū)域?qū)挾? {{ mainContentWidth }}px</p>
      </div>
    </main>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";

//  側(cè)邊欄狀態(tài)
const sidebarWidth = ref(280);
const isSidebarCollapsed = ref(false);
const activeMenu = ref("dashboard");

// 元素引用
const sidebarRef = ref(null);
const mainRef = ref(null);

// 尺寸數(shù)據(jù)
const mainContentWidth = ref(0);

// 菜單數(shù)據(jù)
const menuItems = [
  { id: "dashboard", name: "儀表盤" },
  { id: "users", name: "用戶管理" },
  { id: "orders", name: "訂單管理" },
  { id: "settings", name: "系統(tǒng)設(shè)置" },
];

// 監(jiān)聽(tīng)主內(nèi)容區(qū)域?qū)挾茸兓?
let mainResizeObserver: ResizeObserver | null = null;

onMounted(() => {
  // 監(jiān)聽(tīng)主內(nèi)容區(qū)域?qū)挾?
  mainResizeObserver = new ResizeObserver((entries) => {
    for (let entry of entries) {
      mainContentWidth.value = entry.contentRect.width;
    }
  });

  if (mainRef.value) {
    mainResizeObserver.observe(mainRef.value);
  }
});

// 切換側(cè)邊欄顯示/隱藏
const toggleSidebar = () => {
  isSidebarCollapsed.value = !isSidebarCollapsed.value;
  sidebarWidth.value = isSidebarCollapsed.value ? 0 : 280;
};

// 開(kāi)始調(diào)整側(cè)邊欄寬度
const startResize = (e: MouseEvent) => {
  e.preventDefault();
  const startX = e.clientX;
  const startWidth = sidebarWidth.value;

  const handleMouseMove = (e: MouseEvent) => {
    const newWidth = startWidth + (e.clientX - startX);
    // 限制最小和最大寬度
    if (newWidth >= 200 && newWidth <= 500) {
      sidebarWidth.value = newWidth;
      isSidebarCollapsed.value = false;
    }
  };

  const handleMouseUp = () => {
    document.removeEventListener("mousemove", handleMouseMove);
    document.removeEventListener("mouseup", handleMouseUp);
  };

  document.addEventListener("mousemove", handleMouseMove);
  document.addEventListener("mouseup", handleMouseUp);
};

onUnmounted(() => {
  if (mainResizeObserver) {
    mainResizeObserver.disconnect();
  }
});
</script>
<style scoped>
.demo-container {
  display: flex;
  height: 100vh;
  font-family: Arial, sans-serif;
}

.sidebar {
  position: relative;
  background: #2c3e50;
  color: white;
  transition: width 0.3s ease;
  min-width: 0;
  overflow: hidden;
}

.sidebar-content {
  padding: 20px;
}

.sidebar h3 {
  margin-bottom: 20px;
  border-bottom: 1px solid #34495e;
  padding-bottom: 10px;
}

.nav-item {
  display: block;
  padding: 10px 15px;
  margin: 5px 0;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.3s;
}

.nav-item:hover {
  background: #34495e;
}

.nav-item.active {
  background: #3498db;
}

.resize-handle {
  position: absolute;
  right: 0;
  top: 0;
  width: 4px;
  height: 100%;
  background: #34495e;
  cursor: col-resize;
  transition: background 0.3s;
}

.resize-handle:hover {
  background: #3498db;
}

.main-content {
  flex: 1;
  padding: 20px;
  background: #ecf0f1;
  overflow-y: auto;
}

.content-header {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
  gap: 15px;
}

.toggle-btn {
  padding: 8px 16px;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.toggle-btn:hover {
  background: #2980b9;
}

.size-info {
  background: white;
  padding: 15px;
  border-radius: 4px;
  margin-bottom: 20px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.chart-container,
.table-container {
  background: white;
  padding: 20px;
  border-radius: 4px;
  margin-bottom: 20px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
</style>

以上就是Vue使用ResizeObserver輕松監(jiān)聽(tīng)元素尺寸變化的詳細(xì)內(nèi)容,更多關(guān)于Vue ResizeObserver監(jiān)聽(tīng)元素變化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

南涧| 斗六市| 盐津县| 黄骅市| 宜丰县| 丰台区| 新邵县| 元朗区| 岳阳县| 两当县| 清水河县| 香港| 孙吴县| 丰县| 桃江县| 赤壁市| 芦溪县| 紫阳县| 扎赉特旗| 遂溪县| 怀来县| 汕尾市| 广水市| 芦溪县| 苏尼特左旗| 武鸣县| 大足县| 会昌县| 兴山县| 固镇县| 泰兴市| 南陵县| 岚皋县| 闽侯县| 鄯善县| 汉川市| 莱西市| 西林县| 古蔺县| 司法| 永修县|