首页 > 其他分享 >vue3 手写dropdown

vue3 手写dropdown

时间:2023-02-07 09:45:17浏览次数:40  
标签:const dropdown color border show value vue3 手写 ref

<template>
  <div class="drp_component" :class="classname">
    <p @click="openDrp" class="ws-n" ref="drpDef" title="">
      <slot></slot>
    </p>
  </div>
  <transition :name="openType">
    <div
      :style="{ top: top + 'px', left: left + 'px' }"
      class="drp_item_wrap"
      ref="drpItemWrap"
      v-if="show"
      @click="show = false"
      title=""
    >
      <slot name="dropdown"></slot>
      <span
        class="arrow"
        :class="{
          arror_top: arrowPosition === 'top',
          arror_bottom: arrowPosition === 'bottom',
        }"
        :style="{ left: arrowLeft + 'px' }"
      ></span>
    </div>
  </transition>
</template>

<script>
export default {
  name: "HelloWorld",
};
</script>
<script setup>
import { nextTick, ref, watch } from "vue";
import $ from "jquery";

const openType = ref("");
const classname = ref(Math.random().toString(36).substr(2));
const show = ref(false);
const top = ref(9999999);
const left = ref(9999999);
const arrowLeft = ref(0);
const arrowPosition = ref("");
const openDrp = () => {
  show.value = !show.value;
  if (!show.value) return;
  nextTick(() => {
    const body = document.querySelector("body");
    if (body.append) {
      body.append(drpItemWrap.value);
    } else {
      body.appendChild(drpItemWrap.value);
    }
    let dcWidth = document.body.clientWidth;
    let dcHeight = document.body.clientHeight;
    let pLeft =
      $(`.${classname.value}`).offset().left - document.documentElement.scrollLeft;
    let pTop = $(`.${classname.value}`).offset().top - document.documentElement.scrollTop;
    let pWidth = $(`.${classname.value}`).outerWidth();
    let pHeight = $(`.${classname.value}`).outerHeight();
    let itemWidth = $(".drp_item_wrap").outerWidth();
    let itemHeight = $(".drp_item_wrap").outerHeight();
    if (pTop + pHeight + itemHeight - dcHeight > 0) {
      top.value = pTop - itemHeight - 10;
      arrowPosition.value = "bottom";
      openType.value = "el-zoom-in-bottom";
    } else {
      top.value = pTop + pHeight + 10;
      arrowPosition.value = "top";
      openType.value = "el-zoom-in-top";
    }
    if (pLeft + pWidth / 2 - itemWidth / 2 < 0) {
      left.value = 10;
      arrowLeft.value = pLeft - 10 + pWidth / 2 - 7;
    } else if (pLeft + pWidth / 2 + itemWidth / 2 > dcWidth) {
      left.value = dcWidth - itemWidth - 10;
      arrowLeft.value = pLeft - left.value + pWidth / 2 - 7;
    } else {
      left.value = pLeft + pWidth / 2 - itemWidth / 2;
      arrowLeft.value = itemWidth / 2 - 7;
    }
    show.value = !show.value;
    setTimeout(() => {
      show.value = !show.value;
    });
  });
};
const colse = () => {
  if (show.value) show.value = false;
};
const drpDef = ref(null);
const drpItemWrap = ref(null);
const cm = (e) => {
  if (
    drpItemWrap.value &&
    !drpItemWrap.value.contains(e.target) &&
    drpDef.value &&
    !drpDef.value.contains(e.target)
  ) {
    colse();
  }
};
watch(show, (v) => {
  if (v) {
    window.addEventListener("scroll", colse);
    window.addEventListener("wheel", colse);
    window.addEventListener("mousedown", cm);
  } else {
    window.removeEventListener("scroll", colse);
    window.removeEventListener("wheel", colse);
    window.removeEventListener("mousedown", cm);
  }
});
</script>

