一、ResizeObserver
官方地址:https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserver
资料:
1、探索 ResizeObserver 的神奇力量:https://juejin.cn/post/7248832185808175141
2、一个较新的WEB API——ResizeObserver 的使用:https://juejin.cn/post/6862321554686214158
有一个布局,计算 paginationWidth 的宽度
效果:
说明:
<template>
<div class="project-con">
<div class="left-con">
</div>
<div class="separated-line"></div>
<div ref="wrapper" class="right-con">
<div class="right-box"></div>
<div class="pagination-con" :style="{ width: paginationWidth }"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted, onUnmounted } from 'vue'
const paginationWidth = ref('')
const wrapper = ref<HTMLElement | null>(null)
let resizeObserver: ResizeObserver | null = null
onMounted(() => {
// 创建 ResizeObserver 实例
resizeObserver = new ResizeObserver((entries) => {
let rect = entries[0].target.getBoundingClientRect()
paginationWidth.value = rect.width + 'px'
})
// 开始观察元素
if (wrapper.value) {
resizeObserver.observe(wrapper.value)
}
})
onUnmounted(() => {
// 组件卸载时销毁 ResizeObserver
if (resizeObserver) {
resizeObserver.disconnect()
resizeObserver = null // 清理引用,帮助垃圾回收
}
})
</script>
<style lang="less" scoped>
.pagination-con {
position: fixed;
// width: 100%;
bottom: 0;
height: 70px;
background: #fff;
border-top: 1px solid #e6e6e6;
}
</style>
伸缩效果实现:
伸缩组件:SideNav
<template>
<div class="aside-nav-con" :class="`${isExpand ? 'extend-width' : 'shrink-width'}`">
<div
v-for="(item, index) in props.sideNavList"
:key="index"
@click="changeNavItem(item, index)"
@mouseenter="item.hover = true"
@mouseleave="item.hover = false"
>
<div v-show="isExpand" class="aside-item" :class="{ 'active-item': index === currentIndex }">
<div class="icon-con">
<svg-icon v-show="index === currentIndex" class="icon-svg" :name="item.activeImg"></svg-icon>
<svg-icon v-show="index != currentIndex" class="icon-svg" :name="item.hover ? item.hoverImg : item.deActiveImg"></svg-icon>
<span>{{ item.name }}</span>
</div>
</div>
<t-tooltip class="placement" :content="item.name" placement="right" show-arrow>
<div v-show="!isExpand" class="shrink-aside-item" :class="{ active: index === currentIndex }">
<div class="icon-flex">
<svg-icon v-show="index === currentIndex" class="icon-svg" :name="item.activeImg"></svg-icon>
<svg-icon v-show="index != currentIndex" class="icon-svg" :name="item.hover ? item.hoverImg : item.deActiveImg"></svg-icon>
</div>
</div>
</t-tooltip>
</div>
<!-- 底部 -->
<div class="aside-nav-con-bottom">
<span class="collapse-button" @click="getExpand">
<i v-if="isExpand" class="iconfont icon-menu-unfold1"></i>
<i v-else class="iconfont icon-menu-fold1"></i>
</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps({
currentIndex: {
type: Number,
default: 0,
},
sideNavList: {
type: Array<any>,
default: () => [],
},
})
const isExpand = ref(true)
const emit = defineEmits(['changeNavItem'])
const changeNavItem = (item, index) => {
emit('changeNavItem', item, index)
}
const getExpand = () => {
isExpand.value = !isExpand.value
}
</script>
<style lang="less" scoped>
.flex-column {
display: flex;
flex-direction: column;
align-items: center;
}
.extend-width {
width: 160px;
transition: all 300ms;
}
.shrink-width {
width: 60px;
transition: all 300ms;
}
.expand-info {
width: 22px;
height: 44px;
cursor: pointer;
color: #fff;
transition: all 300ms;
line-height: 44px;
display: flex;
justify-content: center;
}
.aside-nav-con {
position: relative;
// width: 240px;
// 伸
.aside-item {
display: flex;
align-items: center;
height: 48px;
font-size: 16px;
font-weight: 400;
color: rgba(255, 255, 255, 0.5);
padding-left: 10px;
cursor: pointer;
border-radius: 6px;
margin: 15px 10px;
}
.aside-item:hover {
color: #ffffff;
}
.shrink-aside-item {
display: flex;
align-items: center;
height: 48px;
font-size: 14px;
color: #a2a7b3;
margin: 15px 10px;
cursor: pointer;
&.active {
background: #283960;
border-radius: 8px;
color: #ffffff;
}
}
.icon-con {
display: flex;
align-items: center;
white-space: nowrap;
span {
margin-left: 8px;
}
}
.icon-svg {
width: 18px;
height: 18px;
}
.icon-flex {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.active-item {
//background: #162340;
background: #283960;
border-radius: 8px;
color: #ffffff;
}
.aside-nav-con-bottom {
display: flex;
align-items: center;
width: 100%;
position: absolute;
bottom: 0;
height: 40px;
box-sizing: border-box;
border-top: 1px solid rgba(255, 255, 255, 0.2);
padding-left: 10px;
.collapse-button {
padding: 10px;
cursor: pointer;
i {
color: rgba(255, 255, 255, 0.5);
}
}
}
}
</style>
使用组件 SideNav:
<template>
<div class="container-layer">
<head-nav></head-nav>
<div class="container-body">
<side-nav class="left-con" :side-nav-list="sideNavList" :current-index="currentIndex" @change-nav-item="changeNavItem"> </side-nav>
<div class="main">
<div class="main-box">
<router-view />
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import HeadNav from './components/HeadNav.vue'
import SideNav from './components/SideNav.vue'
import { useRouter } from 'vue-router'
interface NavList {
name: string
id?: string
menu_type?: string
url?: string
icon?: string
activeImg?: string
deActiveImg?: string
hoverImg?: string
}
const sideNavList = ref<NavList[]>([
{
name: '第一行',
id: '',
menu_type: '',
url: '/cardBank',
icon: 'icon-kapianku',
activeImg: 'card-active',
deActiveImg: 'card-deactive',
hoverImg: 'card-hover',
},
{
name: '第二行',
id: '',
menu_type: '',
url: '/mapManage',
icon: 'icon-dituguanli',
activeImg: 'layer-active',
deActiveImg: 'layer-deactive',
hoverImg: 'layer-hover',
},
])
const currentIndex = ref(0)
const router = useRouter()
const changeNavItem = (item, index) => {
currentIndex.value = index
router.push({
path: item.url,
})
}
</script>
<style lang="less" scoped>
.container-layer {
height: 100%;
.container-body {
height: calc(100vh - 60px);
margin: 0;
display: flex;
.left-con {
background: #162340;
flex-shrink: 0;
}
.main {
flex: 1;
background: #162340;
.main-box {
height: 100%;
background: #ffffff;
border-radius: 20px 0px 0px 0px;
overflow-y: auto;
}
}
}
}
</style>