美文网首页
darkti UI—popover气泡卡片

darkti UI—popover气泡卡片

作者: darkTi | 来源:发表于2020-06-23 19:27 被阅读0次

一、写前需要思考的

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

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

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

  • 默认的插槽是不需要包起来的;
  • 一定要异步给document添加函数(用setTimeout),否则同步的话,直接运行下来又把visible变成false了,内容永远都不会出来;
  • ()=>{}相当于function(){}.bind(this),而且要清楚function x(){}function x(){}.bind(this)是两个不同的函数!!!!!!!
  • 阻止冒泡,主要是阻止冒到document上面触发x函数;但是阻止冒泡是个很危险的选择,一般我们应该让用户自己抉择是否冒泡,不应该在轮子组件中阻止冒泡;

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里;

  • 这里又要注意一点,一旦你把内容元素append到document里,它就脱离了d-popover的作用域,所以css中,不能把content-wrapper写在popover里了,应该挪出来;而且要获取按钮的top、left来给内容元素赋值top、left;
  • 当你移动vue组件中一个元素的位置时,是不会影响这个元素的功能的,只是改变了位置;

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


垂直方向.png

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

  • 所以我们要获取用户点击的哪里,点击按钮或按钮外部去关闭执行的函数不一样;
  • 怎么获取你点击的是哪个元素?把点击事件本身打印出来看,e.target
  • 因为我们把popover上的阻止冒泡去掉了,所以现在点击内容元素的话它还是会消失,所以我们需要给document的绑定事件判断一下条件,当点击的是内容元素时,我们什么都不要做
  • 把关闭入口收拢,做到高内聚,低耦合!!!!
  • 不要使用this.visible = !this.visible,通过this.visible = true / false控制各个条件下的操作;
  • 需要给document的绑定函数一个执行范围,当它发现点击的范围在内容区域时,就不做任何操作
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
  • 为什么可以做到点击第二个按钮时,第一个按钮的内容元素会关掉?因为点击第二个按钮的时候回同时触发document上的事件,就会把第一个按钮的内容元素关闭了!!!!

三、支持四个位置

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

  • 给contentWrapper设置max-width: 20em; word-break: break-all;(英文网站最好不要加word-break: break-all;

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>

相关文章