<style lang="less">
.drp_component {
  line-height: 1;
}
.drp_item_wrap {
  line-height: 1;
  background-color: #fff;
  padding: 5px 0 5px;
  border: 1px solid var(--el-border-color-light);
  box-shadow: var(--el-box-shadow-light);
  position: fixed;
  z-index: 9999999;
  border-radius: 4px;
  > div {
    padding: 10px 16px;
    cursor: pointer;
    user-select: none;
    font-size: 14px;
    text-align: left;
    white-space: nowrap;
  }
  > div:hover {
    background-color: var(--el-color-primary-light-9);
    color: var(--el-color-primary);
  }
  > button {
    border: none;
    outline: none;
    padding: 6px 16px;
    cursor: pointer;
    user-select: none;
    font-size: 14px;
    text-align: left;
    white-space: nowrap;
    background-color: #fff;
    display: block;
  }
  > button:hover {
    background-color: var(--el-color-primary-light-9);
    color: var(--el-color-primary);
  }
  > button[disabled] {
    background-color: #fff;
    cursor: not-allowed;
    color: #8F9BB3;
  }
  .arrow {
    position: absolute;
    width: 10px;
    height: 10px;
    border: 1px solid var(--el-border-color-light);
    z-index: -1;
    transform: rotate(45deg);
    background-color: #fff;
  }
  .arror_top {
    top: -5px;
    border-bottom-color: transparent;
    border-right-color: transparent;
  }
  .arror_bottom {
    bottom: -5px;
    border-top-color: transparent;
    border-left-color: transparent;
  }
}
</style>

  

标签:const,dropdown,color,border,show,value,vue3,手写,ref
From: https://www.cnblogs.com/turkey-Rabbit/p/17097353.html

相关文章

  • 面试官:vue2和vue3的区别有哪些?
    一、Vue3与Vue2区别详述1.生命周期对于生命周期来说,整体上变化不大,只是大部分生命周期钩子名称上+“on”,功能上是类似的。不过有一点需要注意,Vue3在组合式API(Comp......
  • vue3 + ts 封装树形控件
    vue3+ts封装树形控件父组件调用 <TreeFilter label="name" title="部门列表(单选)" :requestApi="getUserDepartment" :defaultValue="treeFilterValue.depar......
  • vue3中使用pinia
    包管理器安装yarnaddpinia#或者使用npmnpminstallpinia在目录下创建store文件夹,并创建index.js文件import{createPinia}from'pinia'constpinia=cre......
  • vue3自动引入api
    1、问题:vue3使用setup的api,每次都要引入就很麻烦,有没有自动引入的方法,这样就不用那么麻烦2、方案:通过使用unplugin-auto-import/vite插件来自动引入vue的api3、实操:在vi......
  • vue3引入SvgIcon
    这里使用vite-plugin-svg-icons插件yarnaddvite-plugin-svg-icons-D#ornpmivite-plugin-svg-icons-D#orpnpminstallvite-plugin-svg-icons-D在vite.con......
  • vue3配置@文件系统路径
    1、问题:在项目中引用通常是相对路径,在复用代码模块的时候,没注意就很容易路径出错2、方案:通过vite设置resolve.alias来配置文件系统路径,在文件中就可以使用配置的路径,移动......
  • 【Swiper】Swiper 滚动插件在 vue3 的使用,并手动设置切换
    效果图需求展示:右上角可左右滑动并控制遮罩层显示隐藏;每行显示4个,每次滑动4个安装npminstallSwiperyarnaddSwiperpnpmaddSwiper代码template<!--控制分......
  • 手写spring框架
    当我们自己想实现一个Spring框架的时候,想想我们需要实现哪些功能。先从最基础的部分开始,我们首先需要一个容器,我们可以通过这个容器获取bean。那容器里的bean是从哪来的呢?......
  • 卷积生成对抗网络---生成手写数字
    深度卷积生成对抗网络(DCGAN)----生成MNIST手写图片1、基本原理生成对抗网络(GAN)由2个重要的部分构成:生成器(Generator):通过机器生成数据(大部分情况下是图像),目的是“骗过”......
  • 保姆级手写promise以及promise常用得方法
    最近迷上了promise,觉得还挺有意思,看了大佬的一篇知乎文章  面试官:“你能手写一个Promise吗” ,写的很详细,超级赞,看完了来着做个笔记,牢固一下理解和印象。1.常见Prom......