darkti UI—popover气泡卡片

2020-06-23  本文已影响0人  darkTi

一、写前需要思考的

1、 它有两个需求,一是点它出现一个div,二是div里可以出现一些内容;
2、 思路:首先想象一下用户该如何用我们的组件(也就是在template里面他该怎么写)
3、 popover最难的一点,你的元素div要如何出现在卡片上方;
4、 它难就难在要考虑很多浏览器方面的样式行为,也就是css方面;

二、先实现最简单的功能(点击按钮外部关闭内容元素)

1、 思路:当你点击按钮,使内容出现,与此同时,你需要给document绑定一个点击事件,当你第二次点击按钮或者popover外面的区域时,就要执行此事件,并同时把此事件从document上解绑定;你不解绑的话,事件越绑越多;

2、在popover组件上加阻止冒泡会有bug,像下面的代码,<d-popover>外上的点击事件是无法触发到的;

<div @click="yyy">
      <d-popover>
           <template v-slot:content>
               <div>这是一段内容</div>
               <div>nctwayv内容</div>
           </template>  
           <button>点我</button>
      </d-popover>
 </div>

3、不能把内容元素跟按钮写在一起,一旦按钮div上有个overflow:hidden,那么这个元素div就会被遮盖住;所以我们给它一个ref,通过ref获取到元素,再把元素append到document里;

4、不是单单直接赋值top、left就可以了,有一种情况,当document的长度和宽度均超过视图时,就不能单单只是赋值top、left了,所以垂直方向上需要再加上window.scrollY(水平方向同理);


垂直方向.png

5、到此还有bug,就是当你点击按钮关闭内容元素时,它不会去执行document上的那个x函数去关闭,而是直接执行的this.visible = !this.visible这句话去关闭的元素,然后document上的x函数一直没有执行也没有被删除;

documentEvent(e){
   if(this.$refs.contentWrapper && this.$refs.contentWrapper.contains(e.target) ){return}
   this.close()
},
close(){
   //只要关闭内容元素,就要把document上的事件移除
   this.visible = false
   document.removeEventListener('click', this.documentEvent)
},
tips.png

三、支持四个位置

1、先优化内容元素的样式;

2、但是仔细看,三角箭头哪里是没有阴影的,所以就不能用box-shadow了,用filter: drop-shadow(0 0 3px rgba(51,51,51,0.4)); background-color: white;;

image.png
3、表驱动编程!!!
把多个if ....else if....写成excel表一一对应的形式;
表驱动编程.png

四、动态绑定一个事件(click/hover)

1、添加props: trigger,hover时对应mouseenter、mouseleave事件;
2、你没有办法直接在div上给它v-bind一个事件变量,因为v-bind只接受固定的事件名,所以你只能在mounted钩子函数中通addEventListener来触发不同事件;
3、你在mounted钩子函数中添加事件,vue是不知道的,所以你还需要手动在beforeDestory钩子里面移除事件;(!!注意:不能在destoryed钩子里,因为destoryed时DOM已完全消除完毕,是获取不到元素的)
4、到此还有一个小bug,hover时,放到那个三角箭头那里时,会有问题;

bug.png
image.png

五、其他知识点

1、 v-if和v-show区别: v-if会改变它是否存在于DOM树中,而v-show只改变它的display样式(v-show是一直存在于DOM树中的);
2、有时让插槽内容能够访问子组件中才有的数据是很有用的,具体的可见文档:作用域插槽https://cn.vuejs.org/v2/guide/components-slots.html

//d-user组件内
<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>

export default {
    data(){
     return {
            user: 'doudou'
          }
     }
}
//使用组件
<d-user>
  <template  v-slot:default="slotProps">   //当然slotProps这个名字你可以自己随便取
      {{slotProps.user}}     
  </template>
</d-user>
上一篇 下一篇

猜你喜欢

热点阅读