Javascript 一些新的 WEB API 及应用

一、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>

二、

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Windyluna

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值